#pragma once #include #include #include "types_registry.h" #include "entity_ops.h" #include "node_groups.h" #include "core/data_table_types.h" #include #include struct GraphData; struct GraphViewportState; namespace ge { // Discriminador de la NodeGroups window (issue 0036b). Una window puede // estar respaldada por una tabla DuckDB (kind=Table) o por una agrupacion // de entidades en operations.db via `entities.group_id` (kind=Group). enum class NodeGroupsKind { Table, Group }; // Estado compartido entre las vistas y el bucle render. Pasado por puntero // desde main.cpp. struct AppState { // Datos GraphData* graph = nullptr; GraphViewportState* viewport = nullptr; // Layout activo — default grid (1) para que los grafos cargados de // operations.db se distribuyan ordenadamente al abrir. // Default: fixed (5) — respeta posiciones guardadas y physics off por // defecto. El usuario activa fisicas con el boton Physics y/o cambia // layout desde el dropdown Layout en la toolbar. int layout_mode = 5; // 0=force, 1=grid, 2=circular, 3=radial, 4=hierarchical, 5=fixed int apply_layout_tick = 0; // se incrementa cuando hay que reaplicar layout bool want_unpin_all = false; // Reset layout: limpia NF_PINNED y reaplica // Force layout — config + GPU toggle. Repulsion bajada de 1500→800 // (issue 0006 follow-up) para evitar movimiento excesivo al cargar // grafos pequenos. Combinado con damping=0.7 y max_velocity=8 en // run_force_step. float repulsion = 800.0f; float attraction = 0.04f; float gravity = 0.005f; bool use_gpu = false; // Stats UI int fps_estimate = 0; // sintetico, calculado en main loop // Filters / visibility por tipo (longitud = graph->type_count o rel_type_count) bool type_visible[256] = {}; bool rel_type_visible[256] = {}; int type_visible_n = 0; int rel_type_visible_n = 0; // Inspector bool panel_legend = true; bool panel_inspector = true; bool panel_stats = true; bool panel_viewport = true; bool panel_note = false; bool panel_jobs = false; // issue 0026 bool panel_chat = false; // claude -p chat (issue 0001) bool panel_extract = false; // paste & extract (issue 0013) bool show_filters_modal = false; bool show_open_modal = false; // Triggers — main.cpp lee estos flags y actua bool want_fit = false; bool want_save_layout = false; bool want_reload = false; bool want_open_file = false; // marcado al confirmar el modal Open char open_buf[512] = {}; // Project system (issue 0006) std::string active_project; // slug del proyecto activo bool want_switch_project = false; std::string switch_project_target; // slug objetivo del switch bool show_new_project_modal = false; char new_project_buf[80] = {}; std::string new_project_error; // mensaje a mostrar en el modal std::vector project_list_cache; // refrescado al abrir el menu Project std::vector project_recent_cache; // Labels overlay bool labels_enabled = true; // Path activo de operations.db (para CRUD desde toolbar / contextmenu). // main.cpp lo escribe tras cargar y los handlers lo leen. std::string input_db_path; // ---- Grouping (issue 0035b) --------------------------------------------- // Estado de expansion de nodos `Group` en RAM. Default vacio = todos los // grupos colapsados. No persiste entre sesiones (fase 1). El filtro del // loader (apply_group_filter) consulta este map: si una entidad tiene // `group_id != NULL` y el grupo padre no esta en este map con valor true, // la entidad se oculta del grafo. Sin UI todavia para togglear; se setean // valores manualmente desde tests/debug. std::unordered_map group_expanded; // Add-node toolbar input. char add_buf[256] = {}; // Triggers de mutacion — main.cpp los procesa y dispara reload. bool want_add_node = false; // commit del input add_buf bool want_delete_node = false; // delete del nodo en ctx_node bool want_duplicate_node = false; bool want_change_type = false; // a ctx_new_type int ctx_node = -1; // node_idx objetivo char ctx_new_type[64] = {}; // Context menu state — popup global identificado por nombre. bool ctx_open_request = false; // se setea en on_context_menu // Note editor (panel "Note" abierto con doble click sobre nodo). int note_node = -1; // node_idx siendo editado std::string note_entity_id; // sql id resuelto std::string note_entity_label; // display std::string note_entity_type; std::vector note_buf; // editable, NUL-terminated bool note_dirty = false; bool want_save_note = false; bool want_open_note = false; // doble click → cargar y abrir int open_note_target = -1; // node_idx a abrir // ---- Inspector editable (issue 0008) ---------------------------------- // Schema vivo del proyecto activo (load/save desde types.yaml). ParsedTypes parsed_types; // Draft del inspector — todo lo que el usuario esta editando para el // nodo seleccionado. Se carga desde BD al cambiar la seleccion (si no // hay cambios pendientes) y se persiste con entity_update al guardar. int insp_node_idx = -1; std::string insp_entity_id; char insp_name_buf[256] = {}; char insp_type_buf[64] = {}; std::vector insp_desc_buf; // multiline int insp_status_idx = 0; // 0=active 1=stale 2=corrupted 3=archived // Listas paralelas: keys + valores actuales de los campos de metadata. // Las claves del schema del tipo van primero (en su orden), las "extras" // van detras. `is_extra[i]` distingue para render diferenciado y para // permitir borrar solo extras desde la UI. std::vector insp_field_keys; std::vector insp_field_values; std::vector insp_is_extra; std::vector insp_tags; char insp_tag_input[64] = {}; char insp_extra_key[64] = {}; bool insp_dirty = false; bool insp_show_unsaved = false; int insp_pending_target = -1; bool want_inspector_save = false; bool want_inspector_discard = false; // Caches refrescadas tras cargar grafo o tras Save. std::vector insp_tag_suggestions; std::vector insp_type_options; // ---- Table node (issue 0010) ------------------------------------------ // Cache de conteo de filas por nodo Table indexado por user_data hash. // Refrescado tras load_input y tras mutaciones que afecten a Tables. std::unordered_map node_groups_counts; // ---- NodeGroups window (issue 0011, renombrado en 0036a, kind en 0036b) - // Estado runtime por ventana de NodeGroups. Hay dos kinds (issue 0036b): // - Table: respaldada por DuckDB (el comportamiento original — un nodo // `Table` del grafo que apunta a un .duckdb + table_name). // - Group: respaldada por la propia operations.db. Lista las entidades // hijas (`entities.group_id = container_id`) con columnas fijas // id/name/type_ref/status/updated_at. // // Una entrada por container_id (entity_id del nodo contenedor — Table o // Group). La ventana se cierra al pulsar la X de ImGui o, en kind=Table, // al hacer set_expanded(false) desde el menu contextual. struct NodeGroupsWindowState { NodeGroupsKind kind = NodeGroupsKind::Table; // default compat 0036a NodeGroupsMeta meta; // refrescada cada vez que entity cambia int64_t total_rows = 0; int64_t offset = 0; std::vector page; bool page_dirty = true; bool open = true; // bound a ImGui::Begin bool focus_request = false; // 0036c: pedir SetWindowFocus std::string last_error; // ultimo error de query (vacio = OK) }; std::unordered_map node_groups_windows; // Triggers consumidos por main.cpp tras click en filas. bool want_promote_row = false; std::string promote_table_id; // entity_id del Table de origen std::string promote_row_id; // valor del id_column bool want_demote_entity = false; std::string demote_entity_id; bool want_focus_entity = false; // tras promote+open inspector std::string focus_entity_id; // Issue 0036d: Promote en NodeGroups kind=Group → saca la entidad // del grupo (group_id = NULL). main.cpp lo procesa y dispara reload. bool want_clear_group_id_entity = false; std::string clear_group_id_entity_id; // Modal "Import dataset..." (issue 0011 Ingesta). bool show_import_modal = false; char import_path_buf[512] = {}; char import_table_buf[64] = {}; char import_duckdb_buf[256] = {}; // relativo a project root char import_row_type_buf[64] = {}; bool want_import = false; std::string import_error; // Toggle NodeGroups window desde context menu del viewport. bool want_toggle_nodegroups = false; std::string toggle_nodegroups_id; // ---- Table view (issue 0004) ------------------------------------------- // Vista tabular dockeable. Tabs por type_ref del grafo activo + opcional // "All". Click selecciona el nodo en el viewport (mismo flujo que el // Selectable del Inspector). struct TableRow { std::string id; std::string name; std::string type_ref; std::string status; std::string updated_at; std::string group_id; // si pertenece a un Group, su sql id; "" si no int neighbors = 0; int node_idx = -1; }; bool panel_table = false; std::vector table_rows; // snapshot, refrescado tras load/reload bool table_cache_dirty = true; char table_search_buf[96] = {}; bool table_show_all = false; int table_active_tab = 0; // Filtros por columna: column_user_id (0..5) -> substring filter. // Visible UX: right-click sobre header de columna abre popup con input; // chips con filtros activos por encima de la tabla. std::unordered_map table_col_filters; char table_filter_input[96] = {}; // buffer del popup activo int table_filter_pending_col = -1; // col_user_id en edicion // data_table::State para el panel Table (issue 0081-J). // Persiste filters/sort/stages entre frames; uno por instancia de render. data_table::State table_dt_state; // data_table::State para el panel Jobs (issue 0081-J Phase 1). // Persiste filters/sort/stages entre frames para la tabla de jobs. data_table::State jobs_dt_state; // ---- Type Editor (issue 0007) ------------------------------------------ // Draft del editor de tipos. Se inicializa con una copia de parsed_types // tras cargar el grafo. Save reescribe `types.yaml` y dispara // apply_types_yaml + rebuild de IconAtlas. bool panel_type_editor = false; ParsedTypes types_draft; bool types_dirty = false; int te_tab_idx = 0; // 0=Entities 1=Relations int te_entity_idx = -1; // seleccion entity int te_relation_idx = -1; // seleccion relation bool want_types_save = false; bool want_types_reload = false; int te_pending_delete_e = -1; // entity idx pendiente de confirmar int te_pending_delete_r = -1; int te_delete_use_count = 0; // entidades afectadas bool show_te_delete_modal = false; std::string types_save_error; // mensaje a renderizar bajo Save // ---- Filtros y busqueda FTS5 (issue 0009) ------------------------------ // Modos: 0 = highlight (no-match dimmed), 1 = hide (no-match invisible). enum FilterMode { FM_HIGHLIGHT = 0, FM_HIDE = 1 }; int filter_mode = FM_HIGHLIGHT; char filter_query_buf[128] = {}; std::vector filter_tags; // chips activos std::vector filter_hits; // dropdown FTS (max 20) bool filter_dropdown_open = false; bool filter_dirty = false; // pide reapply int filter_focus_target = -1; // node_idx a centrar char filter_tag_input[64] = {}; // input de chip nuevo // ---- Enricher config window -------------------------------------------- // Cuando el usuario clica un enricher con `params` no vacios en el // context menu, se rellena este bloque y se abre una ventana ImGui // (no modal) que permite ajustar los valores antes de submitear el // job. La ventana es dockeable y movible; cerrar la X cancela. // Si el enricher no declara params, se submitea directamente con `{}` // sin pasar por aqui. bool enr_window_open = false; // visibilidad std::string enr_modal_id; // enricher.id std::string enr_modal_node_id; // sql_id del nodo std::string enr_modal_node_label; // label visible // Buffer editable por param. Tamano fijo 256 para inputs de texto; // suficiente para queries y URLs cortas. Indices alineados con // EnricherSpec::params del enricher seleccionado. std::vector> enr_modal_param_bufs; }; // Toolbar superior (Open file, Layout selector, Filters..., Fit, Save layout). void views_toolbar(AppState& app); // Panel Legend — checkboxes por tipo (entity / relation) con color swatch. void views_legend(AppState& app); // Panel Inspector — metadata del nodo seleccionado + vecinos. void views_inspector(AppState& app); // Stats line — counts + fps + energy + selection. void views_stats(AppState& app); // Note editor — abre con doble click sobre un nodo. Edita la columna `notes` // (markdown) de la entidad y guarda con un boton. void views_note(AppState& app); // Modal Filters — toggles por tipo agrupados en columnas. Devuelve true si // el usuario togglo algo. bool views_filters_modal(AppState& app); // Modal Open file — text input + boton Open. bool views_open_modal(AppState& app); // Modal "New project..." — slug input con validacion. Devuelve true si el // usuario confirma con un slug valido. bool views_new_project_modal(AppState& app); // Refresca los flags `flags` de cada nodo/arista segun el array // `type_visible[]` / `rel_type_visible[]`. Lineal en N+M. void views_apply_visibility(AppState& app); // Inicializa los arrays type_visible / rel_type_visible a true para todos // los tipos del grafo activo. Llamar tras cargar/recargar el grafo. void views_reset_visibility(AppState& app); // ---- Inspector editable helpers (issue 0008) ------------------------------ // Refresca insp_tag_suggestions e insp_type_options leyendo BD y schema. // Llamar tras cargar el grafo o tras un Save. void views_inspector_refresh_caches(AppState& app); // Carga el draft del Inspector desde la BD para el nodo `node_idx`. Si el // nodo no es resoluble o no existe, deja el draft vacio. No respeta dirty: // el caller debe haberlo manejado ya. void views_inspector_load_draft(AppState& app, int node_idx, const char* entity_id); // Construye un EntityRecord desde el draft actual respetando el schema // del type_ref para decidir is_string de cada metadata field. EntityRecord views_inspector_build_record(const AppState& app); // Resetea el draft (todos los buffers + dirty=false). Util tras Save o // al cambiar de proyecto. void views_inspector_clear_draft(AppState& app); // ---- NodeGroups window (issue 0011, renombrado en 0036a) ---------------- // Renderiza una ventana ImGui dockeable por cada NodeGroups en // node_groups_windows con `open=true`. Cabecera con nombres de columnas. // Filas paginadas con ImGuiListClipper consumiendo el page cache; al // cambiar el offset, marca dirty para que main.cpp refresque via // node_groups_page. Doble click en fila no promovida -> setea // promote_table_id/promote_row_id; promovida -> focus_entity_id. Cerrar la // ventana setea expanded=false en BD. void views_node_groups_window(AppState& app); // Modal "Import dataset..." — formulario para crear una tabla DuckDB // desde CSV/Parquet/JSON y registrar el nodo Table correspondiente. bool views_import_dataset_modal(AppState& app); // Sincroniza node_groups_windows con la metadata.expanded de cada nodo // Table. Llamar tras load + tras mutaciones que cambien expanded. Crea // entradas para nuevos expanded y borra las que ya no aplican. void views_node_groups_windows_sync(AppState& app, const char* ops_db); // Crea o reusa una entrada en `app.node_groups_windows[container_id]` y la // marca con el `kind` indicado. Setea `focus_request = true` para que el // render pueda llamar a ImGui::SetWindowFocus (lo consume 0036c). Si la // entry ya existe se respeta su kind anterior y solo se setea // focus_request — no recarga ni resetea offset. Si es nueva, llama a // `node_groups_load_metadata` para popular `meta` (en kind=Group eso pre- // puebla las columnas fijas; en kind=Table lee la metadata del Table-typed // node de operations.db). // // `ops_db` es el path de operations.db. Si esta vacio, no se carga // metadata pero la entry se crea de todos modos (caller puede rellenar // despues). Devuelve puntero a la entry — nunca nullptr salvo // container_id vacio. AppState::NodeGroupsWindowState* views_node_groups_open(AppState& app, const std::string& container_id, NodeGroupsKind kind, const char* ops_db); // ---- Table node overlay (issue 0010) ------------------------------------ // Dibuja un overlay rectangulo redondeado sobre cada nodo `Table` del grafo // con etiqueta "Table · N rows" leyendo de app.node_groups_counts. Llamar // despues de graph_viewport(...) — usa GetItemRectMin/Max + GetWindowDrawList // del item viewport. No interactua con eventos; el hit-testing del nodo // sigue usandolo el viewport circular de fondo. void views_node_groups_overlay(AppState& app); // ---- Table view (issue 0004) -------------------------------------------- // Renderiza el panel "Table". Lee de app.table_rows; el caller ya ha hecho el // build/refresh tras cargar el grafo. Click en fila selecciona el nodo en el // viewport (mismo flujo que el Selectable del Inspector). Filtro por // substring sobre name/id en la cabecera. void views_table(AppState& app); // Recompute neighbors[] y node_idx[] de las filas existentes a partir del // grafo cargado. Llamar tras cargar el grafo o tras una mutacion. void views_table_refresh_indices(AppState& app); // ---- Type Editor (issue 0007) ------------------------------------------- // Renderiza el panel "Types" — tabs Entities/Relations, lista a la izquierda // con +/-, panel de edicion a la derecha. Marca app.types_dirty al cambiar y // activa app.want_types_save / app.want_types_reload desde el footer. void views_type_editor(AppState& app); // Modal de confirmacion para borrar un tipo en uso. Se abre cuando // app.show_te_delete_modal = true. main.cpp es responsable de poblar // te_delete_use_count via consulta a operations.db antes de mostrarlo. bool views_type_editor_delete_modal(AppState& app); // ---- Jobs panel (issue 0026) --------------------------------------------- // Renderiza el panel "Jobs" — tabla con todos los jobs (queued/running/done/ // error/cancelled). Botones por fila para cancelar / reintentar / borrar. // Click en target_node centra el viewport sobre ese nodo (futuro). Polling // cada N frames para no spammear la BD. void views_jobs(AppState& app); // ---- Filter helpers (issue 0009) ----------------------------------------- // True si el filtro tiene query no vacia o al menos un tag activo. bool views_filter_active(const AppState& app); // Anade un tag como chip si no existe ya. Marca filter_dirty = true. void views_filter_add_tag(AppState& app, const char* tag); // Limpia query + tags y marca filter_dirty. void views_filter_clear(AppState& app); // Reaplica el filtro al grafo: recompute la mascara de visibilidad/alpha // segun filter_query, filter_tags, filter_mode. Llama a la BD si hace // falta (FTS + tags). Resetea color_override a 0 en los nodos que pasan // el filtro y aplica un alpha bajo en los que no (modo highlight) o limpia // NF_VISIBLE (modo hide). Tambien refresca filter_hits para el dropdown // con max 20 resultados ordenados por rank. void views_filter_apply(AppState& app); } // namespace ge