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.
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.
- TableMetadata struct + tableview_get_metadata: lee la metadata de un
nodo Table (path, table, row_type, columns, label_column, expanded...).
- tableview_set_expanded: persiste el flag expanded usando json_set.
- tableview_set_columns: sobrescribe metadata.columns.
- tableview_promote_row: idempotente — si ya existe entidad con
metadata.source.row_id == row_id la devuelve; si no, lee fila completa
desde DuckDB e inserta entity con id 'prom_<type>_<row_id>' y metadata
incluyendo source + columnas.
- tableview_demote_row: DELETE FROM entities (la fila DuckDB no se toca).
- tableview_ingest_file: CREATE TABLE AS SELECT * FROM read_csv_auto/
read_parquet/read_json_auto segun extension del input.
- tableview_list_columns: SELECT * FROM tabla LIMIT 0 -> nombres.