El modal Save-as-generator usaba BeginPopupModal + InputText + Button
crudo. Ahora usa fn_ui::modal_dialog_begin/end + fn_ui::text_input +
fn_ui::button del registry. El error inline usa fn_tokens::colors::error
en vez de ImVec4(1, 0.4, 0.4, 1). Anade modal_dialog.cpp, text_input.cpp
y button.cpp al CMakeLists del app.
Raw ImGui::Begin*/Selectable/BeginPopupModal: 11 -> 8.
El sidebar agrupaba demos por categoria con un Selectable+PushStyleColor
manual por item. Ahora usa fn_ui::tree_view con las categorias como
ramas (default-open via SetNextItemOpen + ImGuiCond_FirstUseEver) y las
demos como hojas seleccionables. Visualmente equivalente: separadores
por categoria, item activo coloreado.
Raw ImGui::Begin*/Selectable: 4 -> 3 (Selectable eliminado).
Nueva seccion "Tests visuales y CI gate (issue 0048)" describiendo:
- Como capturar/regenerar goldens con cpp/scripts/update_goldens.sh.
- Como diagnosticar un diff (PNG actual en cpp/build/tests/visual_actual/
vs golden en cpp/tests/golden/).
- Cuando test_visual SKIPea (sin goldens, sin binario, sin GL).
- CI gate check_tested.sh y los pasos para satisfacerlo.
Issue 0048.
- update_goldens.sh: build primitives_gallery + lanza --capture sobre
cpp/tests/golden/ con LIBGL_ALWAYS_SOFTWARE=1.
- check_tested.sh [days]: CI gate que falla si una funcion C++ creada en
los ultimos N dias (default 30) no tiene tested:true en su .md. Hookeado
al final de run_tests.sh. No-op si registry.db no existe.
Issue 0048.
- png_diff.{h,cpp}: pixel_diff_ratio(path_a, path_b, channel_threshold) con
stb_image. Devuelve PngDiffResult con pixels_total, pixels_different y
diff_ratio. Si dimensiones difieren, diff_ratio=1.0.
- test_visual.cpp: invoca primitives_gallery --capture sobre tmpdir, compara
cada PNG vs cpp/tests/golden/<demo>.png con tolerancia 1% pixels distintos
(threshold 5/255 por canal). SKIPea con WARN si:
* golden dir vacio (no hay goldens todavia)
* binario primitives_gallery no construido
* el binario falla al capturar (entorno sin GL)
- CMakeLists: registra test_visual con FN_TEST_GOLDEN_DIR, FN_TEST_GALLERY_BIN,
FN_TEST_TMP_DIR y FN_TEST_REPO_ROOT (para que la captura corra desde la
raiz del repo y resuelva paths relativos como sql_workbench's registry.db).
- golden/: 41 PNGs iniciales generados en este entorno (WSL +
LIBGL_ALWAYS_SOFTWARE=1). Pueden regenerarse con cpp/scripts/update_goldens.sh.
Issue 0048.
Modo de captura que renderiza cada demo de la gallery en una ventana GLFW
invisible (GLFW_VISIBLE=GLFW_FALSE) y guarda PNG por demo via stb_image_write.
- capture.{h,cpp}: API gallery::run_capture(cfg, items) — warmup_frames,
glReadPixels(GL_RGBA), flip vertical, stbi_write_png.
- main.cpp: parsea --capture <dir> antes de fn::run_app y delega a capture.cpp.
- vendor: stb_image_write.h v1.16 (mismo commit que stb_image.h).
Funciona en WSL con LIBGL_ALWAYS_SOFTWARE=1 (Mesa/llvmpipe). Si el entorno
no tiene contexto GL, el binario sale con rc!=0 sin generar PNGs.
Issue 0048.
20 funciones C++ pasan de tested:false a tested:true con sus tests
correspondientes y test_file_path apuntando a cpp/tests/. Cubre 4 tests
reales (tween_curves, pie/kpi/bar math) y 16 placeholders (componentes
UI con tests visuales pendientes para 0048).
Coverage cpp pasa de 4% (3/81) a 28% (23/81).
Cada placeholder garantiza que el .cpp compila y linka contra Catch2,
y reserva el slot para tests futuros una vez se extraiga logica pura
del componente. La validacion visual real vive en primitives_gallery
(issue 0048).
Cubre: tokens, button, select, text_input, badge, kpi_card, pie_chart,
bar_chart, tree_view, modal_dialog, toolbar, toast, empty_state,
page_header, dashboard_panel, dashboard_grid, sparkline, table_view,
icon_button.
- test_tween_curves: boundary conditions (t=0, t=0.5, t=1), monotonicidad
para curvas no oscilantes, dispatch via apply(), names() no nulos.
- test_pie_chart_math: replica slice_at (anonymous namespace en pie_chart.cpp)
y testea hit-test angular, edge del radio, distribucion proporcional.
- test_kpi_card_math: classify_delta (Up/Down/Flat) y pct_change con
zero/negativos. La logica visual la cubre primitives_gallery (issue 0048).
- test_bar_chart_math: compute_y_range (incluye 0, negativos, vacio,
single value) y clamp_bar_width [0.05, 1.0].
cpp/tests/CMakeLists.txt compila Catch2 amalgamated como STATIC libreria
una sola vez. Cada test es su propio executable (CATCH_CONFIG_MAIN por
archivo) y se registra con add_test(). add_fn_test(name srcs...) es el
helper: incluye paths de cpp/functions y cpp/framework, linka catch2.
Tests que necesitan symbols reales (fn_framework, imgui) los anaden
explicitamente con target_link_libraries despues.
Sustituye ~30 lineas de cableado manual de save/load/list/delete contra
layout_storage_sqlite por dos llamadas a la nueva API publica:
g_layouts = fn_ui::layout_storage_open("shaders_lab.db");
fn_ui::layout_storage_make_callbacks(g_layouts, g_layout_cb);
El blob pendiente lo gestiona el storage (layout_storage_apply_pending).
on_reset se override para ademas re-mostrar los paneles de shaders_lab.
La tabla ui_layouts heredada queda intacta — la nueva API usa
imgui_layouts en la misma BD.
Auditoria del issue 0044: anota en notes: el contexto de consumo de
huerfanos que no pueden registrarse en uses_functions porque sus
consumidores no son funciones del registry:
- consumido por cpp/framework/app_base.cpp (framework no indexado)
- consumido por cpp/apps/{shaders_lab,chart_demo,text_editor_smoke}/main.cpp
- scaffolding/demo en primitives_gallery
31 huerfanas anotadas. Las que quedan en uses_functions=[] tras esto
son hojas legitimas (no llaman a nada) o realmente sin uso (lista
DEAD reportada en el issue 0044).
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
Auditoria del issue 0044: 14 archivos .md de cpp/functions/gfx/ con
uses_functions actualizado. Resuelve dependencias detectadas via
#include: gl_loader (consumido por casi todo el dominio gfx),
dag_catalog (consumido por la familia dag_*), fullscreen_quad,
gl_framebuffer, gl_shader, mesh_obj_load, uniform_parser y
dag_node_previews.
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
Auditoria del issue 0044: 17 archivos .md de cpp/functions/core/ con
uses_functions actualizado para reflejar las llamadas reales detectadas
mediante #include en sus .cpp/.h. Los huerfanos referenciados (tokens,
app_about, app_settings, layouts_menu, panel_menu, table_view,
text_editor, tween_curves, app_settings) ahora aparecen en el grafo de
dependencias del registry.
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
API publica con handle opaco LayoutStorage* que envuelve la persistencia
de layouts ImGui en SQLite. Cualquier app puede obtener un LayoutCallbacks
listo para app_menubar/layouts_menu_items con dos llamadas:
auto* st = fn_ui::layout_storage_open("app.db");
fn_ui::LayoutCallbacks cb;
fn_ui::layout_storage_make_callbacks(st, cb);
Tabla SQLite imgui_layouts(name, ini, updated_at) creada con
CREATE TABLE IF NOT EXISTS para no chocar con tablas pre-existentes.
fn_framework ahora enlaza SQLite::SQLite3 para que cualquier app que use
el framework herede acceso a layout_storage sin trabajo extra.
- toast.cpp: TI_BELL en lugar de \xf0\x9f\x94\x94 (fuera del rango cargado por icon_font, renderizaba como ?)
- candlestick.cpp: SetupAxes/SetupAxisScale/SetupAxisLimits movidos dentro de BeginPlot/EndPlot — antes el plot desaparecia al entrar
- pie_chart.cpp: SetupLegend(East, Outside, NoButtons), eliminado NoLegend
- kpi_card.cpp: layout 2 cols con sparkline a la derecha centrado verticalmente
- docs/adr/0002-apps-analyses-as-dataforge-master.md: decision arquitectural
con contexto, alternativas descartadas y cambios concretos del 2026-04-28.
- CHANGELOG.md: entrada 2026-04-28 con Added/Changed/Fixed.
- .claude/CLAUDE.md: nota sobre /full-git-push y dataforge/<name>+master.
- .claude/rules/apps_tbd.md: tronco unico master + init.defaultBranch.
- cpp/functions/core/app_menubar.md: notas del submenu Settings con About.
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
cpp/core: nuevo modulo app_about — ventana About con project/version/desc,
componible via about_window_set_info() en el init de la app y rendererizada
automaticamente por fn::run_app al final de cada frame.
app_menubar: el item top-level "Settings..." pasa a ser un BeginMenu
"Settings" con dos subitems: "Settings..." (existente) y "About..." (nuevo).
bash/infra: nueva pipeline ensure_repo_synced que compone gitea_create_repo
y gitea_push_directory para garantizar repo Gitea existente + sync de un
directorio local en una sola llamada idempotente.
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
Para cada tile 4x4 px del rect de render: encontrar seed mas cercano
(distancia Euclidea) y rellenar con su color. Suficiente para N<=200
seeds en region <=600x400.
voronoi_layout deja polygon vacio en MVP — solo rellena seed/color.
Para extraer poligonos analiticos seria necesario half-plane
intersections (Fortune) — diferido a otro issue.
contour_compute implementa marching squares clasico (16 casos, casos
ambiguos 5 y 10 partidos en 2 segmentos). Para cada level devuelve un
ContourLine{pts, level} con segmentos en coords [0..nx-1]x[0..ny-1].
Verificado con gaussiana 32x32 + 4 niveles: todos los endpoints aparecen
>=2 veces (curvas cerradas, ningun endpoint huerfano).
Para una matriz NxN: cada nodo ocupa un arco proporcional a sum(row).
Las cuerdas matrix[i,j] son bandas bezier cubico hacia el centro
conectando los arcos de i y j.
Limitacion: las cuerdas se dibujan con AddConvexPolyFilled aunque la
forma no sea estrictamente convexa — visualmente queda razonable.
compute_levels asigna columnas via BFS, los nodos se apilan verticalmente
proporcional a max(in_total, out_total). Los links se renderizan como
bandas con bezier cubico, color del nodo origen + alpha bajo.
Asume DAG (sin ciclos). Si hay ciclos, los nodos del ciclo quedan en su
nivel parcial — no rompe pero puede solapar visualmente.
treemap_layout devuelve TreemapRect{min, max, item} con coords absolutas
dentro de la region. La suma de areas == area total (verificado via test
standalone, ratio=1.000000). El render usa AddRectFilled + AddText cuando
labels y valores caben dentro de la cell.
Limitaciones MVP: jerarquia plana (no recursivo), sin interaccion.
Genera cubo procedural in-line (mesh_obj_parse de string), permite
cargar .obj desde un text input absoluto. Botones: Reload cube,
Wireframe toggle, Load .obj. Status line con tris count y
instrucciones (drag to orbit, wheel to zoom).
issue 0029
Compila/cachea por id un programa GLSL (vertex+fragment) con
iluminacion Lambert (luz=camara), gestiona Framebuffer cacheado por
id, dibuja MeshGpu con orbit camera, muestra via ImGui::Image y
maneja drag (mouse) + wheel (zoom). Wireframe opcional via
glPolygonMode.
gl_loader: añade glUniformMatrix4fv (proc requerido en Windows para
subir las matrices view/proj del mesh_viewer).
issue 0029