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

23 KiB
Raw Blame History

name, lang, domain, description, tags, uses_functions, uses_types, framework, entry_point, dir_path
name lang domain description tags uses_functions uses_types framework entry_point dir_path
shaders_lab cpp gfx 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.
gui
shaders
opengl
glsl
imgui
node-editor
dag
vj
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
imgui + opengl3 + imgui-node-editor cpp/build/linux/apps/shaders_lab/shaders_lab 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:

#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:

# 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

./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

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:

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:

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:

// 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/.