--- id: "57" title: "Extraer paneles ImGui reutilizables de las apps C++ a cpp/functions/ — roadmap" status: pendiente type: epic domain: - cpp-stack - registry-quality scope: cross-stack priority: media depends: [] blocks: [] related: [] created: 2026-05-09 updated: 2026-05-17 tags: [] --- ## Contexto Las apps C++ del registry (`cpp/apps/*`, `projects/*/apps/*`) llevan acumulando paneles ImGui que comparten patron pero viven dentro de cada app. Hay duplicacion observable y mas que va a aparecer (navegator_dashboard quiere chat panel igual que graph_explorer; el dashboard de Tabs va a parecerse al panel "Browsers"). Hoy ya estan en el registro `cpp/functions/core/`: `dashboard_panel`, `fullscreen_window`, `log_window`, `modal_dialog`, `tree_view`, `selectable_text`. La capa visual (tokens, theming, iconos, settings/about/menubar) vive en `fn_framework`. Esto cubre primitivas. Falta una tier intermedia: **paneles compuestos** (mas grandes que un widget, mas pequeños que una app). Este issue documenta el roadmap de que extraer, cuando, y por que. ## Principios 1. **Rule of three** — no extraer hasta que haya 2-3 consumidores reales. Pre-extraer crea abstracciones malas. 2. **Consumidor primero** — escribir el panel inline en la app. Si una segunda app lo necesita, copiar+adaptar. Si una tercera lo pide, extraer. 3. **Parametrizable, no monolitico** — los paneles extraidos reciben un struct de config (callbacks, paths, env vars) en vez de hardcodear nombres. 4. **No exponer estado global del modulo extraido** — usar contexto opaco que cada app crea/destruye. ## Catalogo: paneles candidatos detectados Revisado todas las apps C++ vivas. Por cada panel: lineas, consumidores actuales, consumidores potenciales, decision. ### Tier 0 — ya extraidos ✓ | Panel | Ubicacion | |---|---| | dashboard cards grid | `cpp/functions/core/dashboard_panel.{h,cpp}` | | fullscreen window wrapper | `cpp/functions/core/fullscreen_window.{h,cpp}` | | log window con buffer | `cpp/functions/core/log_window.{h,cpp}` | | modal dialog | `cpp/functions/core/modal_dialog.{h,cpp}` | | tree view | `cpp/functions/core/tree_view.{h,cpp}` | | selectable text | `cpp/functions/core/selectable_text.{h,cpp}` | Estos definen el "lower-tier" — primitivas. Los nuevos extracts componen sobre estos. ### Tier 1 — extraer cuando aparezca el 3er consumidor #### 1.1 `claude_chat_panel` (origen: graph_explorer/chat.cpp, 1241 LoC) **Que es:** panel ImGui que arranca un subprocess `claude -p`, le envia mensajes del usuario, parsea stream-json, renderiza historial con tool-use detectado, expone callbacks para que la app recargue cuando el agente muta su BD. **Consumidores hoy:** graph_explorer. **Consumidores potenciales:** - navegator_dashboard (issue navegator/0001) — chat para controlar Chrome. - kanban (futuro) — agente para crear/mover cards. - registry_dashboard — agente para crear functions/proposals. **Por que tier 1 (no tier 0):** - 59 referencias a `gx-cli`, `GX_OPS_DB`, `GX_APP_DB`, `agent_mutations`. Acoplamiento alto a graph_explorer. - Refactor a config parametrizado es ~4-6h y modifica codigo que YA funciona — riesgo regresion. - Con solo 2 consumidores no hay claridad de que parametrizar. **Cuando extraer:** 3er consumidor pide chat. Hasta entonces: copiar+adaptar (graph_explorer mantiene el suyo, navegator copia el suyo). **API propuesta cuando se extraiga:** ```cpp struct ClaudeChatConfig { const char* claude_bin; // "claude" o WSL path const char* cli_tool_name; // "gx-cli", "cdp-cli", "kanban-cli", ... const char* cli_tool_dir; std::map env_vars; // GX_*, NAVD_*, ... const char* mcp_config_path; // o func que lo genera on-demand const char* system_prompt; // opcional std::function mutations_counter; // app-side reload trigger const char* log_file_path; }; namespace fn_ui { struct ChatHandle; // opaco ChatHandle* chat_create(const ClaudeChatConfig& cfg); void chat_send(ChatHandle*, const char* user_text); void chat_render(ChatHandle*, bool* p_open); void chat_destroy(ChatHandle*); } ``` #### 1.2 `jobs_queue_panel` (origen: graph_explorer/jobs.cpp + odr_console/jobs) **Que es:** panel + sistema de jobs asincronos. Lista jobs en cola/running/done con barra de progreso, stdout/stderr capture, cancelable, persistente entre sesiones. **Consumidores hoy:** graph_explorer (jobs.cpp + views_jobs.cpp), odr_console (panel Jobs). **Consumidores potenciales:** - navegator_dashboard — scraping jobs (un crawl es un job). - registry_dashboard — fn index, fn run jobs. **Por que tier 1:** ya hay 2 consumidores, parece duplicacion clara. **Inspeccionar diff entre ambos antes de extraer** — pueden estar implementados distinto y no reusarse del todo. **Cuando extraer:** despues de auditar que graph_explorer/jobs.cpp y odr_console/jobs comparten >70% del codigo. Si si: 3er consumidor (navegator scraping) detona la extraccion. **API propuesta:** ```cpp namespace fn_ui { struct Job { std::string id; std::string title; std::function run; // retorna exit code // ... }; void jobs_panel_init(const char* persistence_db_path); void jobs_panel_submit(const Job&); void jobs_panel_render(bool* p_open); } ``` ### Tier 2 — extraer cuando aparezca el 2do consumidor (todavia no detectado) #### 2.1 `project_switcher_panel` (origen: graph_explorer/project_manager.cpp) **Que es:** sidebar/menu para cambiar entre subcarpetas de proyectos (cada proyecto tiene su DB y settings). Persiste el ultimo proyecto abierto. **Hoy en:** graph_explorer. **Potencial:** navegator_dashboard (perfiles browser como proyectos), registry_dashboard (apps como proyectos), kanban (boards). **Cuando extraer:** cuando navegator_dashboard quiera multi-perfil persistente con settings por perfil. v3 del dashboard probablemente. #### 2.2 `key_value_inspector_panel` (origen: graph_explorer/views.cpp `Inspector`) **Que es:** panel "Inspector" generico que muestra propiedades de un objeto seleccionado en formato key:value, con edicion inline opcional. Hoy es ad-hoc para entities. **Potencial:** navegator_dashboard (Tab Detail mostrar propiedades de pestaña), registry_dashboard (function detail), kanban (card details). **Cuando extraer:** cuando dos paneles de "ver propiedades de cosa seleccionada" tengan codigo casi identico. #### 2.3 `paste_and_extract_panel` (origen: graph_explorer/extract_panel.cpp) **Que es:** panel que recibe texto pegado, invoca enricher Python, muestra entities extraidas + boton para insertarlas. **Potencial:** navegator_dashboard (extraer texto seleccionado de pestaña), registry_dashboard (extraer descripcion para auto-fill function metadata). **Cuando extraer:** 2do consumidor con flujo similar. #### 2.4 `http_request_inspector_panel` (origen: registry_dashboard/http_client.cpp + futuro navegator_dashboard/network) **Que es:** panel para ver una peticion HTTP (request line, headers, body) renderizado bonito. Read-only. **Potencial:** navegator_dashboard (Network panel renderiza HAR entries), registry_dashboard (mostrar requests outgoing del fn_registry_api). **Cuando extraer:** cuando 0070b (HAR record) sea consumido por algo que quiera renderizarlo bien. ### Tier 3 — NO extraer (especificos de su app) | Panel | Por que NO | |---|---| | graph_explorer/types_registry | Acoplado a `types.yaml` schema especifico. | | graph_explorer/node_groups | DuckDB + grafo + tipo "Table" del schema graph_explorer. | | graph_explorer/layout_store | Posiciones de nodos del grafo, schema especifico. | | graph_explorer/views.cpp::Legend | Mapa de tipos→colores especifico del grafo. | | graph_explorer/views.cpp::Stats | Conteos por tipo de entity, schema especifico. | | navegator_dashboard/browsers panel | Detector chrome.exe Win32, hereda WMI/CIM, especifico Windows. | | odr_console/datasets panel | Listado especifico del flujo ODR. | Estos viven en su app. Si emerge un patron despues, se reconsidera. ### Tier 4 — primitivas que sirven a TODOS los paneles (extraccion oportunista, sin app-driver) Util tener antes de los Tier 1-2 para hacer la extraccion mas limpia: | Primitiva | Para que | |---|---| | `subprocess_streamer.{h,cpp}` | Encapsula spawn + pipes + read-thread + queue. Lo usaria chat_panel + jobs_panel. | | `mcp_config_writer.{h,cpp}` | Genera `.mcp.json` para Claude. Lo usaria chat_panel. | | `app_db_init.{h,cpp}` | Patron generico de "abrir SQLite junto al exe + aplicar migrations embed.FS". Hoy cada app reimplementa. | | `key_input_history.{h,cpp}` | Input + flecha arriba/abajo recupera entradas previas. Util en chat + REPL evaluate. | | `json_log_renderer.{h,cpp}` | Render de stream JSON estilo claude-code (rol, tool_use, contenido). Util tanto en chat de graph_explorer como en futuros chats. | Estas primitivas se extraen cuando empieza la extraccion de un Tier 1 que las necesita (no antes — caen en el "rule of three" tambien). ## Estrategia de adopcion Por **demanda**: 1. **Hoy (tras este issue):** copiar+adaptar `chat.cpp` a `navegator_dashboard`. Documentar la duplicacion. NO extraer. 2. **Cuando aparezca 3er consumidor de chat** (o `jobs`, o `project_switcher`): pausar feature work, refactorizar el extract con vista 3D. 3. **Auditoria periodica trimestral**: cada 3 meses revisar este issue, contar consumidores actuales, decidir si activar extracts. ## Sub-issues iniciales (no abrir hasta tener 3er consumidor identificado) - `0071a` — extract `claude_chat_panel` (cuando 3 apps lo pidan). - `0071b` — extract `jobs_queue_panel` (auditar duplicacion graph_explorer/odr_console primero). - `0071c` — extract `project_switcher_panel`. - `0071d` — extract `key_value_inspector_panel`. - `0071e` — extract `http_request_inspector_panel`. ## Definicion de hecho (issue 0071 cerrado) - Catalogo arriba esta vivo, refleja apps actuales. - Auditoria trimestral mantiene la lista actualizada. - Sub-issues 0071a-e: cada uno abre cuando se cumple su criterio. - Cuando todos los Tier 1 + 2 esten extraidos, este issue se marca completed. ## Anti-patron a evitar - **Big-bang refactor**: extraer 5 paneles a la vez, con interfaces no validadas, rompiendo todas las apps. NO. - **Pre-extraer "por si acaso"**: el panel mas dificil es el que solo tiene 1 consumidor — no sabes que parametrizar. NO. - **Library wrapper sin control**: meter `claude_chat_panel` con 30 hooks/callbacks "para flexibilidad". KISS — solo expone lo que el 3er consumidor demuestra que necesita.