Commit Graph

32 Commits

Author SHA1 Message Date
egutierrez 2ce7672b9a merge: issue/0010-tablenode-duckdb — DuckDB foundation + render colapsado 2026-05-01 01:24:56 +02:00
egutierrez 082008bc00 feat(table-node): DuckDB foundation + render colapsado (issue 0010)
- tableview.{h,cpp}: capa C sobre DuckDB v1.1.3.
  * tableview_smoke_test (SELECT 42).
  * tableview_count (con sql_filter opcional).
  * tableview_page (LEFT JOIN sobre ops.entities via ATTACH para flag promoted).
  * tableview_create (inserta entidad type_ref='Table' con metadata pointer).
  * tableview_refresh_counts (lee Table entities, count cada DuckDB y cachea
    por user_data hash).
  * tableview_resolve_path (rel a dirname(ops_db) o absoluto).
- AppState::table_node_counts cache, refrescado tras load_input y mutaciones.
- views_table_overlay: rectangulo redondeado overlay ("Table  N") encima
  de cada nodo type_ref='Table'. Sigue camara via cam_x/cam_y/zoom.
- main.cpp:
  * --test-duckdb <path> smoke (SELECT 42).
  * --test-tableview <path> bulk test (1M rows count + page offset).
  * Refresh de counts tras load + reload_after_mutation.
  * Llamada a views_table_overlay despues de graph_labels_draw.
- CMakeLists.txt: link DuckDB::DuckDB + duckdb_copy_runtime.

Smoke tests:
- 1M rows count + page(offset=500k, limit=10) en 0.65 s end-to-end.
- Operations.db con un nodo Table apuntando a duckdb 1M filas: refresh
  reporta correctamente "1 tables, 1000000 total rows".
2026-05-01 01:24:43 +02:00
egutierrez 20d8bbf360 docs(issues): rewrite 0010 con DuckDB + new 0011 (UI fase 2)
0010 cambia de modelo SQLite CONTAINS_ROW a tier DuckDB:
operations.db sigue con grafo + filas promovidas, tablas grandes
viven en projects/<proj>/apps/graph_explorer/tables/<slug>.duckdb.
0011 separa la fase 2 (UI expandida + promote/demote + ingesta CSV).
2026-05-01 01:14:52 +02:00
egutierrez ea90f67298 merge: issue/0004-table-view — vista tabla 2026-05-01 01:05:06 +02:00
egutierrez 84afa4ce70 feat(table): vista tabla por tipo de entidad (issue 0004)
- 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.
2026-05-01 01:05:03 +02:00
egutierrez 078947a2b8 merge: issue/0007-type-editor-panel — Type Editor panel 2026-05-01 00:42:34 +02:00
egutierrez f80348d604 feat(types): Type Editor panel — CRUD de tipos en vivo (issue 0007)
- 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.
2026-05-01 00:42:30 +02:00
egutierrez 69f1afcf9e merge: issue/0009-tag-filter-fts-search — filtro tags + FTS5 2026-05-01 00:23:29 +02:00
egutierrez 54aba71bf5 feat(filter): tag chips + FTS5 search en toolbar (issue 0009)
- entity_ops: entity_search_fts (bm25, prefix tokens) + entity_list_by_tags (AND).
- AppState: filter_query_buf, filter_tags, filter_mode (highlight/hide), hits cache.
- views_filter_apply: combina tipos visibles + match-set (FTS query AND tags),
  highlight = color_override con alpha=0x40, hide = clear NF_VISIBLE.
- toolbar: input search + dropdown (max 20, click centra y selecciona),
  chips de tags con boton X, input para anadir tag, combo Highlight/Hide,
  Clear filter.
- Inspector: right-click sobre tag chip lo anade al filtro.
- main.cpp: reapply en cada frame si filter_dirty; cam_x/y al focus_target.
2026-05-01 00:23:26 +02:00
egutierrez e49cef5cb3 merge: issue/0008-inspector-editable — Inspector editable
Editar name/type/description/status, fields tipados (string/int/float/
bool/date/url/enum) renderizados desde el schema del tipo, extras
key-value libres, tags como chips con autocomplete por la BD.
Save persiste con un solo UPDATE y dispara reload del grafo.

Cierra issue 0008.
2026-05-01 00:13:19 +02:00
egutierrez 1fbd756583 docs(issues): close 0008 — inspector editable
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
2026-05-01 00:13:19 +02:00
egutierrez 6560e358bf feat(main): wire Inspector — load on selection, Save/Discard, refresh caches
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>
2026-05-01 00:13:08 +02:00
egutierrez b2ae793727 feat(views): Inspector editable — identidad, fields tipados, extras, tags
Issue 0008 — refactor del panel Inspector de read-only a editable.

views.h:
- AppState gana ParsedTypes parsed_types (schema vivo del proyecto), draft
  del Inspector (insp_*: name/type/desc/status buffers, field_keys/values
  paralelas, is_extra mask, tags vector, dirty flag), y dos triggers
  (want_inspector_save, want_inspector_discard).
- Helpers expuestos: views_inspector_clear_draft, _refresh_caches,
  _load_draft, _build_record.

views.cpp:
- views_inspector_load_draft: entity_load_full → buffers; campos del
  schema primero (orden del EntitySpec), extras detras.
- views_inspector_build_record: reconstruye EntityRecord respetando el
  schema para decidir is_string de cada campo (FK_BOOL → 'true'/'false',
  FK_INT/FLOAT → literal, resto → string). Extras siempre string.
- views_inspector: render por bloques:
  * Identity: name, type combo (lista del proyecto + tipos del grafo),
    status combo, description multiline.
  * Fields del schema: render por kind (string→InputText con hint,
    int→InputInt, float→InputDouble, bool→Checkbox, date→InputText
    con hint YYYY-MM-DD, url→InputText + boton Open en navegador,
    enum→Combo con values). Required marcado con '*'.
  * Extras: lista key-value con boton trash por fila + 'Add' al final.
  * Tags: chips clickables (click = quitar) + input con autocomplete
    (lista compacta de tags distintas en BD).
  * Footer: Save/Discard/Open notes + label '(modified)' si dirty.
  * Neighbors read-only (igual que antes).
- Si el draft no esta sincronizado con la seleccion actual y NO hay
  cambios pendientes, el inspector muestra 'Cargando...' (main.cpp
  carga). Si hay dirty, banner 'Save/Discard primero' bloqueando.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
2026-05-01 00:13:01 +02:00
egutierrez 6ed00fcdce feat(entity_ops): EntityRecord + JSON helpers + load/update/list_tags
Issue 0008 — capa de datos para el Inspector editable:
- struct MetadataField {key, value_str, is_string} — pares de la
  columna metadata. is_string distingue '"foo"' de literal (number,
  bool). EntityRecord agrupa los campos editables (id, name, type_ref,
  description, status, tags[], metadata[]).
- entity_load_full: SELECT name/type/desc/status/tags/metadata, parsea
  JSON plano con un parser propio (evita arrastrar libs). Soporta
  escapes basicos (\n \t \" \\\\ etc.; \uXXXX → '?').
- entity_update: un solo UPDATE con tags+metadata serializados a JSON.
  Toca updated_at.
- entity_list_distinct_tags: usa json_each (SQLITE_ENABLE_JSON1) para
  enumerar tags distintas — autocomplete del Inspector.
- Parser JSON plano: parse_string_array, parse_flat_object. Solo
  objetos planos (sin nested objects/arrays excepto consumirlos como
  literal). Suficiente para el caso del Inspector.
- Writer JSON: build_string_array, build_flat_object con escape
  apropiado. Si is_string=false pero el valor no es literal valido,
  se re-emite como string para no producir JSON invalido.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
2026-05-01 00:12:44 +02:00
egutierrez 5b1a1bea41 merge: issue/0005-type-schema-fields — schema de fields en types.yaml
Parser+writer para fields (string/int/float/bool/date/url/enum),
principal_field y round-trip estable. Semilla con 44 fields en
11 tipos. CLI --test-types-yaml para verificar round-trip.

Cierra issue 0005.
2026-05-01 00:01:02 +02:00
egutierrez d26449af64 docs(issues): close 0005 — type schema fields
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
2026-05-01 00:01:02 +02:00
egutierrez fb7538088b feat(types): semilla con fields para 11 tipos + CLI --test-types-yaml
- 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>
2026-05-01 00:00:53 +02:00
egutierrez 8a36ad068a feat(types): schema en types.yaml — fields, principal_field, parser+writer
Issue 0005:
- types_registry.h: enum FieldKind {string,int,float,bool,date,url,enum},
  struct FieldSpec {name,kind,required,enum_values}. EntitySpec gana
  principal_field, fields[], icon_name (para round-trip exacto).
- Parser: tolerante con yaml antiguo (sin fields). Soporta sub-key
  'fields:' como lista multilinea de inline-maps con bracket-aware
  split (respeta [a,b,c] sin partir). Soporta 'principal_field:'.
  Tipos desconocidos → FK_STRING con warning.
- Writer types_save_yaml: emite formato compacto (un dict por entity,
  fields como inline-maps, color en #RRGGBB[AA], icon por nombre).
  Round-trip estable: load→save→load produce ParsedTypes identico.

apply_types_yaml sigue ignorando fields (eso lo consumen 0007/0008).

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
2026-05-01 00:00:44 +02:00
egutierrez 396a974686 merge: quick/shape-circle-energy-cap — circulos por defecto + tapa energia 2026-04-30 23:53:21 +02:00
egutierrez f0148ac368 fix(viz): forzar circulo a todos los nodos salvo Table; tapar energia inicial
- 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>
2026-04-30 23:53:21 +02:00
egutierrez 58e835cea6 merge: issue/0006-projects-subfolders — sistema de proyectos dentro de la app
Cada proyecto es una subcarpeta junto al exe con su operations.db,
types.yaml y graph_explorer.db. Switcher en la toolbar con New / Open /
Recent / Reveal. Migracion automatica del layout legacy a projects/default/.

Cierra issue 0006.
2026-04-30 23:47:08 +02:00
egutierrez a3b5a2cd80 docs(issues): close 0006 — projects subfolders
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
2026-04-30 23:47:08 +02:00
egutierrez d6f7318c24 feat(projects): integrate project switcher in app shell + Inspector menu
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>
2026-04-30 23:46:47 +02:00
egutierrez c365b1bc43 feat(projects): project_manager module — DDL bootstrap, slug/paths, settings, reveal
Modulo nuevo que gestiona el sistema de proyectos del issue 0006.
Cada proyecto vive como subcarpeta junto al exe con su operations.db,
types.yaml y graph_explorer.db propios. Helpers:

- project_validate_slug / project_paths / project_list / project_exists
- project_create — bootstrap operations.db con DDL completo (entities,
  relations, fts5, triggers, assertions, executions, logs) + types.yaml
  semilla (copia de examples/types.yaml o embed si no existe).
- projects_migrate_legacy_layout — mueve operations.db / graph_explorer.db
  del cwd a projects/default/ si el directorio projects/ no existe.
- project_settings_load/save/touch — graph_explorer.ini con last_active
  y recent (max 5).
- project_reveal_in_explorer — Windows ShellExecute / Linux xdg-open.

CMakeLists registra project_manager.cpp en add_imgui_app.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
2026-04-30 23:46:35 +02:00
egutierrez 491161204e 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>
2026-04-30 23:40:14 +02:00
egutierrez ccaa76455d merge: iter2 — fixes (docking/hover/conflict) + reset layout + spread + markdown notes 2026-04-30 23:11:52 +02:00
egutierrez adde3026ea fix: docking gaps + hover radius + node spread + markdown notes
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.
2026-04-30 23:11:48 +02:00
egutierrez 02eef6e339 merge: quick/iter1 — docking + add-node + context menu + issues 2026-04-30 22:55:34 +02:00
egutierrez a36530bb6f feat: docking host + add-node toolbar + node context menu
- 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
2026-04-30 22:55:30 +02:00
egutierrez cc43f6fdd6 merge: issue/0049k-graph-explorer-app — graph_explorer app
Cierra issue 0049k. Activa feature flag osint_graph_v1 = true (en fn_registry).
2026-04-30 00:14:03 +02:00
egutierrez b767b5b85e feat: graph_explorer app — agnostic operations.db viewer (issue 0049k)
App C++ ImGui que abre cualquier operations.db del registry y lo visualiza
como grafo con shapes/iconos/layouts/filtros/labels.

Composicion del registry:
- viz/graph_renderer + graph_force_layout(_gpu) + graph_layouts +
  graph_viewport + graph_labels + graph_icons + graph_sources
- core: toolbar, modal_dialog, select, text_input, tree_view, page_header,
  fullscreen_window, button, badge, empty_state

Capas:
- data.{h,cpp}    — dispatcher GraphLoadFn (operations hoy; json/graphml manana).
- types_registry.{h,cpp} — parser YAML minimal + tabler_codepoint_by_name +
  apply_types_yaml + IconAtlas builder.
- views.{h,cpp}   — Toolbar, Legend, Inspector, Stats, modal Filters/Open.
- layout_store.{h,cpp} — graph_explorer.db SQLite con tabla layouts(graph_hash,
  node_id, x, y, pinned, updated_at). UPSERT por nodo.
- main.cpp        — CLI (--input/--types/--layout) + fn::run_app + bucle
  force layout (CPU/GPU toggle) + render con 3 columnas (Legend / Viewport /
  Inspector+Stats).

examples/types.yaml: 10 entidades OSINT (Person/Email/Domain/Phone/Org/IBAN/
Account/Document/Address/Url) + 5 relaciones (owns/knows/located_in/
transfers_to/member_of) con shapes Tabler reales.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
2026-04-30 00:13:59 +02:00
egutierrez 11bf6f94cd chore: initial empty commit (bootstrap from issue 0049a) 2026-04-29 21:06:20 +02:00