Files
fn_registry/apps/shaders_lab/app.md
T
egutierrez b093c898a8 docs(issues): marcar 0025 y 0026 como completados + WIP master
Wave 1 de parallel-fix-issues integrada a master:
- 0025: text_editor_cpp_core + file_watcher_cpp_core
- 0026: gl_texture_load_cpp_gfx (vendor: stb_image v2.30)

Ademas se commitea WIP previo de master que estaba sin commitear (cambios
en shaders_lab, dag_*, framework, tokens, kpi_card, gl_loader.md, etc.)
para dejar HEAD buildable.

Notas:
- Algunos deps del gallery (button.cpp, toolbar.cpp, modal_dialog.cpp...)
  siguen UNTRACKED — gating con FN_BUILD_GALLERY=ON (default OFF) para
  que master build (sin flag) no los necesite.
- Build OK con y sin flag. fn index registra 904 functions.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
2026-04-25 21:14:15 +02:00

351 lines
23 KiB
Markdown
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
---
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]
uses_functions:
- gl_loader_cpp_gfx
- gl_shader_cpp_gfx
- gl_framebuffer_cpp_gfx
- fullscreen_quad_cpp_gfx
- shader_canvas_cpp_gfx
- uniform_parser_cpp_gfx
- uniform_panel_cpp_gfx
- dag_catalog_cpp_gfx
- dag_compile_cpp_gfx
- dag_uniforms_cpp_gfx
- dag_palette_cpp_gfx
- dag_node_editor_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"
dir_path: "cpp/apps/shaders_lab"
---
## Descripcion
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.
## Estado actual
### 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.
### 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).
### 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_<i>(...)` 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).
### 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.
### 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.
### 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).
### 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_<i>` coherentes.
- Drop de nuevo nodo se inserta antes del Output, no al final.
### 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 `<type> <name> = 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/<name>.{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.
## Build
```bash
./fn run build_cpp_linux_bash_infra shaders_lab
./fn run build_cpp_windows_bash_infra 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.
## 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_<i>
```
`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 > <nombre>` para saltar; el activo se marca con `* `.
- `Layouts > Delete > <nombre>` 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/`.