---
id: 0011
title: Nodo tabla — UI expandida, promote/demote, ingesta CSV/Parquet
status: completed
priority: high
created: 2026-05-01
completed: 2026-05-01
depends_on: [0010]
---

## Objetivo

Fase 2 del nodo tabla. Sobre el cimiento DuckDB de 0010, anade UI completa:
expandir nodo Table en el viewport para ver paginas de filas, promover una
fila a entidad del grafo (nodo libre + arista punteada hacia su Table) y
demover de vuelta. Ingesta de CSV / Parquet como punto de entrada para
materializar tablas grandes.

## UI expandida

- Click sobre nodo Table -> selecciona; doble click -> toggle `expanded` en
  metadata.
- Cuando expanded:
  - El cuadrado overlay crece para acomodar grid de cabecera + ~20 filas
    visibles. Mas filas exigen scroll dentro del overlay.
  - Cabecera con nombres de `columns[]`. Anchura proporcional al texto, max
    cap a un % del overlay.
  - Filas paginadas via `ImGuiListClipper` + `tableview_page(offset,limit)`.
  - Indicador "promovida" (chip o color) en filas que ya estan en `entities`.
  - Doble-click en fila -> abre Inspector con esa entidad si esta promovida;
    si no, la promueve y abre Inspector.
- Aristas entrantes/salientes del nodo Table se siguen dibujando al centro
  (mejora a "al borde" se aplaza).

## Promote / demote

- Context menu sobre fila visible (overlay expandido):
  - "Promote to graph node": crea entidad en `operations.db` con metadata de
    origen, posiciona el nodo al lado del Table y dibuja arista punteada
    hacia el Table (overlay).
  - "Demote": deletea la entidad. La fila sigue viva en DuckDB.
- Una fila puede estar promovida una sola vez por (duckdb_path, table,
  row_id) — el helper de promocion debe checar y hacer no-op idempotente.

## Ingesta

- Boton/comando "Import dataset..." en menu o context menu del canvas.
- Modal con:
  - Path al CSV / Parquet / JSON.
  - Nombre de la tabla DuckDB destino.
  - row_type (combo con los entity types del proyecto + "(none)").
  - Boton "Import" -> ejecuta:
    ```sql
    CREATE TABLE <name> AS SELECT * FROM read_csv_auto('<path>');
    ```
    sobre `tables/<slug>.duckdb` (crea el .duckdb si no existe).
  - Tras import, crea automaticamente un nodo Table en el viewport apuntando
    a la nueva tabla.

## Cambios en codigo

- `tableview.{h,cpp}`:
  - `tableview_promote_row(ops_db, duckdb_path, duck_table, row_id, row_type, out_entity_id)`.
  - `tableview_demote_row(ops_db, entity_id)`.
  - `tableview_ingest_file(duckdb_path, file_path, dest_table, *file_kind)`.
- `views.cpp`:
  - Render expandida via overlay. Hit-testing por fila (rect intersection).
  - Modal "Import dataset...".
- `main.cpp`:
  - Wire context menu items. Recargar grafo tras promote/demote/ingest.

## Definicion de hecho

- Toggle expanded persiste en `entities.metadata` (JSON write).
- Tabla con 1M filas se navega con scroll fluido (paginacion 200 filas).
- Promote: la entidad creada aparece como nodo libre adyacente al Table,
  unida por arista punteada (visual solamente — no es relacion en BD).
- Demote: el nodo desaparece, la fila sigue contandose en `tableview_count`.
- Ingesta de CSV de 100k filas tarda < 5 s y deja la tabla lista para mostrar.
- Doble-click en fila no promovida la promueve y enfoca Inspector.
