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).
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.
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
mesh_obj_parse (pure) + mesh_obj_load (impure file helper).
Soporta v / vn / f (tris y quads). Genera normales per-face si
faltan (flat shading). Quads se parten en 2 tris; n-gons (>4) se
descartan silenciosamente. Indices 1-based positivos y negativos.
issue 0029
Timeline widget with:
- Header: play/pause + reset + duration drag + loop checkbox
- Ruler: 0.5s ticks, scrub via click+drag
- Tracks: horizontal rows with diamond-shaped draggable keyframes
- Playhead: vertical primary_light line + ruler triangle marker
State and types:
- Keyframe { time, value, ease }
- Track { name, vector<Keyframe> }
- TimelineState { tracks, current_time, duration, playing, loop }
Pure functions:
- track_value_at(track, t): interp between keys, ease applied via the
destination keyframe (Maya/AfterEffects convention)
- timeline_update(state, dt): advance current_time, wrap or saturate
Render with fn_tokens for visual coherence with the rest of the design
system. Keys are sorted by time on every changed frame to keep order
consistent during drag.
ImGui canvas with 4 draggable control points (p0/p3 locked at (0,0)/(1,1)
by default for use as easing curves). Pure evaluation via De Casteljau
(bezier_point) plus sampling-based y(x) lookup (bezier_eval).
Render uses fn_tokens for visual coherence: bg, border, primary curve,
text_dim diagonal reference, text_muted handle tangents.
p1.x and p2.x clamped to [0,1] to keep the curve usable as easing; Y
values are free to allow deliberate overshoot.
16 easing curves (linear, quad, cubic, expo, elastic, bounce in/out/inOut)
header-mostly so the compiler inlines on hot paths. Pure, no I/O, no state.
Includes:
- tween::apply(Ease, t) dispatcher for data-driven uses (timeline keyframe.ease,
dropdown selection)
- tween::name(Ease) for UI labels
- tween::ease_count for iteration
Tested values documented in tween_curves.md.
surface_plot_3d (v2.0.0): quita el STUB. API basada en
SurfacePlot3DConfig (z[nx*ny] row-major + ranges X/Y) que delega en
ImPlot3D::PlotSurface. Las coordenadas X/Y por vertice se generan
internamente desde [x_min, x_max] x [y_min, y_max].
scatter_3d (v1.0.0): nuevo primitivo. Scatter 3D con tamano y color
opcionales por punto via ImPlot3DSpec::MarkerSizes / MarkerFillColors.
Util para PCA / clustering / nubes de puntos sinteticas.
Ambos namespace fn::, kind component, purity pure. Orbit / zoom / pan
los aporta ImPlot3D nativo.
Issue 0028.
Dos primitivas del pipeline de shaders_lab que ya estaban en uso pero sin
indexar al registry:
- code_to_generator_cpp_gfx (function, pure)
Traduce un fragment shader GLSL escrito a mano (modo Code, con void main()
+ fragColor = ...) en un body de DAG Gen + DagControl[]. Cada uniform
anotado se convierte en un control; el body usa el parametro uv y reemplaza
fragColor= por return. Empaqueta uniforms en vec4 (4 x n_uniforms).
- shaderlab_db_cpp_gfx (function, impure)
CRUD persistente para generators custom de shaders_lab via sqlite3.
Guarda el GLSL original, el body traducido para el DAG, los DagControl y
los param_defaults en una BD local (shaders_lab.db). Soporta open(:memory:)
para tests.
Ambas se indexan ahora en registry.db y son reusables fuera de shaders_lab
si en el futuro hay otra app que componga DAGs de shaders.
5 primitivas que componen el chrome de una app fn_ui completa:
- app_settings_cpp_core (function, impure) ventana flotante de
settings globales (Display + Typography + secciones extra registrables) con
persistencia automatica en app_settings.ini junto al ejecutable.
- app_menubar_cpp_core (component, pure) MainMenuBar unificada
con menu View (toggles de paneles) y Layouts. Punto de entrada de la
menubar de cualquier app fn_ui.
- layouts_menu_cpp_core (component, pure) menu para guardar/aplicar/
borrar/reset layouts de ImGui via callbacks (no I/O propio).
- panel_menu_cpp_core (component, pure) menu checkable para
abrir/cerrar paneles, composable dentro de la MainMenuBar.
- layout_storage_sqlite_cpp_core (function, impure) primitivas CRUD de bajo
nivel para persistir blobs INI de ImGui en sqlite (ui_layouts table).
Permiten que apps como shaders_lab y registry_dashboard ofrezcan: panel
toggles, layouts persistentes en BD, ventana de settings con preview en
vivo. Todas usan tokens_cpp_core para el styling.
Anade 9 primitivas reutilizables al registry C++ que replican el comportamiento
de los componentes correspondientes de @fn_library / Mantine v9, todas
estilizadas con tokens_cpp_core (colores Mantine dark + indigo):
- button_cpp_core (component, pure) variantes primary/secondary/subtle/danger + sm/md/lg
- icon_button_cpp_core (component, pure) cuadrado 28x28 con glyph centrado + tooltip
- toolbar_cpp_core (component, pure) grupo horizontal de acciones con separadores
- modal_dialog_cpp_core (component, pure) popup modal centrada + close con Escape
- text_input_cpp_core (component, impure) InputText con label muted + placeholder
- select_cpp_core (component, impure) dropdown con label + opcion '(none)' opcional
- toast_cpp_core (component, impure) notificaciones efimeras + inbox con badge
- tree_view_cpp_core (component, impure) jerarquia low-level con tree_node_clicked helper
- process_runner_cpp_core (component, impure) tarea en std::thread + spinner inline
Cada primitiva tiene su .md con frontmatter completo (params/output) y se
indexa via fn index. Son la base del primitives_gallery y de cualquier
app fn_ui futura.
icon_font_cpp_core (impure): carga Karla-Regular como texto vectorial y
mergea Tabler Icons al mismo tamano en el atlas de ImGui. Tras el call,
los TI_* renderizan inline con el texto.
icons_tabler.h: header con macros TI_<NAME> que apuntan a los codepoints
del set Tabler (UTF-8 escapado). Generado a partir del CSS del vendor con
cpp/vendor/tabler-icons/gen_header.py — re-ejecutable si se actualiza el
set sin tocar a mano los ~5500 codepoints.
Justifica la regla cpp_icons.md: todas las apps C++ usan TI_* en lugar de
emoji o hex inline.
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>
Watcher de archivos no bloqueante con backend nativo por plataforma:
- Linux: inotify_init1(IN_NONBLOCK | IN_CLOEXEC), inotify_add_watch con
mascara MODIFY|CREATE|DELETE|CLOSE_WRITE|MOVED_*. Drain en cada poll().
- Windows: ReadDirectoryChangesW overlapped + GetOverlappedResult no
bloqueante. Para vigilar un archivo, registra el directorio padre y filtra
por nombre exacto en el poll().
- Otros: stub — poll() devuelve vacio y last_error() reporta no soportado.
API en namespace fn:: con tipos opacos (FileWatcher PIMPL). Errores via
last_error() en vez de excepciones. Documentadas las limitaciones (limite de
inotify watches en Linux, granularidad directorio-level en Windows, no
recursividad).
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
Wrapper en namespace fn:: sobre ImGuiColorTextEdit. La API publica solo expone
TextEditorState como tipo opaco; el TextEditor del vendor vive dentro del .cpp.
Soporta CodeLang::{Generic, GLSL, SQL, Cpp} (highlighting via las
LanguageDefinition del vendor). text_editor_render() devuelve true en el frame
en que el contenido cambia; flag dirty manejado independientemente para casos
"editado pero aun no guardado".
text_editor_get_text() cachea el resultado en un std::string del state para
mantener el const char* valido entre llamadas (el GetText() del vendor devuelve
std::string por valor).
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
Funcion impura del registry que carga PNG/JPG/BMP/TGA/HDR a una textura
OpenGL lista para sampler2D. Composable con gl_loader, gl_shader,
shader_canvas. Genera mipmaps, soporta sRGB y HDR (RGBA16F).
API:
GlTexture gl_texture_load(path, flip_y=true, srgb=false)
GlTexture gl_texture_load_from_memory(data, size, ...)
void gl_texture_destroy(tex)
const char* gl_texture_last_error() // thread-local
void gl_texture_bind_uniform(prog, name, tex, unit)
Errores via thread_local string accesible por gl_texture_last_error().
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
Necesarios para que gl_texture_load (cpp/functions/gfx/) funcione en
Windows tras wglGetProcAddress. En Linux son simbolos directos via
GL_GLEXT_PROTOTYPES, no afecta.
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
- DagStep: preview_open flag (default false).
- dag_compile: emit `uniform int u_preview_target` and a series of
early-return branches at the start of fragColor selection. -1 (default)
falls through to the real Output-driven fragColor.
- dag_node_previews (new fn): per-node FBO keyed by editor_uid, lazy
created. Renders each node with preview_open=true to its FBO by
setting u_preview_target = step index. Texture exposed via
dag_preview_texture(uid) for ImGui::Image.
- dag_node_editor: small toggle button "[+] preview"/"[-] preview" in
each non-Output node; when open, ImGui::Image(96x64, V-flipped).
- dag_node_editor: double right-click on hovered node deletes it
(Output is protected).
- main.cpp: dag_previews_render after Canvas DAG; dag_previews_destroy
on shutdown.
Single GL program drives both the canvas and all thumbnails — moving
sliders never recompiles, only the topology change does.
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
draw_pin_circle takes a PinSide and centers the circle exactly on the
left or right edge of the node. The reserved Dummy is half-width
(PIN_RADIUS instead of PIN_DIAMETER) so the inside layout stays
compact, and ed::PinRect is set to the full circle so the protruding
half is still grabbable.
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
- Pin radius bumped to 9px (18px diameter). Easier to grab.
- Single neutral pin/cable color across all kinds (data is uniformly
vec4 — coloring by node kind was misleading).
- ed::StyleVar_NodePadding(0,8,0,8): zero left/right padding so the pin
circles sit flush with the node's edges, separated from the title
and controls by 8px gaps.
- Right-click on a pin clears all connections on that pin:
- input pin → clear that single slot's source_id
- output pin → clear every source_id across the pipeline pointing
to this node (fan-out).
- Right-click on a link still deletes that single link (existing).
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
- Three-column node layout: input pins on the left edge, controls in
the middle, output pin on the right edge.
- Pins rendered as 14px filled circles with darker outline. Color is
the node's kind (gen=blue, op=violet, blend=amber, output=red).
- ed::PinPivotAlignment(0,0.5)/(1,0.5) so the cable starts/ends at
the circle center.
- Empty columns (gen with no inputs, output with no output) get a
pin-sized Dummy so column widths stay consistent.
- ed::Link now passes color (= source node kind) and 2.5px thickness.
- Right-click on a link deletes it immediately via
ed::ShowLinkContextMenu (no popup).
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
Previously the cycle validator rejected any link whose source had a
vector index >= target's, which silently killed legitimate connections
between nodes added in the wrong drop order.
Switch to a DFS over source_ids: an edge from->to creates a cycle iff
`from` already (transitively) depends on `to`. topo_sort runs after
each topology change so the vector ends up in a consistent order
regardless of how nodes were inserted.
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
Two bugs:
1. Dropped nodes were pushed to the end of the pipeline, but the Output
node already sat there from startup. The cycle validator and the
compiler only look for sources at indices strictly lower than the
target, so new nodes were invisible to the Output. Fix: insert
dropped nodes before the first Output; topo_sort also stable-moves
Output nodes to the back.
2. ColorEdit3 with default flags rendered RGB text inputs alongside the
swatch; clicking them dragged the node instead of opening the picker.
Fix: NoInputs + NoLabel leaves only the swatch (a single item), and
ed::Suspend/Resume wraps the call so the popup isn't clipped to the
node or captured by the canvas.
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>