Hipotesis del bug 'tras promover, la tabla expandida queda a 0 filas':
en Windows std::filesystem::path::string() devuelve la ruta con
backslashes ('C:\\Users\\...\\operations.db'). Al embebirla en
'ATTACH ''<path>'' AS ops' DuckDB la interpretaba con quirks segun
version, fallaba el ATTACH (silent), pero ademas el siguiente
duckdb_open con paths mixtos podria no abrir el .duckdb correcto.
Cambios:
- tableview_resolve_path normaliza '\\' -> '/' (DuckDB acepta ambos
para duckdb_open, pero forzamos '/' para evitar ambiguedad en SQL).
- ATTACH normaliza ops_db tambien.
- TableWindowState.last_error: cuando count o page fallan, se setea
con el path/tabla involucrada y se muestra en rojo en la cabecera
de la ventana. Asi el bug es visible sin abrir consola.
- tableview_page log incluye la SQL completa cuando falla — facil
diagnosticar via stderr en linux.
Cambios de UX en la toolbar y arranque:
- Boton 'Layout: <name>' que abre popup con la lista de layouts (force,
grid, circular, radial, hierarchical, fixed) + 'Reset positions
(unpin + restart)' + 'Save current layout'. Reemplaza el combo
pequeno + los botones Save/Reset que estaban dispersos.
- Boton 'Physics: ON/OFF' (Player Play/Pause) toggle visible que
reemplaza el checkbox 'Run layout'. Variant Primary cuando ON,
Subtle cuando OFF.
- Default: layout_mode = 5 (fixed) y layout_running = false. Asi al
abrir un proyecto los nodos respetan posiciones guardadas y no se
mueven solos. El usuario activa fisicas con el boton Physics y/o
cambia el layout desde el dropdown si quiere.
Reset layout (boton dentro del popup Layout) sigue activando physics
para que el grafo se reasiente; es el flujo natural del 'Reset'.
reload_after_mutation reconstruye g_graph.types[] con defaults via
reload_graph, pero NO reaplica el types.yaml ni reconstruye el icon
atlas. Resultado: cualquier mutacion (add/delete/duplicate/change_type/
promote/demote/import) hacia que los tipos perdiesen shape/color/icon
y todos los nodos volvieran a renderizarse como circulos grises.
Caso reproducible: doble-click en fila de tabla expandida -> promote
-> reload -> el nodo Table dejaba de ser cuadrado y se renderizaba
como circulo.
Fix: tras reload_graph + entity_index_build, si parsed_types tiene
contenido, reaplicar types.yaml y reconstruir el atlas con un
graph_icons_destroy + build_icon_atlas + g_atlas_bound=false +
g_gpu_dirty=true para que el viewport rebincie en el siguiente frame.
Tres ajustes derivados de feedback en uso:
1. tableview_promote_row recibe ahora `table_entity_id` y, si no es
nulo, inserta una relacion 'CONTAINS_ROW' (id estable, INSERT OR
IGNORE) entre la tabla origen y la entidad promovida. El viewport
pinta la arista de pertenencia automaticamente sin codigo extra.
2. apply_types_yaml fija default_size = 32 px (world) para tipos
Table junto al SHAPE_SQUARE ya existente. La GPU pinta el cuadrado
real; antes era invisible bajo el overlay rectangular.
3. views_table_overlay adelgaza al rol que le toca: solo dibuja un
contador discreto "<N> rows" debajo del cuadrado (texto pequeno
con bg semitransparente). El cuadrado en si lo pinta el GPU.
Defensiva: views_table_windows_sync marca page_dirty=true en TODAS las
windows live tras cada sync para que el flag promoted se refresque
inmediatamente despues de promote/demote/import.
- entity_ops: entity_list_rows (bulk pull id/name/type_ref/status/updated_at).
- AppState::TableRow + cache + filtros (search substring + show_all toggle).
- views_table: tabs por type_ref (alfabetico) o tabla unica con todos los
tipos. ImGui::BeginTable con sort + clipper para >10k filas. Click en
Selectable selecciona el nodo en el viewport (clear + add via
graph_viewport_*).
- views_table_refresh_indices: degree + node_idx por user_data hash.
- main.cpp: panel "Table" en g_panels; cache build tras load_input y
reload_after_mutation.
- views_type_editor: panel "Types" con tabs Entities/Relations.
Entities: name, color picker, shape combo, icon (ti-* + cp preview),
principal_field combo, tabla de Fields (string/int/float/bool/date/url/enum)
con required y enum values CSV; up/down/X por fila.
Relations: name, color, style.
Footer Save / Reload from disk + indicador dirty + error inline.
- views_type_editor_delete_modal: confirm con conteo de entidades en uso.
- types_registry: shape_name() + shape: emit en types_save_yaml para
round-trip estable de la cosmetica editada en UI.
- main.cpp: panel "Types" en g_panels; init types_draft tras load_input;
want_types_save -> save + apply_types_yaml + rebuild atlas + bind +
refresh inspector caches; want_types_reload simetrico; conteo de
uso desde operations.db cuando se abre el modal de delete.
Issue 0008:
- types.yaml: stash ParsedTypes en g_app.parsed_types tras parsear (lo
consume el Inspector para resolver schemas por type_ref).
- load_input: tras load, llama views_inspector_clear_draft + _refresh_caches.
- switch_to_project: limpia draft y parsed_types antes de cargar nuevo.
- render(): seleccion → load_draft (si single sel y no dirty), Save →
entity_update + reload_graph + relocaliza node_idx por sql_id +
re-load draft + reselecciona en viewport, Discard → re-load draft.
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
- examples/types.yaml: principal_field + fields para Person, Email,
Domain, Phone, Org, IBAN, Account, Document, Address, Url, Table.
44 fields totales. Documentacion del formato en cabecera.
- project_manager.cpp: seed con fields para los tipos basicos (fallback
cuando no se encuentra examples/types.yaml).
- main.cpp:
- Log de carga incluye conteo de schemas y total de fields.
- --test-types-yaml <path>: smoke test que carga, serializa a temp y
recarga. Compara entidades/relaciones/fields field-a-field. Salida
PASS/FAIL con exit code 0/1. Permite verificar round-trip sin
framework de tests.
Verificado: examples/types.yaml round-trip estable (11 entities, 44
fields, 6 relations).
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
- types_registry.cpp::apply_types_yaml: tras aplicar el yaml, sobreescribe
shape de cada tipo: 'Table' → SHAPE_SQUARE, todo lo demas → SHAPE_CIRCLE.
Convencion fija — ediciones futuras del Type Editor (issue 0007) o del
yaml no rompen la regla.
- examples/types.yaml + project_manager.cpp seed: quitar campo `shape`,
añadir tipo Table (cuadrado) y relacion CONTAINS_ROW (preview de 0010).
- main.cpp run_force_step: damping=0.7, max_velocity=8 explicitos para
evitar que el grafo "explote" al cargar grafos pequenos.
- AppState repulsion: 1500 → 800 (lo mismo, aplicado al force layout).
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
main.cpp:
- Forward decl + switch_to_project: cierra layout_store, libera grafo,
aplica nuevos paths, vuelve a cargar.
- apply_project_paths: deriva operations.db/types.yaml/graph_explorer.db
del slug y los expone a g_app.active_project.
- main: arg --project <slug>; modo legacy si --input/positional dado;
modo proyecto si no — migra layout legacy, decide target via
arg/last_active/'default', crea si no existe, abre BDs y carga.
- render(): handler want_switch_project + monta views_new_project_modal.
views.h: AppState gana active_project, want_switch_project,
switch_project_target, show_new_project_modal, new_project_buf,
new_project_error, project_list_cache, project_recent_cache.
views.cpp:
- Toolbar: boton 'Project: <slug>' con popup (New/Recent/Open/Reveal).
Refresca caches al abrir el menu.
- views_new_project_modal: input slug + validacion + creacion + switch.
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
Bug fixes
- ImGui ID conflict en menu Change type: dedup tipos del grafo +
defaults; PushID/PopID por entrada.
- Dockspace ya no tapa la toolbar: se posiciona 44 px por debajo, asi
las ventanas dockeadas al borde superior quedan bajo la barra de
filtros, no detras.
- Hover radius proporcional al tamaño visual del nodo: query espacial
amplio (24/zoom) + filtro fino por (radio_visual + 2 px) / zoom. El
tooltip solo se dispara si el raton esta efectivamente sobre el nodo.
Layout
- Default layout = grid (en vez de force) para que los grafos cargados
se distribuyan ordenadamente al abrir.
- Boton "Reset layout" en la toolbar: limpia NF_PINNED en todos los
nodos, resetea velocidades y reaplica el layout activo.
- Nodos recien creados (add_node, duplicate) caen en un anillo poisson
alrededor del centro de la vista, no en el origen. Posicion
determinista por user_data para que el mismo nodo no salte entre
reloads.
Notes (markdown)
- Panel "Note" (dockeable) abierto con doble click sobre un nodo.
- entity_get_notes / entity_set_notes en entity_ops sobre la columna
`notes` de operations.db (ya existente en el schema).
- Ctrl+S guarda. Cabecera muestra entity, type, id.
- Dockspace host (PassthruCentralNode) bajo la toolbar para que las
ventanas Viewport/Legend/Inspector/Stats puedan dockearse dentro de la
app principal.
- Toolbar: input "Add node" con auto-deteccion de tipo (text/email/
ip_address/url/domain/phone). Insert en operations.db + reload.
- Context menu (right-click sobre nodo): Change type, Duplicate, Delete,
submenu "Run enricher" (placeholder hasta issues 0001-0003).
- Inspector: vecinos ahora muestran etiqueta de relacion ("-> employs",
"<- owns") usando rel_types[].name como label de arista.
- Default relation label k_default_relation_name="RELATED_TO" para
relaciones creadas sin nombre semantico explicito.
- Indice EntityIndex (FNV1a hash -> sql id) reconstruido tras cada load
para resolver mutaciones desde el grafo en memoria.
Issues planteadas para iteraciones siguientes:
- 0001: chat con Claude sobre el grafo (HTTP + tool-use)
- 0002: enricher GLiNER+GLiREL desde nodo texto
- 0003: enricher web (fetch URL/dominio + extract text)
- 0004: vista tabla por tipo de entidad