Files
fn_registry/dev/issues/0071-extract-reusable-cpp-panels-roadmap.md
T

10 KiB

id, title, status, type, domain, scope, priority, depends, blocks, related, created, updated, tags
id title status type domain scope priority depends blocks related created updated tags
57 Extraer paneles ImGui reutilizables de las apps C++ a cpp/functions/ — roadmap pendiente epic
cpp-stack
registry-quality
cross-stack media
2026-05-09 2026-05-17

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:

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<std::string, std::string> env_vars;  // GX_*, NAVD_*, ...
    const char* mcp_config_path;        // o func que lo genera on-demand
    const char* system_prompt;          // opcional
    std::function<int()> 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:

namespace fn_ui {
    struct Job {
        std::string id;
        std::string title;
        std::function<int(JobContext&)> 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.