- 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.
- 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
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
Adds three new demos to the Core section of primitives_gallery:
- demo_tween: dropdown of all 16 Ease modes + animated plot showing the
curve and a moving marker that traverses t=0..1 in a loop.
- demo_bezier_editor: live editor with reset + ease-out / ease-in-out
presets, current control points displayed, slider over t showing
bezier_eval(curve, t).
- demo_timeline: 2 tracks (hue, amp) with mixed eases, live progress bars
showing track_value_at(current_time) updating each frame.
Wires the three demos into k_demos[] in main.cpp and adds the new sources
(plus the three function .cpp files) to CMakeLists.txt.
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.
Demo nuevo en demos_sql.cpp: abre registry.db en SQLITE_OPEN_READONLY
(resolviendo via FN_REGISTRY_ROOT o cwd ascendente), monta
fn::SqlWorkbenchState con readonly=true y query inicial sobre la tabla
functions. Wire-up: entry en k_demos[] tras process_runner; declaracion
en demos.h; sources sql_workbench.cpp + demos_sql.cpp + link
SQLite::SQLite3 en CMakeLists.txt.
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
- demos_3d.cpp con dos demos:
* demo_surface_plot_3d: malla 64x64 de A*sin(fx*x)*cos(fy*y) con
sliders fx/fy/amp en tiempo real.
* demo_scatter_3d: 3 clusters gaussianos (N=500) coloreados por
cluster, semilla fija para reproducibilidad.
- demos.h: declara las dos demos en la seccion Viz.
- main.cpp: dos entradas nuevas en k_demos[] (Viz, tras heatmap /
table_view).
- CMakeLists.txt: anade demos_3d.cpp + surface_plot_3d.cpp +
scatter_3d.cpp al target.
Issue 0028.
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.
ImPlot3D::CreateContext() / DestroyContext() acoplado al ciclo de
vida de ImPlot. Cualquier app que use fn::run_app obtiene el contexto
3D listo sin codigo extra.
Issue 0028.
Cobertura del catalogo visual:
- text_editor (Wave 1, 0025): demo solo del editor con dropdown GLSL/SQL/Cpp/Generic.
- file_watcher (Wave 1, 0025): demo solo del watcher con boton touch + log.
- gl_texture_load (Wave 1, 0026): ya tenia demo (Gfx).
- process_runner: tarea simulada en background con spinner.
- candlestick: 30 dias OHLC sintetico.
- gauge: 3 indicadores con sliders (CPU/MEM/GPU).
- heatmap: gaussiana 12x12.
- table_view: 6 funciones del registry como muestra.
El demo combinado anterior (text_editor + watcher) se separa en dos para
que cada entry del sidebar exhiba un solo primitivo. Mas claro y mas
indexable.
Total entries en gallery: 26 (antes 23).
Tras commitear los deps (button, toolbar, modal_dialog, app_settings, etc.)
todos los sources del gallery estan tracked. Quita el gate FN_BUILD_GALLERY
y deja el subdirectorio como add_subdirectory incondicional (igual que
chart_demo y shaders_lab).
Build verificado: cmake --build cpp/build construye chart_demo, shaders_lab,
text_editor_smoke, primitives_gallery y registry_dashboard.
Documenta el sistema de design tokens del registry C++:
- Identidad visual unica (Mantine v9 dark + indigo) compartida entre apps
- Como se aplican los tokens via fn::run_app
- Convenciones de uso (cuando usar tokens.colors.surface vs accent, etc.)
Es la fuente de verdad cuando se crean nuevas primitivas o apps fn_ui.
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.
Vendorea el set de iconos Tabler (~5500 iconos, MIT) para usar en apps
ImGui via icon_font_cpp_core. Incluye:
- tabler-icons.ttf — atlas TTF para mergear en ImGui
- tabler-icons.css — referencia de codepoints (no se compila)
- gen_header.py — script que regenera cpp/functions/core/icons_tabler.h
con macros TI_<NAME> a partir del CSS
- LICENSE / README.md — atribucion MIT
Versionar el TTF (~700 KB) evita depender de descargas en build. Las apps
usan TI_* en strings y el atlas se carga con icon_font_cpp_core::load().
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>
Tras integrar 0025 (que trajo la app entera) y 0026 (que solo añadio
demos_gl_texture.cpp + assets/sample.png + cpp/functions/gfx/gl_texture_load.*),
falta wire-up de la entrada nueva en los archivos compartidos del gallery.
- demos.h: declarar gallery::demo_gl_texture()
- main.cpp: entrada en k_demos[] con id "gl_texture", category "Gfx"
- CMakeLists.txt: añadir demos_gl_texture.cpp, gl_texture_load.cpp,
stb_image_impl.cpp, e include dir vendor/stb
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
- demos_text_editor.cpp: split horizontal con editor GLSL precargado a la
izquierda (boton Save to /tmp/fn_demo.glsl + dirty indicator) y panel de
eventos a la derecha (path, active flag, lista scrollable, boton clear).
Watcher activo sobre /tmp/fn_demo.glsl; reintenta el add() tras el primer
Save si el archivo no existia al iniciar.
- demos.h: declaracion de gallery::demo_text_editor()
- main.cpp: entry "text_editor"/"text_editor + watcher" en categoria Core
- CMakeLists.txt: anade demos_text_editor.cpp + sources de text_editor,
file_watcher y vendor TextEditor.cpp + include path de imgui_text_edit
Nota: la primitives_gallery NO se construye en este branch (sus deps —
button.cpp, toolbar.cpp, etc. — son untracked en master). El subdirectorio
se anade pero protegido por FN_BUILD_GALLERY=OFF para no romper builds.
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
App minima (no abre ventana ImGui) que crea/settea/lee text_editor y registra
un watch sobre /tmp/fn_smoke_test.txt para confirmar que TextEditor.cpp del
vendor + text_editor.cpp + file_watcher.cpp enlazan correctamente. Activada
por defecto si la carpeta existe (no requiere la primitives_gallery).
Tambien anade flag FN_BUILD_GALLERY (OFF default) para no romper el build
cuando la primitives_gallery no esta presente — sus deps (button.cpp,
toolbar.cpp...) son sources untracked en algunas branches.
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>
Pinneado al commit 0a88824f del upstream. Patches locales aplicados a
TextEditor.cpp para compilar contra ImGui 1.91+ (GetKeyIndex eliminado,
PushAllowKeyboardFocus/PopAllowKeyboardFocus removidos). Documentado en
cpp/vendor/imgui_text_edit/README.md.
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
Añade demos_gl_texture.cpp (carga assets/sample.png con la nueva
funcion gl_texture_load_cpp_gfx, muestra w/h/channels y sliders de
tint RGB + zoom UV via ImGui::ImageWithBg) y un PNG damero 256x256
generado para la demo.
Tambien registra apps/primitives_gallery como subdirectorio en
cpp/CMakeLists.txt (la app vive como WIP en master; aqui solo se
añade el hook de build).
Para integrarse, demos.h, main.cpp y CMakeLists.txt de la gallery
deben anadir respectivamente la declaracion gallery::demo_gl_texture(),
la entrada {"gl_texture", "gl_texture_load", "Gfx", &gallery::demo_gl_texture}
en k_demos[], y demos_gl_texture.cpp + cpp/functions/gfx/gl_texture_load.cpp +
cpp/vendor/stb/stb_image_impl.cpp + include de cpp/vendor/stb a sus sources.
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>
Header-only image loader (public domain). Pinneado al commit
f0569113c93ad095470c54bf34a17b36646bbbb5 de nothings/stb.
Layout:
- stb_image.h — header (no modificar)
- stb_image_impl.cpp — UNICO TU que define STB_IMAGE_IMPLEMENTATION
- README.md — origen + commit + uso
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>