- .claude/CLAUDE.md - .claude/agents/fn-recopilador/SKILL.md - .claude/rules/INDEX.md - .claude/rules/cpp_apps.md - bash/functions/infra/build_cpp_windows.sh - cpp/CMakeLists.txt - cpp/PATTERNS.md - cpp/framework/app_base.cpp - cpp/framework/app_base.h - dev/issues/README.md - ... Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
9.2 KiB
Estandarizacion de apps C++ del registry
Fuentes autoritativas:
cpp/PATTERNS.md— checklist y esqueleto del app shell (fn::run_app, AppConfig, panels, layouts, Settings, About).cpp/DESIGN_SYSTEM.md— identidad visual (fn_tokens, ThemeMode, equivalencias@fn_library↔ C++).
Esta regla NO duplica esos documentos — los señala como obligatorios y añade convenciones estructurales que no aparecen alli.
1. Ubicacion
| Caso | Donde vive |
|---|---|
| App independiente | cpp/apps/<nombre>/ |
| App de un proyecto | projects/<proyecto>/apps/<nombre>/ |
NUNCA en cpp/apps/<nombre>/ si pertenece a un proyecto, NUNCA fuera de apps/ directamente. Ver apps_location en memoria + regla apps_vs_functions.md.
2. Estructura minima
<app_dir>/
CMakeLists.txt # usa add_imgui_app(target ...)
app.md # frontmatter de registro (ver §4)
main.cpp # entry: parseo de args + fn::run_app + render()
[data.{h,cpp}] # opcional: capa de datos (DB / HTTP / archivos)
[views.{h,cpp}] # opcional: composicion de paneles
[<modulo>.{h,cpp}] # opcional: dominio especifico
[vendor/] # opcional: deps no comunes (se prefieren las globales en cpp/vendor/)
[.git/] # cada app es su propio repo Gitea (ver §6)
Reglas de split:
main.cppSIEMPRE — punto de entrada conint main()+fn::run_app(...)+ funcionrender().- Si la app supera ~400 lineas en
main.cpp, partir endata.{h,cpp}(carga/persistencia) +views.{h,cpp}(UI por panel). - Modulos especificos del dominio en archivos propios (
compiler.cppenshaders_lab,data_http.cppenregistry_dashboard). - NO crear archivos de "utilidades genericas" dentro de la app — eso va al registry como funcion (
cpp/functions/...).
3. CMakeLists.txt
Patron canonico:
add_imgui_app(<target>
main.cpp
[extra_modules.cpp]
# Funciones del registry usadas (paths absolutos):
${CMAKE_SOURCE_DIR}/functions/<dominio>/<funcion>.cpp
...
)
target_include_directories(<target> PRIVATE ${CMAKE_CURRENT_SOURCE_DIR})
target_link_libraries(<target> PRIVATE [SQLite::SQLite3] [imgui_node_editor] ...)
if(WIN32)
set_target_properties(<target> PROPERTIES WIN32_EXECUTABLE TRUE)
endif()
Reglas:
- Usar SIEMPRE la macro
add_imgui_app(target ...)— gestiona enlace confn_frameworky copia de TTFs. - Listar explicitamente cada
.cppdel registry usado (no glob). Hace visible el grafo de dependencias. - NO listar
tokens.cpp,icon_font.cpp,app_settings.cpp,app_about.cpp,fps_overlay.cpp,panel_menu.cpp,app_menubar.cpp,layouts_menu.cpp,gl_loader.cpp,layout_storage.cpp— viven enfn_frameworky dan multiple-definition si se duplican. - En
WIN32, marcarWIN32_EXECUTABLE TRUEpara apps GUI (sin consola).
4. app.md (frontmatter)
Plantilla minima para apps C++:
---
name: <name>
lang: cpp
domain: <gfx|tui|tools|infra|...>
description: "Frase corta — lo que hace y por que existe."
tags: [imgui, ...] # si es service, anadir 'service'
uses_functions: # IDs del registry — el indexer NO deduce C++
- <nombre>_cpp_<dominio>
- ...
uses_types: []
framework: "imgui"
entry_point: "main.cpp"
dir_path: "cpp/apps/<name>" o "projects/<proyecto>/apps/<name>"
repo_url: "https://gitea-.../dataforge/<name>"
---
Reglas:
uses_functionsse rellena a mano con los IDs de las funciones del registry usadas enCMakeLists.txt. Auditar con:sqlite3 registry.db "SELECT id FROM apps WHERE id='<id>';"+ revisar diffs.framework: "imgui"siempre que usefn::run_app. Otros valores solo si la app NO usa el shell (raro).tags: incluirservicesi es daemon de larga duracion (verfunction_tags.md).repo_urlapunta al sub-repo en Gitea (ver §6).
5. Registro en cpp/CMakeLists.txt
Cada app nueva se registra al final de cpp/CMakeLists.txt:
# --- <app_name> ---
if(EXISTS ${CMAKE_CURRENT_SOURCE_DIR}/apps/<name>/CMakeLists.txt)
add_subdirectory(apps/<name>)
endif()
Para apps en proyectos (fuera del arbol cpp/):
# --- <app_name> (lives in projects/<proj>/apps/) ---
set(_<NAME>_DIR ${CMAKE_SOURCE_DIR}/../projects/<proj>/apps/<name>)
if(EXISTS ${_<NAME>_DIR}/CMakeLists.txt)
add_subdirectory(${_<NAME>_DIR} ${CMAKE_BINARY_DIR}/apps/<name>)
endif()
El if(EXISTS ...) hace el registro tolerante a apps no clonadas (cada app es sub-repo separado).
6. Sub-repo Gitea (TBD obligatorio)
Cada app C++ es su propio repo en dataforge/<name> con branch master. Esto significa:
- El directorio
<app_dir>/esta en el.gitignoredefn_registry(exceptoapp.md). - El propio directorio tiene
.git/apuntando al sub-repo. - TBD obligatorio mientras se desarrolla la app: ver
apps_tbd.md. Trabajar enissue/<NNNN>-<slug>oquick/<slug>, mergear amastercon--no-ff. - Sync entre PCs y push/pull se gestionan con
/full-git-pushy/full-git-pull.
7. Convencion local_files/ — separacion de distribuible vs estado local
OBLIGATORIO: TODA app coloca sus archivos escribibles bajo
<exe_dir>/local_files/. Los archivos distribuibles (.exe, .dll,
.ttf, enrichers/, runtime/) viven directos en <exe_dir>/.
<exe_dir>/
├── <app>.exe
├── duckdb.dll, *.ttf, runtime/, enrichers/ ← read-only, ships con el zip
└── local_files/ ← writable, per-PC
├── imgui.ini ← gestionado por fn::run_app
├── app_settings.ini ← gestionado por fn_ui::settings_*
└── <lo que la app escriba> ← usar fn::local_path("nombre")
fn::run_app lo gestiona automaticamente para imgui.ini y
app_settings.ini y migra desde <exe_dir>/ o cwd si vienen de
una version previa.
Apps que escriban archivos extra (DBs, caches, proyectos del
usuario) DEBEN usar fn::local_path("nombre") al construir
sus paths. Ejemplo:
// MAL
sqlite3_open("graph_explorer.db", &db);
fopen("graph_explorer.ini", "r");
// BIEN
sqlite3_open(fn::local_path("graph_explorer.db"), &db);
fopen(fn::local_path("graph_explorer.ini"), "r");
API en cpp/framework/app_base.h:
fn::exe_dir()— directorio del ejecutable.fn::local_dir()—<exe_dir>/local_files/, creado on-demand.fn::local_path(name)—<local_dir>/<name>.fn::migrate_to_local_files(names, n)— mueve archivos viejos.
Beneficios:
- Carpeta del .exe limpia para distribuir (zip portable).
- Reset trivial (basta borrar
local_files/). - Separacion clara para backup/sync (solo
local_files/es propio del PC).
8. Convenciones de runtime
Cumplir el checklist completo de cpp/PATTERNS.md. Resumen de lo que NUNCA debe aparecer en una app:
| Anti-patron | Sustituir por |
|---|---|
glfwInit() en main |
fn::run_app(cfg, render) |
ImGui::StyleColorsDark() |
cfg.theme = ThemeMode::FnDark (default) |
ImVec4(0.5,0.5,0.5,1) |
fn_tokens::colors::* |
ImGui::Begin(u8"\xEF...") |
ImGui::Begin(TI_HOME " ...") |
| Menubar inline cada frame | cfg.panels + cfg.layouts_cb |
| About hardcoded en un panel | cfg.about = {...} |
gl* directo sin loader |
cfg.init_gl_loader = true |
| Tabla SQLite en la raiz del repo | <app_dir>/<app>.db (operations.db es solo para entities/relations/executions) |
fopen("foo.ini", ...) con path relativo |
fopen(fn::local_path("foo.ini"), ...) (ver §7) |
8. Tests visuales (recomendado, no obligatorio)
Si la app tiene componentes que se quieren proteger contra regresiones visuales, anadir un demo en cpp/apps/primitives_gallery/demos_<dominio>.cpp que use los mismos componentes/funciones del registry. El sistema de capture-and-compare de primitives_gallery --capture funciona como golden-image gate (ver final de cpp/PATTERNS.md).
9. Decisiones que cada app debe tomar y documentar en su app.md
viewports:true(default) si las ventanas pueden arrastrarse fuera del main;falsesi la app necesita estar siempre embebida.init_gl_loader:truesi llamagl*directo (renderers GPU custom comograph_renderer);falsesi solo usa ImGui/ImPlot.aboutinfo: nombre, version (semver), descripcion 1 frase.- Persistencia:
<app>.dbSQLite junto al exe; nunca tocarregistry.dbnioperations.dbsalvo lectura. - Modo CLI: si la app acepta args, documentarlos en el
app.mdcon ejemplos.
10. Layouts persistentes (default)
fn::run_app provee menu Layouts (Save current as.../Apply/Delete/Reset) sin
codigo. Crea <exe_dir>/local_files/layouts.db (tabla imgui_layouts) y
persiste el imgui.ini serializado por nombre.
- App nueva: nada que tocar — Layouts viene activo.
- App quiere personalizar
on_reset(ej. re-mostrar paneles especificos comoshaders_lab): abre su propioLayoutStorage, llamalayout_storage_make_callbacks, overrideon_reset, y pasacfg.layouts_cb = &cb. Cuando se pasalayouts_cb, el auto-storage se desactiva y la app es responsable delayout_storage_apply_pendingal inicio de surender. - App headless / capture mode:
cfg.auto_layouts = false. - Cambiar nombre del archivo:
cfg.auto_layouts_db = "<algo>.db"(relativo alocal_files/).