From c3fe61818ee912a300f7712439565832b891d1de Mon Sep 17 00:00:00 2001 From: egutierrez Date: Fri, 8 May 2026 15:55:35 +0200 Subject: [PATCH] chore: auto-commit (3 archivos) - apps/shaders_lab/app.md - dev/issues/README.md - dev/issues/0063-kanban-stickers.md Co-Authored-By: Claude Opus 4.7 (1M context) --- apps/shaders_lab/app.md | 376 +++++------------------------ dev/issues/0063-kanban-stickers.md | 139 +++++++++++ dev/issues/README.md | 1 + 3 files changed, 205 insertions(+), 311 deletions(-) create mode 100644 dev/issues/0063-kanban-stickers.md diff --git a/apps/shaders_lab/app.md b/apps/shaders_lab/app.md index 6d412dbb..3e5ecc26 100644 --- a/apps/shaders_lab/app.md +++ b/apps/shaders_lab/app.md @@ -2,9 +2,10 @@ name: shaders_lab lang: cpp domain: gfx -description: "Live GLSL fragment shader playground. Editor de codigo + node-editor visual (DAG con multi-source) + Functions palette drag-and-drop, dos canvas paralelos (Code y DAG), thumbnails per-nodo, todo nativo C++17 + ImGui + OpenGL 3.3 + imgui-node-editor." -tags: [gui, shaders, opengl, glsl, imgui, node-editor, dag, vj] +description: "Live GLSL shader playground con DAG pipeline. Editor de codigo con compilacion en caliente, panel DAG con paleta de generadores/filtros/output, dos canvas (Code y DAG), parseo de uniforms anotados (// @slider, @color, @xy) que se convierten en controles, persistencia de generators en shaders_lab.db, y guardado/carga de layouts ImGui." +tags: [imgui, opengl, glsl, shaders, dag, live-coding, playground, sqlite] uses_functions: + # gfx - gl_loader_cpp_gfx - gl_shader_cpp_gfx - gl_framebuffer_cpp_gfx @@ -15,336 +16,89 @@ uses_functions: - dag_catalog_cpp_gfx - dag_compile_cpp_gfx - dag_uniforms_cpp_gfx - - dag_palette_cpp_gfx + - dag_panel_cpp_gfx - dag_node_editor_cpp_gfx + - dag_palette_cpp_gfx - dag_node_previews_cpp_gfx - shaderlab_db_cpp_gfx - code_to_generator_cpp_gfx - - fps_overlay_cpp_core - - panel_menu_cpp_core - - layouts_menu_cpp_core - - app_menubar_cpp_core - - layout_storage_sqlite_cpp_core -uses_types: [] -framework: "imgui + opengl3 + imgui-node-editor" -entry_point: "cpp/build/linux/apps/shaders_lab/shaders_lab" + # core (modal Save-as-generator) + - modal_dialog_cpp_core + - text_input_cpp_core + - button_cpp_core +uses_types: + - dag_types_cpp_gfx +framework: "imgui" +entry_point: "main.cpp" dir_path: "cpp/apps/shaders_lab" +repo_url: "" --- -## Descripcion +## Arquitectura -App de live-coding y composicion de fragment shaders GLSL con dos modos coexistentes: editor de codigo libre y editor de DAG visual con catalogo de nodos arrastrables. Pensada para jugar con shaders de tipo VJ y para extraer funciones GLSL al registry global. +App ImGui de live-coding GLSL con dos modos en paralelo: -## Estado actual +1. **Code panel** — editor de fragment shader libre. Las anotaciones en + uniforms (`// @slider`, `// @color`, `// @xy`, `// @toggle`) se parsean y + convierten en controles del panel **Controls** que escriben en un + `UniformStore` aplicado al programa cada frame. +2. **DAG panel** — pipeline node-based con catalogo de generadores + (plasma, voronoi, etc.) y filtros (blur, threshold, etc.) que se + compilan a un fragment shader unificado y se renderizan en **Canvas DAG**. -### Fase 1 — Core renderer + editor de codigo `[done]` -- Ventana ImGui + OpenGL 3.3 via `fn::run_app` del framework. -- Editor de texto del fragment shader con recompile auto (debounce 250 ms). -- Render a FBO + `ImGui::Image` para mostrar preview en panel propio. -- Errores de compilacion con linea, footer rojo en el panel Code. -- 3 presets seed: Plasma, Circle, Checker. -- Cross-compile a Windows con loader propio (`gl_loader`) — sin dependencias externas mas alla de GLFW/ImGui ya vendorizados. +Al guardar un Code shader como "generator" se traduce a un `DagNodeDef` y se +persiste en `shaders_lab.db` (tabla via `shaderlab_db`), apareciendo en la +paleta del DAG junto a los builtins. -### Fase 2 — Annotated uniforms + auto-controls `[done]` -- Parser de comentarios `// @slider`, `// @color`, `// @toggle`, `// @xy` sobre las declaraciones de uniforms. -- Panel `Controls` con widgets ImGui auto-generados (sliders, color pickers, checkboxes). -- Sincronizacion de valores entre recompilaciones por nombre del uniform. -- Tests inline para `uniform_parser` (6/6 asserts). +## Capas -### Fase 3 — DAG mode `[done]` -- Catalogo de 11 nodos: 4 Gen (`solid`, `gradient`, `plasma`, `circle`), 3 Op (`invert`, `gamma`, `hueShift`), 3 Blend (`mix`, `multiply`, `screen`), 1 Output. -- Compilador `compile_dag_to_glsl(pipeline)` que emite un fragment shader unico con `vec4 node_(...)` por nodo y main encadenando outputs. -- Multi-source: hasta 4 inputs por nodo via `source_ids[4]`. Compilador resuelve cada slot. -- Nodo `Output` (sink, rojo, no borrable): su `source_ids[0]` decide que va a `fragColor`. -- Tests inline para `dag_compile` (6/6) y `dag_catalog` (8/8). +| Archivo | Responsabilidad | +|---|---| +| `main.cpp` | UI shell, paneles, modal save-as, layouts, AppConfig | +| `compiler.cpp` | `compile_code()`, `compile_dag()`, `mark_code_dirty()` con debounce 250ms | -### Fase 4a — Layout multi-ventana + dos canvas `[done]` -- Cada panel es ventana ImGui dockable independiente: `Code`, `DAG Pipeline`, `Canvas Code`, `Canvas DAG`, `Controls`, `Generated GLSL`, `Functions`. -- Dos `ShaderCanvas` simultaneos: el del Code y el del DAG renderizan en paralelo, cada uno con su FBO y programa propio. -- Sin focus-based recompile: cada fuente recompila solo cuando su contenido cambia. +`main.cpp` mantiene estado global de sesion (g_source, g_pipeline, g_descs, +g_store, g_layouts...) — ImGui retained-mode obliga a que persista entre +frames. Toda la logica pura de compilacion vive en `compiler.cpp` y en las +funciones `dag_compile`, `code_to_generator`, `uniform_parser` del registry. -### Fase 4b — Visual node editor (imgui-node-editor) `[done]` -- Vendorizada `imgui-node-editor` de thedmd en `cpp/vendor/imgui-node-editor/` (parche puntual en `imgui_extra_math.inl` para evitar choque con ImGui 1.92.7). -- Layout 3 columnas por nodo: pines input a la izquierda, controles en el centro, pin output a la derecha. -- Pines como circulos de radio 9 pegados al borde del nodo (mitad fuera, mitad dentro), color uniforme neutro (data type uniforme = `vec4`). -- `ed::PinRect` cubre el circulo entero — la mitad sobresaliente sigue siendo grabbable. -- Cables 2.5px del color del pin. -- Node drag, pan, zoom — todo nativo del editor. -- Topology change disparado solo cuando se anaden/quitan/reconectan nodos. Mover sliders no recompila. +## Persistencia -### Fase 4c — Functions palette drag-drop `[done]` -- Ventana `Functions` con catalogo agrupado en `Generators / Operators / Blends`. -- Cada item es drag source con payload `DAG_NODE_TYPE`. -- Drop sobre el canvas del DAG anade el nodo en la posicion del mouse. -- Sin botones `+ Add Node` / `Clear` — todo flujo via drag-drop. -- Output node nunca aparece en la paleta (sink unico fijo). +- **`shaders_lab.db`** (junto al .exe) — tabla de generators de usuario via + `shaderlab_db_*`, ademas de `imgui_layouts` (creada por `layout_storage`). +- `imgui.ini` y `app_settings.ini` — gestionados por `fn::run_app` en + `/local_files/`. -### Fase 4d — UX deletes + cycle check real `[done]` -- **Right-click sobre cable**: borra ese link. -- **Right-click sobre pin output**: limpia el fan-out completo (todos los inputs que apuntaban a este nodo). -- **Right-click sobre pin input**: limpia ese slot. -- **Doble right-click sobre nodo**: borra el nodo (Output protegido). -- Validacion de ciclo via DFS sobre `source_ids` (no por indice del vector); `topo_sort` reordena el pipeline tras cada cambio para mantener `out_` coherentes. -- Drop de nuevo nodo se inserta antes del Output, no al final. +## Paneles -### Fase 4e — Per-node preview `[done]` -- Toggle `[+] preview` / `[-] preview` en cada nodo no-Output (off por defecto). -- Cada nodo abierto tiene su FBO de 96x64 keyed por `editor_uid`. -- Compilador emite `uniform int u_preview_target` y branches `if (u_preview_target == i) { fragColor = out_i; return; }`. -- `dag_previews_render` itera nodos con preview abierto, dibuja al FBO con ese index. -- Sin recompile al togglear preview ni al mover sliders — un solo programa GL. - -### Fase 5 — SQLite + custom generators desde el Code `[done]` -- **`u_params` a tamaño dinámico**: array global `vec4 u_params[64]` (256 floats), cada nodo ocupa `ceil(param_count/4)` vec4s consecutivos. `dag_param_layout(pipeline)` calcula el indice base por nodo; compilador y `dag_uniforms_apply` lo comparten. `DagStep::params` y `DagNodeDef::param_*` pasan a `vector<>`. -- **Nuevos Gen nodes (8)**: `checker`, `stripes`, `dots`, `rings`, `polar_rays`, `noise_value`, `voronoi`, `truchet`. Catalogo total: 19 nodos (4 originales + 8 nuevos Gen + 4 Op + 3 Blend + Output). -- **Bug fix `solid`**: el control Color con `ImGuiColorEditFlags_NoLabel` no mostraba el nombre. Ahora `dag_node_editor` imprime `TextUnformatted(label) + SameLine` antes del swatch. -- **Persistencia `shaders_lab.db`** (SQLite local en `apps/shaders_lab/shaders_lab.db`): tabla `generators` con `id, label, description, source_glsl, body_glsl, param_count, param_defaults, param_names, controls, tags, timestamps`. Funcion `shaderlab_db` (CRUD) testeada (7/7) y reutilizable. -- **Catalogo mutable**: `dag_register_node()` / `dag_unregister_node()`. Built-ins protegidos via flag `is_builtin`. -- **Code → Generator**: funcion pura `code_to_generator(source)` traduce el GLSL del Code en un body de Gen + DagControl[] (testeada 7/7). Cada uniform anotado se convierte en su control (slider/xy/color); cada uniform reclama 1 vec4 entero. El body se transforma asi: lineas `vec2 uv = ...` eliminadas, `fragColor = X;` -> `return X;`, locales ` = u_params[__BASE__+i].swizzle;` prependidas. La lambda `body_glsl` substituye `__BASE__` con el indice runtime. -- **UI**: boton `Save as generator...` en el panel `Code` con modal (name snake_case + label + description + tags). Tras guardar, el nodo aparece en la paleta `Functions`. Al arrancar, `load_user_generators_into_catalog()` re-traduce y registra los persistidos. -- **Quitados**: botones de presets `Plasma / Circle / Checker` y el archivo `seed_shaders.h`. Default del Code = un placeholder con uniforms anotados como ejemplo. - -### Fase 6 — Menubar reusable (View + Layouts) `[done]` - -App estrena una `BeginMainMenuBar` con dos menus, cableada via `app_menubar_cpp_core`: - -- **View** (`panel_menu_cpp_core`): MenuItem checkable por cada uno de los 7 paneles (`Code`, `DAG Pipeline`, `Canvas Code`, `Canvas DAG`, `Controls`, `Functions`, `Generated GLSL`). Cada bool `g_show_*` se comparte con el `bool*` de `ImGui::Begin(name, &g_show_X)`, asi que la X de cada ventana sincroniza con el menu. Cada `Begin/End` envuelto en guard para no llamar `End` si el panel esta oculto. - -- **Layouts** (`layouts_menu_cpp_core`): captura del layout actual de ImGui (`SaveIniSettingsToMemory`) bajo un nombre, persistido en la tabla `ui_layouts(name, blob, created_at, updated_at)` de `shaders_lab.db`. Items: - - Lista de layouts guardados (click → apply, marker `* ` en el activo). - - `Save current as...` (popup con InputText). - - `Delete` (submenu listando los layouts). - - `Reset to default` (abre todos los paneles, limpia marker activo). - -Detalles tecnicos: -- `LoadIniSettingsFromMemory` se difiere al inicio del frame siguiente via `g_pending_layout_blob` (no se puede llamar mid-frame entre `NewFrame` y `Render`). -- `shaders_lab.db` se reutiliza para `ui_layouts` via nuevo getter `shaderlab_db_handle()` — una sola conexion SQLite para generators y layouts. -- Las callbacks (`list/on_apply/on_save/on_delete/on_reset`) se cablean en `main()` con lambdas que envuelven las primitivas CRUD de `layout_storage_sqlite_cpp_core`. - -### Como usarlo en otras apps - -Patron reusable de tres pasos: - -```cpp -#include "core/app_menubar.h" -#include "core/layout_storage_sqlite.h" - -// 1. Declarar bools de visibilidad por panel -static bool g_show_foo = true; -static bool g_show_bar = true; - -// 2. Declarar callbacks y blob diferido -static fn_ui::LayoutCallbacks g_layout_cb; -static std::string g_pending_blob; -static std::string g_pending_name; - -// 3. En main(), cablear callbacks contra tu sqlite3* -fn_ui::layout_storage_init(db); -g_layout_cb.list = [db]{ return fn_ui::layout_storage_list(db); }; -g_layout_cb.on_apply = [db](const std::string& n) { - g_pending_blob = fn_ui::layout_storage_load_blob(db, n); - g_pending_name = n; -}; -g_layout_cb.on_save = [db](const std::string& n) { - size_t sz = 0; - const char* b = ImGui::SaveIniSettingsToMemory(&sz); - if (b && sz) fn_ui::layout_storage_save(db, n, std::string(b, sz)); - g_layout_cb.active_name = n; -}; -g_layout_cb.on_delete = [db](const std::string& n) { - fn_ui::layout_storage_delete(db, n); - if (g_layout_cb.active_name == n) g_layout_cb.active_name.clear(); -}; -g_layout_cb.on_reset = []{ /* abrir todos los paneles, limpiar active_name */ }; - -// 4. En render(), aplicar pendientes y llamar app_menubar -void render() { - if (!g_pending_blob.empty()) { - ImGui::LoadIniSettingsFromMemory(g_pending_blob.c_str(), g_pending_blob.size()); - g_layout_cb.active_name = g_pending_name; - g_pending_blob.clear(); g_pending_name.clear(); - } - ImGui::DockSpaceOverViewport(0, ImGui::GetMainViewport()); - - fn_ui::PanelToggle toggles[] = { - {"Foo", "Ctrl+1", &g_show_foo}, - {"Bar", "Ctrl+2", &g_show_bar}, - }; - fn_ui::app_menubar(toggles, std::size(toggles), &g_layout_cb); - - if (g_show_foo) { - if (ImGui::Begin("Foo", &g_show_foo)) { /* ... */ } - ImGui::End(); - } - // ... -} -``` - -### Fase 7 — UX node editor + DAG correctness `[done]` (2026-04-25) - -Pulido de la edición visual del DAG y corrección de fugas en el render. Sin cambio de schema ni de catalog público (más allá del `dag_register_node` ya añadido en Fase 5). - -- **Nodos más grandes para conectar más rápido** (`dag_node_editor.cpp`): - - `PIN_RADIUS` 9 → **14 px** (área de grab ~2.5×). `PIN_DIAMETER`, `CABLE_THICK` 2.5 → **3.5**, borde de pin 1.5 → 2.0. - - `CONTROL_WIDTH` constante 150 → **220 px**, `COL_GAP` 8 → **14 px**, `NodePadding` vertical 8 → 12. - - Espaciado inicial entre nodos auto-colocados 220 → 320 px. -- **Bug fix `solid` sin label**: el control `Color` usaba `ImGuiColorEditFlags_NoLabel`, así que el swatch era el único contenido del nodo y parecía "sin nombre ni parámetro". Fix en `dag_node_editor.cpp`: imprimir `ImGui::TextUnformatted(ctrl.label) + SameLine` antes del swatch. Aplica a todo control de tipo `Color`, no solo a `solid`. -- **Strict output** (`dag_compile.cpp`): eliminado el fallback `last_valid_out` que filtraba el output del último nodo evaluado cuando `Output` no tenía source o no existía. Ahora la regla es: solo se emite lo conectado al nodo `Output`; en cualquier otro caso `seed()` (gris oscuro `vec4(0.04, 0.04, 0.06, 1.0)`). El `resolve()` de inputs internos también dejó de caer a `last_valid_out` y ahora emite `vec4(0,0,0,1)` para slots sin conectar. Tests: `dag_compile` 6/6 → **7/7** (test 4b verifica que el seed final aparece después de las branches de preview, no antes). -- **Generated GLSL autocontenido** (`compile_dag_to_glsl_baked`, nuevo en `dag_compile.{h,cpp}`): - - Sustituye `uniform vec4 u_params[64]` por `const vec4 u_params[N] = vec4[N](vec4(...), ...)` con los valores actuales del pipeline empaquetados (mismo layout que `dag_uniforms_apply`). - - Sustituye `uniform int u_preview_target` por `const int u_preview_target = -1` (las branches de preview quedan muertas y el GLSL compiler las elimina). - - Resultado: el shader del panel `Generated GLSL` no depende de ningún uniform externo. Pegarlo en el editor `Code` reproduce exactamente el render del DAG en el momento del copy. Después editar el DAG no afecta al Code. - - Test 7 nuevo: `dag_compile_baked` no contiene `uniform vec4 u_params` ni `uniform int u_preview_target`, sí contiene `const vec4 u_params[` y los valores empaquetados. - - **Importante**: el `Canvas Code` ya NO recibe `dag_uniforms_apply`. Es totalmente independiente. (Versión anterior intentaba sincronizarlos; rompía el aislamiento entre paneles.) -- **`dag_uniforms_apply` también resetea `u_preview_target = -1`** al final, para que la rama de preview quede desactivada en el render principal del Canvas DAG. La rutina `dag_previews_render` la activa de forma transitoria por nodo y la deja restaurada. -- **Drop-replace del mismo kind**: - - Soltar un nodo de la paleta sobre un nodo existente del **mismo `DagKind`** (Gen sobre Gen, Op sobre Op, Blend sobre Blend, nunca sobre Output) sustituye `name`+`params`+`controls` conservando `id`, `editor_uid`, `editor_pos_x/y`, `source_ids[]` y `preview_open`. - - Slots de input que sobran (si el nuevo def tiene menos `num_inputs` que el anterior) se limpian. - - Hit-test contra cajas de nodos vía `ed::GetNodePosition` + `ed::GetNodeSize` (canvas-space). No se usa `ed::GetHoveredNode()` porque no es fiable durante un drag-drop activo. -- **Drop-on-cable splice (intercalar nodo)**: - - Soltar un nodo de la paleta **o** arrastrar un nodo Op/Blend ya existente sobre un cable: el nodo se inserta entre `src` y `dst`. `new.source_ids[0] = src.id`, `dst.source_ids[slot] = new.id`. Para Blend (2 inputs), slot 0 queda cableado y slot 1 vacío. - - Para nodos existentes movidos: además de las dos rewires anteriores, se limpian todas las refs hacia el nodo movido en otros `source_ids[]` antes (lo desengancha de cualquier consumidor previo, queda exclusivamente en la nueva posición). Tracking del nodo arrastrado vía `s_drag_existing_uid` (set en `IsMouseClicked(0)` cuando hay un nodo hovered y no hay pin hovered, def es Gen/Op/Blend, no Output). - - Hit-test del cable: distancia punto-segmento (`dist_point_to_segment`) entre el cursor y la línea aproximada `(src.right_mid → dst.left_at_slot_k)`. Threshold **18 px** canvas-space. - - Prioridad: cable-hit > node-hit > add-vacío. -- **Splice highlight (preview visual)**: - - Mientras hay un drag activo de paleta o de nodo del canvas, el cable candidato se redibuja en `SPLICE_COLOR = (1.00, 0.82, 0.18, 1)` (dorado) más grueso (`CABLE_THICK + 2`). - - **Garantía visual**: además de cambiar el color en `ed::Link()`, se dibuja un bezier dorado encima en el `ImGui::GetForegroundDrawList()` (canvas → screen via `ed::CanvasToScreen`). Esto evita problemas de compositing interno del editor que podían enterrar el cambio de color. - - Detección sin gates: la versión anterior gateaba con `IsMouseDown` + `window_hovered`, lo que silenciaba el highlight. Ahora basta con la presencia del payload de drag-drop (paleta) o del `s_drag_existing_uid` (nodo del canvas). -- **Catalog `dag_catalog.cpp`** ya soporta `is_builtin` (Fase 5) y permite `dag_register_node` / `dag_unregister_node` para generators custom; el splice/replace funciona sobre todos por igual (Built-ins, Gen custom guardados desde Code). - -Comandos: -```bash -# Build linux -./fn run build_cpp_linux_bash_infra shaders_lab - -# Build windows (cross-compile) -./fn run build_cpp_windows_bash_infra shaders_lab - -# Tests del dominio gfx (puros, sin GL) -g++ -std=c++17 -Icpp/functions -DDAG_CATALOG_TEST cpp/functions/gfx/dag_catalog.cpp -o /tmp/dag_catalog_test && /tmp/dag_catalog_test -g++ -std=c++17 -Icpp/functions -DDAG_COMPILE_TEST cpp/functions/gfx/dag_compile.cpp cpp/functions/gfx/dag_catalog.cpp -o /tmp/dag_compile_test && /tmp/dag_compile_test -g++ -std=c++17 -Icpp/functions -DCODE_TO_GENERATOR_TEST cpp/functions/gfx/code_to_generator.cpp cpp/functions/gfx/uniform_parser.cpp -o /tmp/code_to_generator_test && /tmp/code_to_generator_test -g++ -std=c++17 -Icpp/functions -DUNIFORM_PARSER_TEST cpp/functions/gfx/uniform_parser.cpp -o /tmp/uniform_parser_test && /tmp/uniform_parser_test -gcc -c -O2 -DSQLITE_THREADSAFE=1 cpp/vendor/sqlite3/sqlite3.c -o /tmp/sqlite3.o && \ - g++ -std=c++17 -Icpp/functions -Icpp/vendor/sqlite3 -DSHADERLAB_DB_TEST cpp/functions/gfx/shaderlab_db.cpp /tmp/sqlite3.o -lpthread -ldl -o /tmp/shaderlab_db_test && /tmp/shaderlab_db_test -``` - -Cobertura de tests inline tras esta fase: **8 + 7 + 7 + 6 + 7 = 35 asserts** sobre `dag_catalog` (19 nodos), `dag_compile` (strict + baked), `code_to_generator`, `uniform_parser`, `shaderlab_db`. - -Sync de binarios Windows (regla establecida en esta sesión): -- `cpp/build/windows/apps/shaders_lab/shaders_lab.exe` (origen) -- `apps/shaders_lab/shaders_lab.exe` (in-repo) -- `/mnt/c/Users/lucas/Desktop/shaders_lab.exe` (Windows Desktop) -- **NUNCA** copiar a `/mnt/c/Users/AdminLocal/`. Memoria persistente: `feedback_no_adminlocal.md`. - -## Lo siguiente que pega - -- Push selectivo al registry global: boton `Push to registry` que extrae el generator a `cpp/functions/gfx/.{cpp,md}` con tag `shaders_lab` y dispara `fn index`. -- Listado / borrado de generators custom desde la UI (hoy solo via DB directa). -- Persistencia de pipelines con nombre. -- Mas nodos: warps (twirl, polar, kaleidoscope), perlin/fbm reales, SDFs, filtros de luma. -- Save as Op (1 input `a`) y Save as Blend (2 inputs). -- Crossfade A↔B: tercer canvas que mezcla Canvas Code y Canvas DAG con un slider. -- Cliente Claude: chat con tool use (`search_registry`, `apply_shader`, `save_function`). -- Integracion VJ: Spout/Syphon/NDI para mandar el output a Resolume/OBS. - -Documentacion de exploraciones aparcadas (no en backlog inmediato): -- `NEXT_STEPS_DATA_TYPES.md` — extensiones del DAG: pins tipados, texturas, SDF/raymarch, multi-pass, geometria 3D. -- `NEXT_STEPS_BORDERLESS_WINDOW.md` — quitar la titlebar del SO y mover min/max/close al `MainMenuBar` ImGui. +| Panel | Atajo | Que muestra | +|---|---|---| +| Code | Ctrl+1 | Editor del fragment shader + boton "Save as generator" | +| DAG Pipeline | Ctrl+2 | Node editor con la pipeline | +| Canvas Code | Ctrl+3 | Render del Code shader | +| Canvas DAG | Ctrl+4 | Render del shader compilado del DAG | +| Controls | Ctrl+5 | Sliders/color pickers de uniforms anotados | +| Functions | Ctrl+6 | Paleta del DAG (generators + filters + output) | +| Generated GLSL | Ctrl+7 | GLSL final del DAG con uniforms baked como const array | ## Build ```bash -./fn run build_cpp_linux_bash_infra shaders_lab -./fn run build_cpp_windows_bash_infra shaders_lab +# Linux +cd cpp && cmake -B build/linux -S . && cmake --build build/linux --target shaders_lab + +# Windows (cross-compile) +cd cpp && cmake -B build/windows -S . -DCMAKE_TOOLCHAIN_FILE=toolchains/mingw-w64.cmake \ + && cmake --build build/windows --target shaders_lab ``` -- Linux: `cpp/build/linux/apps/shaders_lab/shaders_lab` -- Windows (cross-compile mingw-w64): `cpp/build/windows/apps/shaders_lab/shaders_lab.exe` — copiado a `apps/shaders_lab/shaders_lab.exe` y al Desktop tras cada build. +## Decisiones -## Tests - -```bash -g++ -std=c++17 -Icpp/functions -DUNIFORM_PARSER_TEST cpp/functions/gfx/uniform_parser.cpp -o /tmp/uniform_parser_test && /tmp/uniform_parser_test -g++ -std=c++17 -Icpp/functions -DDAG_COMPILE_TEST cpp/functions/gfx/dag_compile.cpp cpp/functions/gfx/dag_catalog.cpp -o /tmp/dag_compile_test && /tmp/dag_compile_test -g++ -std=c++17 -Icpp/functions -DDAG_CATALOG_TEST cpp/functions/gfx/dag_catalog.cpp -o /tmp/dag_catalog_test && /tmp/dag_catalog_test -``` - -Cobertura actual: 6 + 6 + 8 = 20 asserts puros (sin GL/ImGui). La parte UI (`dag_node_editor`, `dag_palette`, `uniform_panel`, `shader_canvas`) no es testeable sin entorno grafico. - -## Uniforms del Code mode - -Auto-prependidos por `compile_fragment`: - -```glsl -uniform vec2 u_resolution; -uniform float u_time; -uniform vec2 u_mouse; -out vec4 fragColor; -``` - -El cuerpo del fragment se escribe sin `#version`, sin `out`, sin esos uniforms. Cualquier `uniform` adicional declarado por el usuario con anotacion (`// @slider`, etc.) genera un widget en `Controls`. - -## Uniforms del DAG mode - -Ademas de los anteriores, el shader generado por `compile_dag_to_glsl` declara: - -```glsl -uniform vec4 u_params[16]; // 4 floats por nodo (slot del nodo i en u_params[i]) -uniform int u_preview_target; // -1 = real Output; 0..15 = render out_ -``` - -`dag_uniforms_apply` sube `u_params[16]` cada frame antes del draw del Canvas DAG. `dag_previews_render` rebinde el FBO de cada nodo abierto y setea `u_preview_target` antes de cada draw. - -## Layouts - -ImGui persiste el layout actual en `imgui.ini` junto al binario (autosave). Ademas, el menu **Layouts** permite tener varios layouts guardados con nombre: - -- Mueve los paneles donde quieras. -- `Layouts > Save current as...` y dale un nombre (ej. "Coding", "DAG mode", "Showcase"). -- Cambia el layout, guarda otro. -- `Layouts > ` para saltar; el activo se marca con `* `. -- `Layouts > Delete > ` para borrar. -- `Layouts > Reset to default` reabre todos los paneles y limpia el marker. - -Los layouts guardados viven en la tabla `ui_layouts` de `shaders_lab.db`. - -Disposicion comoda al primer arranque: -- `Code` y `DAG Pipeline` ocupan la fila superior. -- `Canvas Code` y `Canvas DAG` ocupan la fila inferior, lado a lado. -- `Functions` y `Controls` van a un lateral. -- `Generated GLSL` minimizado o en pestana junto a `Controls`. - -El menu **View** togglea cada panel individualmente (mismo `bool*` que la X de la ventana). - -## Notas de cross-compile - -- `gl_loader` resuelve simbolos OpenGL 2.0+ con `wglGetProcAddress` en Windows; en Linux es no-op (`GL_GLEXT_PROTOTYPES`). -- `WIN32_EXECUTABLE TRUE` en `CMakeLists.txt` evita la consola al lanzar el .exe. -- Vendor de imgui-node-editor cuesta ~1MB en el binario final (~18 MB total). - -## Notas — Settings + iconos (sesion 2026-04-25) - -- `app_menubar` ahora añade automaticamente un tercer item `Settings...` junto a `View` y `Layouts`. Click abre la ventana flotante de `app_settings` (Display: toggle FPS overlay; Typography: combo de fuente Karla/Roboto/DroidSans/Cousine + slider de tamaño 10..32 px). Persiste en `app_settings.ini` junto a `shaders_lab.exe`. -- Defaults: DroidSans 15 px, FPS overlay off (antes hardcoded ON dentro del panel `Controls`). -- Removida la llamada explicita `fps_overlay()` del panel `Controls` — ahora se respeta el toggle de Settings. -- Removidos los `.cpp` de `fps_overlay`, `panel_menu`, `layouts_menu`, `app_menubar` del `CMakeLists.txt` — viven en `fn_framework` para evitar multiple-definition. Solo `layout_storage_sqlite.cpp` sigue listado explicitamente. -- 5 TTFs (Karla / Roboto / DroidSans / Cousine / Tabler) copiadas junto al exe via `add_imgui_app` post-build. - -Para añadir secciones propias de settings: - -```cpp -// En main.cpp antes de fn::run_app: -fn_ui::settings_window_add_section("shader_compiler", "Shader compiler", []{ - ImGui::Checkbox("Auto-compile on save", &g_auto_compile); - ImGui::SliderInt("Debounce (ms)", &g_debounce_ms, 50, 2000); -}); -// Aparece debajo de Display/Typography. Persistencia propia (puede ir en -// shaders_lab.db, tabla ui_settings). -``` - -## Lo siguiente que pega - -- Ejemplo concreto de seccion extra de settings: `auto-compile on save` + `debounce_ms` registrados desde `main.cpp` y persistidos en una tabla `ui_settings` en `shaders_lab.db`. -- Auditar hex UTF-8 (`"\x..\x.."`) o emojis Unicode hardcoded en uniform_panel, dag_panel, dag_node_editor → migrar a `TI_*` de `core/icons_tabler.h`. -- Rebuild Windows + sync: `cmake --build cpp/build/windows --target shaders_lab && cp cpp/build/windows/apps/shaders_lab/{shaders_lab.exe,*.ttf} /mnt/c/Users/lucas/Desktop/apps/shaders_lab/`. +- `init_gl_loader = true` (via `fn::run_app` por default cuando se enlaza + con OpenGL) — `shader_canvas`, `gl_shader`, `gl_framebuffer` llaman gl*. +- `viewports = true` — los Canvas se pueden arrastrar fuera del main. +- DAG default: arranca con un nodo "plasma" + "output" si la paleta los + encuentra; persiste el INI con `layout_storage`. +- El boton "Save as generator" valida snake_case, evita colisionar con + builtins, traduce con `code_to_generator`, persiste con `shaderlab_db_save_generator`, + y registra el nodo nuevo en el catalogo en vivo (`dag_register_node`). diff --git a/dev/issues/0063-kanban-stickers.md b/dev/issues/0063-kanban-stickers.md new file mode 100644 index 00000000..fb0ac5f2 --- /dev/null +++ b/dev/issues/0063-kanban-stickers.md @@ -0,0 +1,139 @@ +# 0063 — kanban: sistema de stickers (emojis) sobre cards + +## APP Metadata + +| Campo | Valor | +|-------|-------| +| **ID** | 0063 | +| **Estado** | pendiente | +| **Prioridad** | media | +| **Tipo** | feature — `apps/kanban/` | + +## Dependencias + +- Ninguna. Aplica TBD obligatorio (`.claude/rules/apps_tbd.md`): trabajar en `issue/0063-stickers`, merge `--no-ff` a master. + +## Objetivo + +Permitir que el usuario "decore" visualmente las cards del tablero pegandoles emojis (stickers) con transparencia. Util para marcar estados visualmente sin abusar de tags ni colores: 🔥 (urgente), ✅ (revisado), ⚠️ (cuidado), 🚀 (lanzado), ❤️ (importante), 💀 (bloqueado), etc. + +## Contexto + +`apps/kanban/` es una app Go que embebe un frontend React + Mantine v9 + dnd-kit. El tablero esta en `frontend/src/App.tsx` y cada card se renderiza en `frontend/src/components/KanbanCard.tsx`. Las cards ya tienen color (`color`), tags, asignado, bloqueo, historial y descripcion. Faltan **stickers** como capa decorativa libre, separada del modelo de tags. + +UX deseada: + +1. Usuario hace clic en boton "Stickers" de la toolbar del board (`App.tsx:920`). +2. Aparece picker con lista de emojis predefinidos. +3. Usuario selecciona uno → entra en "modo pegar sticker" (cursor cambia, indicador visible). +4. Hace clic sobre cualquier card → el emoji se pinta encima de la card con opacidad (~0.6). +5. ESC sale del modo. Click derecho sobre un sticker existente lo borra. +6. Stickers persisten en BD para que sobrevivan recargas y todos los usuarios los vean. + +## Decisiones de diseno (a confirmar antes de implementar) + +| Decision | Recomendacion default | Alternativas | +|----------|-----------------------|--------------| +| Persistencia | BD (campo `stickers JSON` en cards) | localStorage por usuario | +| Cantidad por card | Multiples apilados | Uno solo | +| Posicion | Arrastrable dentro de la card | Esquina/centrado fijo | +| Tamaño / opacidad | Fijos: 48px, 0.6 | Configurables por sticker | +| Fuente emojis | Lista hardcoded ~20 | `emoji-mart`, picker nativo | +| UX modo activo | Cursor especial + ESC sale | Toggle persistente | +| Borrado | Click derecho sobre sticker | Modo "quitar sticker" | + +Confirmar con usuario antes de Fase 1. Si elige defaults, proceder. + +## Arquitectura + +### Archivos afectados + +**Backend Go (`apps/kanban/`):** +- `migrations/00X_add_stickers.sql` (NEW) — `ALTER TABLE cards ADD COLUMN stickers TEXT NOT NULL DEFAULT '[]'`. +- `db.go` — leer/escribir columna `stickers` en `Card`. +- `handlers.go` — endpoint `PATCH /api/cards/:id/stickers` (body: `{"stickers":[{emoji,x,y}]}`). Reusar el patron de `updateCard`. +- `tools.go` — exponer mutacion al chat agent si aplica (opcional). + +**Frontend (`apps/kanban/frontend/src/`):** +- `types.ts` — añadir `Sticker { emoji: string; x: number; y: number; }` y `Card.stickers: Sticker[]`. +- `api.ts` (NEW endpoint helper) — `updateCardStickers(id, stickers)`. +- `App.tsx` — boton "Stickers" en toolbar (`:920`), estado `pickerOpen`, `activeSticker` (emoji seleccionado en modo pegar), handler global ESC, handler `onCardSticker(cardId, x, y)`. +- `components/KanbanCard.tsx` — render overlay absoluto de `card.stickers` (z-index alto, pointer-events: none salvo en modo borrado), cuando hay `activeSticker` cambiar cursor + capturar onClick para añadir sticker en coords relativas. +- `components/StickerPicker.tsx` (NEW) — Popover con grid de emojis predefinidos. + +**Pure / impure split:** +- Pure: util `relativeCoords(event, cardEl) → {x,y}` (frontend, en `components/format.ts` o nuevo `stickers.ts`). +- Impure: handler de PATCH HTTP, mutacion de BD, broadcast de cambio si hay realtime. + +### Funciones del registry a reutilizar + +Auditar antes de escribir: + +```bash +sqlite3 registry.db "SELECT id, description FROM functions WHERE id IN (SELECT id FROM functions_fts WHERE functions_fts MATCH 'name:emoji OR description:emoji OR name:sticker OR description:sticker');" +``` + +Probable: nada existe. Si `relativeCoords` resulta reusable → delegar a `fn-constructor` y crear `dom_relative_coords_ts_core` o similar (evaluar tras decision UX). + +## Tareas + +### Fase 1 — confirmar diseno +1.1 Confirmar con usuario las 7 decisiones de la tabla. Si elige defaults, seguir. +1.2 Auditar registry: `fn search "emoji"`, `fn search "sticker"`. Documentar reutilizacion en este issue. + +### Fase 2 — backend +2.1 Crear migracion `migrations/00X_add_stickers.sql`. +2.2 Tipo Go `Sticker { Emoji string; X, Y float64 }` en `db.go`. `Card.Stickers []Sticker` con custom marshalling JSON ↔ TEXT. +2.3 Endpoint `PATCH /api/cards/:id/stickers` en `handlers.go`. Validar payload (emoji no vacio, x/y en [0,1]). +2.4 Aplicar migracion al arrancar (mecanismo existente de `apps/kanban/migrations`). + +### Fase 3 — frontend tipos + api +3.1 Añadir `Sticker` y `Card.stickers` en `types.ts`. +3.2 Añadir `updateCardStickers(id, stickers)` en `api.ts`. + +### Fase 4 — UI: picker + modo activo +4.1 Crear `components/StickerPicker.tsx` con lista hardcoded (≈20 emojis: 🔥⭐✅⚠️🚀💀🎯❤️👀💡📌🐛✨🎉🙏🤔😅🚧🟢🔴). +4.2 En `App.tsx`: nuevo state `activeSticker: string | null`. Boton "Stickers" con `IconMoodSmile` o similar de `@tabler/icons-react`. Popover con `StickerPicker`. Listener global `keydown` ESC → `setActiveSticker(null)`. +4.3 Indicador visual del modo activo (banner o cursor custom). + +### Fase 5 — render + interaccion en cards +5.1 En `KanbanCard.tsx`: render overlay absoluto de `card.stickers` (cada uno como `{emoji}`). +5.2 Cuando `activeSticker !== null`, en `onClick` de la Paper de la card calcular coords relativas (`event.offsetX / cardEl.offsetWidth`, idem Y, clamp [0,1]), pushear nuevo sticker a `card.stickers`, llamar `updateCardStickers`, optimistic update. +5.3 Click derecho sobre un sticker → quitar de la lista + persistir. + +### Fase 6 — tests + verificacion +6.1 `tools_test.go`: extender (o nuevo `stickers_test.go`) con test de PATCH `/api/cards/:id/stickers` (insertar 2, borrar 1, leer card). +6.2 Manual smoke test: boton aparece, picker funciona, sticker se pega, persiste tras F5, click derecho borra, ESC sale del modo. +6.3 `fn doctor uses-functions` no debe regresionar para `kanban_go_tools`. + +### Fase 7 — docs y cleanup +7.1 Si se reutilizo o creo funcion del registry → declararla en `apps/kanban/app.md` (`uses_functions`). +7.2 Una linea en `apps/kanban/app.md` describiendo la feature. +7.3 `fn index` y verificar `fn show kanban_go_tools`. + +## Ejemplo de uso + +``` +1. Usuario clica "Stickers" en toolbar del board +2. Picker abre, usuario clica 🔥 +3. Cursor pasa a "modo pegar", banner: "Modo sticker: 🔥 — ESC para salir" +4. Usuario clica en card "Migrar usuarios" + → 🔥 aparece donde clico, opacidad 0.6, persistido +5. Usuario sigue clicando → mas 🔥 se acumulan +6. Pulsa ESC → modo off, cursor normal +7. F5 → stickers siguen ahi +8. Click derecho sobre 🔥 → desaparece +``` + +## Riesgos + +- **Hit-test confuso**: si los stickers absorben pointer events, rompen drag-and-drop de la card. Mitigacion: `pointerEvents: 'none'` salvo cuando estamos en modo borrado. +- **Coords absolutas vs %**: si guardamos px y la card cambia de ancho (resize de columna), el sticker se sale. Guardar como % (`x,y ∈ [0,1]`). +- **Coleccion en chat tool agent**: si el chat agent expone tools que hacen `updateCard` con payload completo, debe respetar el campo `stickers` y no sobrescribirlo a null. +- **Migracion de BD en prod (laptop + casa)**: la migracion corre en cada arranque idempotente, OK. Pero confirmar antes en prod local del usuario. +- **WIP en kanban (issue 0058)**: este issue toca `app.md` igualmente — coordinar para no pisar el sync pendiente de `uses_functions`. + +## Prerequisitos + +- Confirmar las 7 decisiones de diseno con el usuario (Fase 1). +- TBD: rama `issue/0063-stickers` desde `master` actualizado. diff --git a/dev/issues/README.md b/dev/issues/README.md index 00e6c482..7e915974 100644 --- a/dev/issues/README.md +++ b/dev/issues/README.md @@ -78,3 +78,4 @@ | [0060](0060-fn-doctor-secrets-subcommand.md) | `fn doctor secrets`: scan de secrets en TODOS los repos | pendiente | media | feature | — | | [0061](0061-notify-telegram-integration.md) | Integrar `notify_telegram` en deploy_server + bucle reactivo | pendiente | media | integration | 0054 | | [0062](0062-deprecate-unused-core-functions.md) | Politica de deprecacion para funciones del registry sin consumidores | pendiente | baja | research | — | +| [0063](0063-kanban-stickers.md) | kanban: sistema de stickers (emojis) sobre cards | pendiente | media | feature | — |