docs(issues): planning issues 0005-0010 (types schema, projects, type editor, inspector, fts, table node)

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
This commit is contained in:
2026-04-30 23:40:14 +02:00
parent ccaa76455d
commit 491161204e
6 changed files with 331 additions and 0 deletions
+56
View File
@@ -0,0 +1,56 @@
---
id: 0005
title: Type schema en types.yaml — fields por tipo + principal_field
status: pending
priority: high
created: 2026-04-30
---
## Objetivo
Extender `types.yaml` para declarar el **esquema de campos** de cada entity
type, ademas de la cosmetica que ya existe (color/shape/icon). Es la base
sobre la que tiran 0007 (Type Editor) y 0008 (Inspector editable).
## Formato propuesto
```yaml
entities:
- name: Person
color: "#5B8DEF"
shape: circle
icon: ti-user
principal_field: name # opcional, default = "name"
fields:
- { name: name, type: string, required: true }
- { name: first_name, type: string }
- { name: last_name, type: string }
- { name: age, type: int }
- { name: nationality, type: string }
- { name: birth_date, type: date }
- { name: gender, type: enum, values: [male, female, other] }
```
Tipos soportados (v1): `string`, `int`, `float`, `bool`, `date`, `url`,
`enum`. `enum` requiere `values: [...]`.
`principal_field` es el campo que el viewport usa como label visible del
nodo cuando no hay otro override (sustituye al actual `name` hardcodeado).
## Cambios en codigo
- `types_registry.h`:
- Nuevo `FieldSpec { string name; FieldKind kind; bool required; vector<string> enum_values; }`.
- `EntitySpec` gana `string principal_field` y `vector<FieldSpec> fields`.
- `types_registry.cpp`:
- Parser tolerante: ignora `fields:` si no esta presente; tipos
desconocidos se mapean a `string` con warning.
- **Writer** nuevo: `bool types_save_yaml(const char* path, const ParsedTypes& types, string* error_msg)`. Round-trip que preserva el orden de claves y el formato compacto del ejemplo.
- `examples/types.yaml`: anadir `fields` a Person, Email, Domain, Phone, Org como semilla.
## Definicion de hecho
- Parser lee el yaml extendido sin romper el yaml existente (sin `fields`).
- Writer regenera un yaml legible y re-parseable (test round-trip).
- `examples/types.yaml` incluye `fields` para los 10 tipos existentes.
- `apply_types_yaml` no se rompe (no toca `fields` aun, eso es 0007/0008).
+68
View File
@@ -0,0 +1,68 @@
---
id: 0006
title: Sistema de proyectos dentro de la app — subcarpetas + switcher
status: pending
priority: high
created: 2026-04-30
---
## Objetivo
Cada "proyecto" es una subcarpeta junto al exe que contiene su propio
`operations.db`, `types.yaml` y `graph_explorer.db` (layouts). El usuario
crea proyectos, los nombra, y conmuta entre ellos sin reiniciar la app.
Esto es lo que va a permitir guardar muchos datos separados por caso/tema
sin acumularlos en un solo grafo.
Independiente del concepto de "project" del registry (`projects/` raiz
del repo) — aqui es organizativo, dentro de la app desplegada.
## Layout en disco (junto al exe)
```
graph_explorer.exe
graph_explorer.ini # estado global: ultimo proyecto abierto, recientes
projects/
default/
operations.db
types.yaml # copia editable; semilla = examples/types.yaml
graph_explorer.db # layouts de este proyecto
notes/ # opcional, .md sueltos
caso_aurgi/
operations.db
types.yaml
graph_explorer.db
osint_demo/
...
```
## UI
- Menu "Project" en la menubar:
- `New...` (input de nombre, valida slug, crea carpeta + semilla types.yaml + operations.db vacio con schema migrado).
- `Open...` (combo con los proyectos detectados en `./projects/`).
- `Recent` (lista los ultimos 5 desde `graph_explorer.ini`).
- `Reveal in explorer` (abre la carpeta del proyecto activo).
- Title bar muestra `graph_explorer — <proyecto activo>`.
- Switch de proyecto: cierra BDs, limpia grafo + viewport, abre nuevas BDs, hace reload.
## Cambios en codigo
- `main.cpp`:
- Resolver paths a traves de `g_active_project` en vez de `./operations.db` y `./graph_explorer.db` directos.
- Modo legacy: si arranca con `--input <path>` o positional, no usa proyecto (compatibilidad con flujo actual).
- `app_state` nuevo campo `string active_project_dir`.
- Nueva fn `project_create(name)` que escribe schema base de operations.db (entities/relations/...) — extraerlo de lo que ya hace el registry o copiar el DDL.
- `graph_explorer.ini` lee/escribe via `app_settings_*` ya existente.
## Migracion del flujo actual
- Al primer arranque sin `--input`, si no existe `./projects/`, crear `./projects/default/` y mover `./operations.db` + `./graph_explorer.db` ahi (si existen).
- Issues 0005/0007/0008 ya asumen este layout.
## Definicion de hecho
- Crear, abrir y conmutar proyectos sin reiniciar.
- Cada proyecto tiene su `operations.db`, `types.yaml`, `graph_explorer.db` aislados.
- "Recent" persiste entre sesiones via `graph_explorer.ini`.
- El layout (posiciones de nodos) es por-proyecto: cambiar de proyecto recupera las posiciones del nuevo, no las del anterior.
+44
View File
@@ -0,0 +1,44 @@
---
id: 0007
title: Type Editor panel — CRUD de tipos desde la app
status: pending
priority: medium
created: 2026-04-30
depends_on: [0005, 0006]
---
## Objetivo
Panel "Types" dockeable que permita editar el `types.yaml` del proyecto
activo desde dentro de la app. Cosmetica (color/shape/icon) y schema de
campos (lista de fields con tipo). Save reescribe el yaml y dispara
`apply_types_yaml` + reload del grafo.
## UI
- Tabs: "Entities" / "Relations".
- Lista a la izquierda con los tipos. Botones `+` (anadir), `-` (borrar), drag para reordenar.
- Panel derecho con la edicion del tipo seleccionado:
- Entities: name, color (color picker), shape (combo), icon (input + preview), principal_field (combo entre los fields).
- Relations: name, color, style.
- Sub-seccion **Fields** (solo entities): tabla con name / type / required / values (para enum). Botones para anadir/quitar/reordenar fila.
- Footer: `Save to types.yaml` (deshabilitado si no hay cambios) + `Reload from disk` (descarta cambios).
## Cambios en codigo
- `views.{h,cpp}`: nueva fn `views_type_editor(AppState&)` + entrada en panels menu.
- `app_state`: anade `ParsedTypes types_draft;` y `bool types_dirty;`.
- Usa `types_save_yaml` de 0005 para persistir.
- Tras Save: `apply_types_yaml(graph, types_draft)` + rebuild del IconAtlas.
## Notas
- Borrar un tipo NO toca las entidades existentes con ese `type_ref` — se quedan con un tipo "huerfano" hasta que el usuario las cambie. Mostrar warning antes del delete con conteo de entidades afectadas.
- Cambiar `principal_field` de un tipo regenera los labels visibles.
## Definicion de hecho
- Crear, editar y borrar entity/relation types desde la UI.
- Editar fields (anadir, cambiar type, marcar required, definir enum values).
- Save persiste a `projects/<activo>/types.yaml` y aplica al grafo en vivo.
- Reload from disk descarta cambios.
+55
View File
@@ -0,0 +1,55 @@
---
id: 0008
title: Inspector editable — campos del schema + tags + extras
status: pending
priority: high
created: 2026-04-30
depends_on: [0005]
---
## Objetivo
Refactor del Inspector (`views_inspector`) de read-only a editable. El usuario
edita los datos de un nodo y al pulsar Save se persisten en `operations.db`
con un solo UPDATE.
## Bloques del nuevo Inspector
1. **Identidad**
- `name` (InputText), `type_ref` (combo con tipos del proyecto), `description` (InputTextMultiline corto), `status` (combo: active/stale/corrupted/archived).
2. **Fields del tipo** (driven by 0005)
- Render automatico segun `EntitySpec.fields`:
- `string` → InputText.
- `int` → InputInt.
- `float` → InputDouble.
- `bool` → Checkbox.
- `date` → InputText con placeholder `YYYY-MM-DD` + validacion.
- `url` → InputText + boton "Open" que lanza el navegador.
- `enum` → Combo con `values`.
- Lee/escribe a `entities.metadata` (JSON). Campos requeridos marcados con `*`.
3. **Extras** (campos fuera del schema del tipo)
- Lista key-value, "+" para anadir. Tipo siempre string en v1.
- Permite que el usuario meta info ad-hoc sin tocar el schema.
4. **Tags**
- Chips con boton X para quitar. Input al final con autocomplete (lista de tags ya existentes en la BD via `SELECT DISTINCT json_each.value FROM entities, json_each(entities.tags)`).
- Enter o coma valida el chip.
5. **Notes** — sigue en su panel actual, pero anadir un boton "Open notes" en el Inspector que enfoca el panel Note.
## Cambios en codigo
- `entity_ops.{h,cpp}`:
- `bool entity_update(db_path, id, name, type_ref, description, status, tags_json, metadata_json)` — un solo UPDATE.
- `bool entity_list_distinct_tags(db_path, vector<string>* out)` para autocomplete.
- `views.cpp::views_inspector`: reemplazo completo. Estado de edicion en `AppState` para que cerrar y reabrir conserve el draft hasta Save.
- Save → reload del grafo (mismo flujo que toolbar add-node hoy).
## Definicion de hecho
- Editar name, type_ref, description, status, tags, fields tipados y extras.
- Save persiste y el viewport refleja los cambios (label, color del tipo, etc.).
- Cancelar / cambiar de seleccion sin guardar muestra un confirm dialog.
- Tags se autocompletan con las existentes en la BD.
+37
View File
@@ -0,0 +1,37 @@
---
id: 0009
title: Filtro por tag + busqueda FTS5 en toolbar
status: pending
priority: medium
created: 2026-04-30
depends_on: [0008]
---
## Objetivo
Buscar y filtrar entidades por nombre/tag/description directamente desde la
toolbar, aprovechando el `entities_fts` que ya existe en `operations.db`.
## UI
- Toolbar: input de busqueda + chip-list de tags activas como filtro.
- Mientras se escribe, dropdown con resultados (max 20). Click en resultado selecciona/centra el nodo.
- Click en un tag (en Inspector o en el dropdown) lo anade como chip de filtro.
- Modos de filtro:
- **Highlight**: nodos no-coincidentes se atenuan (alpha bajado), pero siguen presentes.
- **Hide**: nodos no-coincidentes se ocultan del viewport.
- Toggle en la toolbar.
## Cambios en codigo
- Nueva fn `entity_search_fts(db_path, query, limit, vector<EntityHit>* out)` con resultados ordenados por rank.
- Filtro vive en `AppState`: vector de tags + query string + modo (highlight/hide).
- En `graph_viewport.cpp` o donde aplique: aplicar mascara de visibilidad/alpha segun el filtro activo (sin tocar la BD).
- Reset filter desde la toolbar.
## Definicion de hecho
- FTS sobre name/description/tags/domain funciona y los resultados centran el viewport.
- Multiples chips de tag combinan con AND.
- Modo highlight y modo hide funcionan sin recargar el grafo.
- Limpiar filtro restaura visibilidad completa.
+71
View File
@@ -0,0 +1,71 @@
---
id: 0010
title: Nodo tabla — contenedor cuadrado con filas que son nodos del grafo
status: pending
priority: medium
created: 2026-04-30
depends_on: [0004, 0005, 0008]
---
## Objetivo
Un tipo especial de nodo, **Table**, que se renderiza en el viewport como
un rectangulo (no circulo) y agrupa visualmente N entidades del grafo. Cada
fila de la tabla = un nodo real del grafo (con su `type_ref`, sus fields,
sus tags). Las filas se pueden **extraer** (salen al canvas como nodos
sueltos) y **meter** (un nodo suelto entra como fila).
Distinto del issue 0004 (vista tabla global por tipo): ese es una **ventana
auxiliar** que tabula entidades existentes; este es un **nodo en el grafo**
que existe en `entities` y posee filas via relaciones.
## Modelo de datos
- El nodo tabla es una entidad normal con `type_ref = 'Table'` y metadata:
```json
{
"row_type": "Person", // tipo esperado de las filas (puede ser vacio = mixto)
"columns": ["name","age","email"], // subset de fields del row_type a mostrar como columnas
"expanded": true // estado de UI persistido
}
```
- La pertenencia se modela con relaciones:
- `name = "CONTAINS_ROW"`, `from_entity = <table_id>`, `to_entity = <row_entity_id>`, `order = N`.
- Una fila puede pertenecer a varias tablas (varias relaciones `CONTAINS_ROW` apuntando al mismo nodo). Confirmado en la conversacion.
- Las columnas pueden ser fijas (`row_type` definido → columnas = subset de los `fields` de ese tipo) o libres (definidas por el creador de la tabla en `columns`).
## Render en viewport
- Forma: `square` o `rounded_square` con tamano dependiente del numero de filas (clamp a min/max).
- Cuando esta **colapsada**: caja con titulo + contador (`Table · 23 filas`).
- Cuando esta **expandida**: caja crece y dibuja un grid interno (filas x columnas) con los valores principales. Las filas son arrastrables individualmente.
- Las relaciones `CONTAINS_ROW` no se dibujan como aristas normales (serian ruido visual). En su lugar, una fila extraida muestra una arista fina punteada hacia su tabla de origen.
- Aristas entrantes/salientes del nodo tabla se dibujan al borde del rectangulo, no al centro.
## Operaciones
- **Crear tabla**: en context menu del viewport, "New table here". Pide `row_type` opcional. Crea entidad `Table` y la posiciona donde el click.
- **Anadir fila** (tabla expandida o seleccionada): boton "+ row" que crea una entidad nueva con `type_ref = row_type` (si esta definido) y la engancha via `CONTAINS_ROW`.
- **Extraer fila**: borra la relacion `CONTAINS_ROW`. La fila queda como nodo libre, posicionada al lado de la tabla.
- **Extraer multiples**: shift+click en filas dentro de la tabla expandida, "Extract selected".
- **Meter fila**: drag de un nodo sobre el rectangulo de una tabla. Confirm dialog si su `type_ref` no coincide con `row_type` de la tabla.
- **Editar fila**: doble-click en fila → abre Inspector con esa entidad seleccionada.
## Cambios en codigo
- `entity_ops`:
- `bool table_create(db_path, name, row_type, columns_csv, char* out_id)`.
- `bool table_add_row(db_path, table_id, char* out_row_id)` (crea entidad + relacion CONTAINS_ROW).
- `bool table_extract_row(db_path, table_id, row_id)` (borra solo la relacion).
- `bool table_attach_row(db_path, table_id, row_id, int order)`.
- `bool table_list_rows(db_path, table_id, vector<string>* out_row_ids)`.
- Renderer del viewport (`graph_viewport.cpp` y/o `graph_renderer`): branch para `is_table_node` (detectado por `type_ref == "Table"`) que dibuja rectangulo + grid expandido y devuelve hit-testing por filas individuales.
- `graph_load_from_operations`: filtrar las aristas `CONTAINS_ROW` para que no entren en el layout fisico (no aplican fuerzas).
## Definicion de hecho
- Crear tabla, anadir filas, extraer y meter filas funciona round-trip via SQLite.
- Tabla colapsada y expandida se renderizan correctamente en el viewport.
- Doble-click en fila enfoca el Inspector con esa entidad.
- Una fila puede pertenecer a varias tablas sin duplicarse.
- Borrar la tabla pregunta: "borrar tabla y todas sus filas" o "extraer filas y borrar solo la tabla".