Merge issue/0036a-rename-nodegroups
This commit is contained in:
+1
-1
@@ -23,7 +23,7 @@ add_imgui_app(graph_explorer
|
|||||||
layout_store.cpp
|
layout_store.cpp
|
||||||
entity_ops.cpp
|
entity_ops.cpp
|
||||||
project_manager.cpp
|
project_manager.cpp
|
||||||
tableview.cpp
|
node_groups.cpp
|
||||||
jobs.cpp
|
jobs.cpp
|
||||||
enrichers.cpp
|
enrichers.cpp
|
||||||
chat.cpp
|
chat.cpp
|
||||||
|
|||||||
@@ -429,7 +429,7 @@ def cmd_table_list(args) -> None:
|
|||||||
|
|
||||||
|
|
||||||
def cmd_table_promote(args) -> None:
|
def cmd_table_promote(args) -> None:
|
||||||
"""Promociona una fila DuckDB a entidad. Replica tableview_promote_row."""
|
"""Promociona una fila DuckDB a entidad. Replica node_groups_promote_row."""
|
||||||
cn = _connect(_ops_db())
|
cn = _connect(_ops_db())
|
||||||
try:
|
try:
|
||||||
row = cn.execute(
|
row = cn.execute(
|
||||||
|
|||||||
@@ -0,0 +1,93 @@
|
|||||||
|
---
|
||||||
|
id: 0036a
|
||||||
|
title: Rename Table-expanded -> NodeGroups (paperwork, sin cambio funcional)
|
||||||
|
status: done
|
||||||
|
priority: high
|
||||||
|
created: 2026-05-04
|
||||||
|
parent: 0036
|
||||||
|
---
|
||||||
|
|
||||||
|
## Objetivo
|
||||||
|
|
||||||
|
Cambio puramente de nombres. Cero cambio de comportamiento. Bloquea
|
||||||
|
los issues siguientes (0036b-f) que ya asumen la nueva convencion.
|
||||||
|
|
||||||
|
## Cambios
|
||||||
|
|
||||||
|
### Archivos renombrados
|
||||||
|
|
||||||
|
- `tableview.cpp` -> `node_groups.cpp`
|
||||||
|
- `tableview.h` -> `node_groups.h`
|
||||||
|
|
||||||
|
### Tipos (en el sub-repo del app)
|
||||||
|
|
||||||
|
| Antes | Despues |
|
||||||
|
|---|---|
|
||||||
|
| `TableWindowState` (en `views.h`) | `NodeGroupsWindowState` |
|
||||||
|
| `TableMetadata` (en `tableview.h`) | `NodeGroupsMeta` |
|
||||||
|
| `TablePageRow` (en `tableview.h`) | `NodeGroupsRow` |
|
||||||
|
|
||||||
|
### Campos AppState
|
||||||
|
|
||||||
|
| Antes | Despues |
|
||||||
|
|---|---|
|
||||||
|
| `app.table_windows` | `app.node_groups_windows` |
|
||||||
|
| `app.table_node_counts` | `app.node_groups_counts` |
|
||||||
|
| `app.toggle_expanded_id` | `app.toggle_nodegroups_id` |
|
||||||
|
| `app.want_toggle_expanded` | `app.want_toggle_nodegroups` |
|
||||||
|
| `app.want_promote_row` (relacionado) | dejar igual por ahora — es promote de DuckDB, lo retocaremos en 0036d |
|
||||||
|
|
||||||
|
### Funciones y prefijos
|
||||||
|
|
||||||
|
| Antes | Despues |
|
||||||
|
|---|---|
|
||||||
|
| `tableview_load_metadata` | `node_groups_load_metadata` |
|
||||||
|
| `tableview_page_rows` | `node_groups_page_rows` |
|
||||||
|
| `tableview_refresh_counts` | `node_groups_refresh_counts` |
|
||||||
|
| Cualquier `tableview_*` no relacionado al panel generico `Table` | `node_groups_*` |
|
||||||
|
|
||||||
|
**OJO**: el panel generico `Table` (`views_table` en `views.cpp`) NO se
|
||||||
|
renombra. Es la lista global de entidades, no la window por contenedor.
|
||||||
|
Lo afectado es solo lo que abre el "expand" de un nodo Table-typed
|
||||||
|
(hoy) y lo que abrira el "drill" de un Group (mañana, 0036c).
|
||||||
|
|
||||||
|
### CMakeLists.txt
|
||||||
|
|
||||||
|
Actualizar `apps/graph_explorer/CMakeLists.txt` para usar el nombre
|
||||||
|
nuevo `node_groups.cpp` en lugar de `tableview.cpp`.
|
||||||
|
|
||||||
|
### Strings de UI
|
||||||
|
|
||||||
|
| Antes | Despues |
|
||||||
|
|---|---|
|
||||||
|
| Window title `"Table: <name>"` | `"NodeGroups: <name>"` |
|
||||||
|
| `"Expand table"` (context menu) | `"Open NodeGroups"` |
|
||||||
|
| `"Collapse table"` | `"Close NodeGroups"` |
|
||||||
|
| Mensajes de logs `[tableview]` | `[node_groups]` |
|
||||||
|
|
||||||
|
### Comentarios y docs
|
||||||
|
|
||||||
|
Buscar y reemplazar referencias a "Table-expanded", "Table expanded",
|
||||||
|
"tabla expandida" en comentarios. NO tocar los issues completados que
|
||||||
|
documentan la historia (0010, 0011) — son archivo historico.
|
||||||
|
|
||||||
|
## Acceptance criteria
|
||||||
|
|
||||||
|
- Build C++ verde sin warnings nuevos.
|
||||||
|
- pytest verde en WSL y Windows.
|
||||||
|
- Smoke test manual: abrir un Table existente con right-click "Open
|
||||||
|
NodeGroups" → la window se abre como antes, mismo contenido,
|
||||||
|
mismo titulo "NodeGroups: <name>".
|
||||||
|
- `git grep -i "tableview\|TableWindow\|TablePageRow\|TableMetadata"`
|
||||||
|
solo encuentra ocurrencias en `issues/completed/` (historia) y en
|
||||||
|
el panel generico `Table` (que NO se toca).
|
||||||
|
|
||||||
|
## TBD
|
||||||
|
|
||||||
|
Branch `issue/0036a-rename-nodegroups`, merge `--no-ff` a master.
|
||||||
|
|
||||||
|
## Out of scope
|
||||||
|
|
||||||
|
- Cualquier cambio funcional (eso es 0036b y siguientes).
|
||||||
|
- Renombrar el panel generico `Table` (queda como esta).
|
||||||
|
- Tocar la lista de issues completados.
|
||||||
@@ -33,7 +33,7 @@
|
|||||||
|
|
||||||
#include "../../../../cpp/vendor/sqlite3/sqlite3.h"
|
#include "../../../../cpp/vendor/sqlite3/sqlite3.h"
|
||||||
|
|
||||||
#include "tableview.h"
|
#include "node_groups.h"
|
||||||
#include "duckdb.h"
|
#include "duckdb.h"
|
||||||
|
|
||||||
#include <cstdio>
|
#include <cstdio>
|
||||||
@@ -707,15 +707,15 @@ static bool load_input(bool first_load) {
|
|||||||
|
|
||||||
// Cache de conteos de Table nodes (issue 0010).
|
// Cache de conteos de Table nodes (issue 0010).
|
||||||
if (g_input.uri) {
|
if (g_input.uri) {
|
||||||
ge::tableview_refresh_counts(g_input.uri, &g_app.table_node_counts);
|
ge::node_groups_refresh_counts(g_input.uri, &g_app.node_groups_counts);
|
||||||
int64_t total_rows = 0;
|
int64_t total_rows = 0;
|
||||||
for (auto& kv : g_app.table_node_counts) total_rows += kv.second;
|
for (auto& kv : g_app.node_groups_counts) total_rows += kv.second;
|
||||||
std::fprintf(stdout,
|
std::fprintf(stdout,
|
||||||
"[graph_explorer] table counts refreshed: %zu tables, %lld total rows\n",
|
"[graph_explorer] table counts refreshed: %zu tables, %lld total rows\n",
|
||||||
g_app.table_node_counts.size(), (long long)total_rows);
|
g_app.node_groups_counts.size(), (long long)total_rows);
|
||||||
// Sync de windows expandidas (issue 0011) — reabre las que el
|
// Sync de windows expandidas (issue 0011) — reabre las que el
|
||||||
// usuario tenia abiertas en la sesion previa (metadata.expanded=true).
|
// usuario tenia abiertas en la sesion previa (metadata.expanded=true).
|
||||||
ge::views_table_windows_sync(g_app, g_input.uri);
|
ge::views_node_groups_windows_sync(g_app, g_input.uri);
|
||||||
}
|
}
|
||||||
|
|
||||||
// Cache de la vista tabla (issue 0004) — pull bulk + neighbors desde grafo.
|
// Cache de la vista tabla (issue 0004) — pull bulk + neighbors desde grafo.
|
||||||
@@ -882,15 +882,15 @@ static void render_context_menu() {
|
|||||||
const char* sql_id = ge::entity_index_lookup(g_idx, n.user_data);
|
const char* sql_id = ge::entity_index_lookup(g_idx, n.user_data);
|
||||||
|
|
||||||
if (is_table && sql_id) {
|
if (is_table && sql_id) {
|
||||||
// Determinar estado expanded actual sin ir a BD: mira table_windows.
|
// Determinar estado actual sin ir a BD: mira node_groups_windows.
|
||||||
bool currently_expanded =
|
bool currently_open =
|
||||||
g_app.table_windows.find(sql_id) != g_app.table_windows.end();
|
g_app.node_groups_windows.find(sql_id) != g_app.node_groups_windows.end();
|
||||||
const char* lbl_exp = currently_expanded
|
const char* lbl_exp = currently_open
|
||||||
? TI_X " Collapse table"
|
? TI_X " Close NodeGroups"
|
||||||
: TI_TABLE " Expand table";
|
: TI_TABLE " Open NodeGroups";
|
||||||
if (ImGui::MenuItem(lbl_exp)) {
|
if (ImGui::MenuItem(lbl_exp)) {
|
||||||
g_app.want_toggle_expanded = true;
|
g_app.want_toggle_nodegroups = true;
|
||||||
g_app.toggle_expanded_id = sql_id;
|
g_app.toggle_nodegroups_id = sql_id;
|
||||||
}
|
}
|
||||||
ImGui::Separator();
|
ImGui::Separator();
|
||||||
}
|
}
|
||||||
@@ -1580,7 +1580,7 @@ static void render() {
|
|||||||
|
|
||||||
// Reaplica types.yaml + atlas. Sin esto, despues de cualquier
|
// Reaplica types.yaml + atlas. Sin esto, despues de cualquier
|
||||||
// mutacion los tipos pierden color/shape/icon (todo nodo vuelve a
|
// mutacion los tipos pierden color/shape/icon (todo nodo vuelve a
|
||||||
// circulo gris). Issue: al promover desde tableview el Table
|
// circulo gris). Issue: al promover desde node_groups el Table
|
||||||
// dejaba de ser cuadrado.
|
// dejaba de ser cuadrado.
|
||||||
if (!g_app.parsed_types.entities.empty() ||
|
if (!g_app.parsed_types.entities.empty() ||
|
||||||
!g_app.parsed_types.relations.empty()) {
|
!g_app.parsed_types.relations.empty()) {
|
||||||
@@ -1592,10 +1592,10 @@ static void render() {
|
|||||||
}
|
}
|
||||||
|
|
||||||
// Refresh Table node counts (issue 0010).
|
// Refresh Table node counts (issue 0010).
|
||||||
ge::tableview_refresh_counts(g_input.uri, &g_app.table_node_counts);
|
ge::node_groups_refresh_counts(g_input.uri, &g_app.node_groups_counts);
|
||||||
|
|
||||||
// Sincroniza windows (issue 0011) por si una Table aparecio o desaparecio.
|
// Sincroniza windows (issue 0011) por si una Table aparecio o desaparecio.
|
||||||
ge::views_table_windows_sync(g_app, g_input.uri);
|
ge::views_node_groups_windows_sync(g_app, g_input.uri);
|
||||||
|
|
||||||
// Refresh table cache (issue 0004).
|
// Refresh table cache (issue 0004).
|
||||||
std::vector<ge::EntityRowSnapshot> snap;
|
std::vector<ge::EntityRowSnapshot> snap;
|
||||||
@@ -1688,30 +1688,30 @@ static void render() {
|
|||||||
}
|
}
|
||||||
|
|
||||||
// ---- Table node UI fase 2 (issue 0011) ----
|
// ---- Table node UI fase 2 (issue 0011) ----
|
||||||
if (g_app.want_toggle_expanded && !g_app.toggle_expanded_id.empty()
|
if (g_app.want_toggle_nodegroups && !g_app.toggle_nodegroups_id.empty()
|
||||||
&& !g_input_path.empty()) {
|
&& !g_input_path.empty()) {
|
||||||
std::string id = g_app.toggle_expanded_id;
|
std::string id = g_app.toggle_nodegroups_id;
|
||||||
bool currently = g_app.table_windows.find(id) != g_app.table_windows.end();
|
bool currently = g_app.node_groups_windows.find(id) != g_app.node_groups_windows.end();
|
||||||
ge::tableview_set_expanded(g_input_path.c_str(), id.c_str(), !currently);
|
ge::node_groups_set_expanded(g_input_path.c_str(), id.c_str(), !currently);
|
||||||
ge::views_table_windows_sync(g_app, g_input_path.c_str());
|
ge::views_node_groups_windows_sync(g_app, g_input_path.c_str());
|
||||||
g_app.want_toggle_expanded = false;
|
g_app.want_toggle_nodegroups = false;
|
||||||
g_app.toggle_expanded_id.clear();
|
g_app.toggle_nodegroups_id.clear();
|
||||||
}
|
}
|
||||||
// Cierre via X de la ventana -> bajar expanded en BD.
|
// Cierre via X de la ventana -> bajar expanded en BD.
|
||||||
for (auto it = g_app.table_windows.begin(); it != g_app.table_windows.end(); ) {
|
for (auto it = g_app.node_groups_windows.begin(); it != g_app.node_groups_windows.end(); ) {
|
||||||
if (!it->second.open && !g_input_path.empty()) {
|
if (!it->second.open && !g_input_path.empty()) {
|
||||||
ge::tableview_set_expanded(g_input_path.c_str(),
|
ge::node_groups_set_expanded(g_input_path.c_str(),
|
||||||
it->first.c_str(), false);
|
it->first.c_str(), false);
|
||||||
it = g_app.table_windows.erase(it);
|
it = g_app.node_groups_windows.erase(it);
|
||||||
} else ++it;
|
} else ++it;
|
||||||
}
|
}
|
||||||
// Refrescar la pagina si alguna window esta dirty.
|
// Refrescar la pagina si alguna window esta dirty.
|
||||||
for (auto& kv : g_app.table_windows) {
|
for (auto& kv : g_app.node_groups_windows) {
|
||||||
auto& w = kv.second;
|
auto& w = kv.second;
|
||||||
if (!w.page_dirty) continue;
|
if (!w.page_dirty) continue;
|
||||||
const auto& m = w.meta;
|
const auto& m = w.meta;
|
||||||
w.last_error.clear();
|
w.last_error.clear();
|
||||||
bool ok_count = ge::tableview_count(m.duckdb_path_abs.c_str(),
|
bool ok_count = ge::node_groups_count(m.duckdb_path_abs.c_str(),
|
||||||
m.table_name.c_str(),
|
m.table_name.c_str(),
|
||||||
m.filter_sql.empty() ? nullptr : m.filter_sql.c_str(),
|
m.filter_sql.empty() ? nullptr : m.filter_sql.c_str(),
|
||||||
&w.total_rows);
|
&w.total_rows);
|
||||||
@@ -1725,14 +1725,14 @@ static void render() {
|
|||||||
}
|
}
|
||||||
if (m.columns.empty()) {
|
if (m.columns.empty()) {
|
||||||
std::vector<std::string> cols;
|
std::vector<std::string> cols;
|
||||||
if (ge::tableview_list_columns(m.duckdb_path_abs.c_str(),
|
if (ge::node_groups_list_columns(m.duckdb_path_abs.c_str(),
|
||||||
m.table_name.c_str(), &cols)) {
|
m.table_name.c_str(), &cols)) {
|
||||||
ge::tableview_set_columns(g_input_path.c_str(),
|
ge::node_groups_set_columns(g_input_path.c_str(),
|
||||||
m.entity_id.c_str(), cols);
|
m.entity_id.c_str(), cols);
|
||||||
w.meta.columns = cols;
|
w.meta.columns = cols;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
bool ok_page = ge::tableview_page(m.duckdb_path_abs.c_str(),
|
bool ok_page = ge::node_groups_page(m.duckdb_path_abs.c_str(),
|
||||||
m.table_name.c_str(), m.id_column.c_str(),
|
m.table_name.c_str(), m.id_column.c_str(),
|
||||||
w.meta.columns,
|
w.meta.columns,
|
||||||
m.filter_sql.empty() ? nullptr : m.filter_sql.c_str(),
|
m.filter_sql.empty() ? nullptr : m.filter_sql.c_str(),
|
||||||
@@ -1749,11 +1749,11 @@ static void render() {
|
|||||||
}
|
}
|
||||||
if (g_app.want_promote_row && !g_app.promote_table_id.empty()
|
if (g_app.want_promote_row && !g_app.promote_table_id.empty()
|
||||||
&& !g_input_path.empty()) {
|
&& !g_input_path.empty()) {
|
||||||
ge::TableMetadata m;
|
ge::NodeGroupsMeta m;
|
||||||
if (ge::tableview_get_metadata(g_input_path.c_str(),
|
if (ge::node_groups_get_metadata(g_input_path.c_str(),
|
||||||
g_app.promote_table_id.c_str(), &m)) {
|
g_app.promote_table_id.c_str(), &m)) {
|
||||||
char new_id[128] = {};
|
char new_id[128] = {};
|
||||||
if (ge::tableview_promote_row(g_input_path.c_str(),
|
if (ge::node_groups_promote_row(g_input_path.c_str(),
|
||||||
g_app.promote_table_id.c_str(),
|
g_app.promote_table_id.c_str(),
|
||||||
m.duckdb_path_abs.c_str(),
|
m.duckdb_path_abs.c_str(),
|
||||||
m.table_name.c_str(),
|
m.table_name.c_str(),
|
||||||
@@ -1763,8 +1763,8 @@ static void render() {
|
|||||||
new_id, sizeof(new_id))) {
|
new_id, sizeof(new_id))) {
|
||||||
std::fprintf(stdout, "[promote] %s -> %s\n",
|
std::fprintf(stdout, "[promote] %s -> %s\n",
|
||||||
g_app.promote_row_id.c_str(), new_id);
|
g_app.promote_row_id.c_str(), new_id);
|
||||||
auto it = g_app.table_windows.find(g_app.promote_table_id);
|
auto it = g_app.node_groups_windows.find(g_app.promote_table_id);
|
||||||
if (it != g_app.table_windows.end()) it->second.page_dirty = true;
|
if (it != g_app.node_groups_windows.end()) it->second.page_dirty = true;
|
||||||
reload_after_mutation();
|
reload_after_mutation();
|
||||||
g_app.want_focus_entity = true;
|
g_app.want_focus_entity = true;
|
||||||
g_app.focus_entity_id = new_id;
|
g_app.focus_entity_id = new_id;
|
||||||
@@ -1776,10 +1776,10 @@ static void render() {
|
|||||||
}
|
}
|
||||||
if (g_app.want_demote_entity && !g_app.demote_entity_id.empty()
|
if (g_app.want_demote_entity && !g_app.demote_entity_id.empty()
|
||||||
&& !g_input_path.empty()) {
|
&& !g_input_path.empty()) {
|
||||||
if (ge::tableview_demote_row(g_input_path.c_str(),
|
if (ge::node_groups_demote_row(g_input_path.c_str(),
|
||||||
g_app.demote_entity_id.c_str())) {
|
g_app.demote_entity_id.c_str())) {
|
||||||
std::fprintf(stdout, "[demote] %s\n", g_app.demote_entity_id.c_str());
|
std::fprintf(stdout, "[demote] %s\n", g_app.demote_entity_id.c_str());
|
||||||
for (auto& kv : g_app.table_windows) kv.second.page_dirty = true;
|
for (auto& kv : g_app.node_groups_windows) kv.second.page_dirty = true;
|
||||||
reload_after_mutation();
|
reload_after_mutation();
|
||||||
}
|
}
|
||||||
g_app.want_demote_entity = false;
|
g_app.want_demote_entity = false;
|
||||||
@@ -1806,17 +1806,17 @@ static void render() {
|
|||||||
if (g_app.want_import) {
|
if (g_app.want_import) {
|
||||||
g_app.want_import = false;
|
g_app.want_import = false;
|
||||||
g_app.import_error.clear();
|
g_app.import_error.clear();
|
||||||
std::string duck_abs = ge::tableview_resolve_path(
|
std::string duck_abs = ge::node_groups_resolve_path(
|
||||||
g_input_path.c_str(), g_app.import_duckdb_buf);
|
g_input_path.c_str(), g_app.import_duckdb_buf);
|
||||||
std::string err;
|
std::string err;
|
||||||
if (!ge::tableview_ingest_file(duck_abs.c_str(),
|
if (!ge::node_groups_ingest_file(duck_abs.c_str(),
|
||||||
g_app.import_path_buf,
|
g_app.import_path_buf,
|
||||||
g_app.import_table_buf,
|
g_app.import_table_buf,
|
||||||
ge::INGEST_AUTO, &err)) {
|
ge::INGEST_AUTO, &err)) {
|
||||||
g_app.import_error = "Ingest failed: " + err;
|
g_app.import_error = "Ingest failed: " + err;
|
||||||
} else {
|
} else {
|
||||||
char new_id[80] = {};
|
char new_id[80] = {};
|
||||||
if (ge::tableview_create(g_input_path.c_str(),
|
if (ge::node_groups_create(g_input_path.c_str(),
|
||||||
g_app.import_table_buf,
|
g_app.import_table_buf,
|
||||||
g_app.import_duckdb_buf,
|
g_app.import_duckdb_buf,
|
||||||
g_app.import_table_buf,
|
g_app.import_table_buf,
|
||||||
@@ -1923,7 +1923,7 @@ static void render() {
|
|||||||
if (type_name && std::strcmp(type_name, "Group") == 0) is_group = true;
|
if (type_name && std::strcmp(type_name, "Group") == 0) is_group = true;
|
||||||
|
|
||||||
if (is_group && sql_id) {
|
if (is_group && sql_id) {
|
||||||
// Drill-in: abrir tableview filtrada por group_id = sql_id.
|
// Drill-in: abrir Table panel filtrado por group_id = sql_id.
|
||||||
g_app.table_filter_group_id = sql_id;
|
g_app.table_filter_group_id = sql_id;
|
||||||
const char* lbl = graph::graph_label(&g_graph, g_graph.nodes[n].label_idx);
|
const char* lbl = graph::graph_label(&g_graph, g_graph.nodes[n].label_idx);
|
||||||
g_app.table_filter_group_name = lbl ? lbl : sql_id;
|
g_app.table_filter_group_name = lbl ? lbl : sql_id;
|
||||||
@@ -2007,7 +2007,7 @@ static void render() {
|
|||||||
&get_label_cb, nullptr);
|
&get_label_cb, nullptr);
|
||||||
}
|
}
|
||||||
// Table node overlay (issue 0010) — encima de las labels.
|
// Table node overlay (issue 0010) — encima de las labels.
|
||||||
ge::views_table_overlay(g_app);
|
ge::views_node_groups_overlay(g_app);
|
||||||
}
|
}
|
||||||
ImGui::End();
|
ImGui::End();
|
||||||
} else {
|
} else {
|
||||||
@@ -2051,7 +2051,7 @@ static void render() {
|
|||||||
ge::views_table(g_app);
|
ge::views_table(g_app);
|
||||||
|
|
||||||
// Table node windows (issue 0011) — una por Table expandida.
|
// Table node windows (issue 0011) — una por Table expandida.
|
||||||
ge::views_table_window(g_app);
|
ge::views_node_groups_window(g_app);
|
||||||
ge::views_import_dataset_modal(g_app);
|
ge::views_import_dataset_modal(g_app);
|
||||||
|
|
||||||
// Jobs panel (issue 0026) — flotante, dockeable.
|
// Jobs panel (issue 0026) — flotante, dockeable.
|
||||||
@@ -2181,7 +2181,7 @@ int main(int argc, char** argv) {
|
|||||||
return test_types_yaml_roundtrip(argv[++i]);
|
return test_types_yaml_roundtrip(argv[++i]);
|
||||||
} else if (std::strcmp(a, "--test-duckdb") == 0 && i + 1 < argc) {
|
} else if (std::strcmp(a, "--test-duckdb") == 0 && i + 1 < argc) {
|
||||||
const char* p = argv[++i];
|
const char* p = argv[++i];
|
||||||
if (!ge::tableview_smoke_test(p)) {
|
if (!ge::node_groups_smoke_test(p)) {
|
||||||
std::fprintf(stderr, "[duckdb] smoke test FAILED for %s\n", p);
|
std::fprintf(stderr, "[duckdb] smoke test FAILED for %s\n", p);
|
||||||
return 2;
|
return 2;
|
||||||
}
|
}
|
||||||
@@ -2209,25 +2209,25 @@ int main(int argc, char** argv) {
|
|||||||
duckdb_disconnect(&cn); duckdb_close(&db);
|
duckdb_disconnect(&cn); duckdb_close(&db);
|
||||||
|
|
||||||
int64_t total = 0;
|
int64_t total = 0;
|
||||||
if (!ge::tableview_count(p, "people", nullptr, &total) || total != 1000000) {
|
if (!ge::node_groups_count(p, "people", nullptr, &total) || total != 1000000) {
|
||||||
std::fprintf(stderr, "[tableview_count] expected 1000000, got %lld\n",
|
std::fprintf(stderr, "[node_groups_count] expected 1000000, got %lld\n",
|
||||||
(long long)total);
|
(long long)total);
|
||||||
return 2;
|
return 2;
|
||||||
}
|
}
|
||||||
std::vector<std::string> cols = { "name", "age" };
|
std::vector<std::string> cols = { "name", "age" };
|
||||||
std::vector<ge::TablePageRow> page;
|
std::vector<ge::NodeGroupsRow> page;
|
||||||
if (!ge::tableview_page(p, "people", "id", cols, nullptr,
|
if (!ge::node_groups_page(p, "people", "id", cols, nullptr,
|
||||||
nullptr, nullptr, 500000, 10, &page)) {
|
nullptr, nullptr, 500000, 10, &page)) {
|
||||||
std::fprintf(stderr, "[tableview_page] failed\n");
|
std::fprintf(stderr, "[node_groups_page] failed\n");
|
||||||
return 2;
|
return 2;
|
||||||
}
|
}
|
||||||
if (page.size() != 10) {
|
if (page.size() != 10) {
|
||||||
std::fprintf(stderr, "[tableview_page] expected 10 rows, got %zu\n",
|
std::fprintf(stderr, "[node_groups_page] expected 10 rows, got %zu\n",
|
||||||
page.size());
|
page.size());
|
||||||
return 2;
|
return 2;
|
||||||
}
|
}
|
||||||
std::fprintf(stdout,
|
std::fprintf(stdout,
|
||||||
"[tableview] OK — count=%lld, page[0]={id=%s, name=%s, age=%s}\n",
|
"[node_groups] OK — count=%lld, page[0]={id=%s, name=%s, age=%s}\n",
|
||||||
(long long)total, page[0].id.c_str(),
|
(long long)total, page[0].id.c_str(),
|
||||||
page[0].values.size() > 0 ? page[0].values[0].c_str() : "",
|
page[0].values.size() > 0 ? page[0].values[0].c_str() : "",
|
||||||
page[0].values.size() > 1 ? page[0].values[1].c_str() : "");
|
page[0].values.size() > 1 ? page[0].values[1].c_str() : "");
|
||||||
|
|||||||
@@ -1,4 +1,4 @@
|
|||||||
#include "tableview.h"
|
#include "node_groups.h"
|
||||||
|
|
||||||
#include "duckdb.h"
|
#include "duckdb.h"
|
||||||
#include "../../../../cpp/vendor/sqlite3/sqlite3.h"
|
#include "../../../../cpp/vendor/sqlite3/sqlite3.h"
|
||||||
@@ -62,7 +62,7 @@ bool duck_query_silent(duckdb_connection cn, const char* sql) {
|
|||||||
|
|
||||||
} // namespace
|
} // namespace
|
||||||
|
|
||||||
bool tableview_smoke_test(const char* duckdb_path) {
|
bool node_groups_smoke_test(const char* duckdb_path) {
|
||||||
DuckHandle h;
|
DuckHandle h;
|
||||||
if (!h.open(duckdb_path)) return false;
|
if (!h.open(duckdb_path)) return false;
|
||||||
duckdb_result r;
|
duckdb_result r;
|
||||||
@@ -76,7 +76,7 @@ bool tableview_smoke_test(const char* duckdb_path) {
|
|||||||
return ok;
|
return ok;
|
||||||
}
|
}
|
||||||
|
|
||||||
bool tableview_count(const char* duckdb_path,
|
bool node_groups_count(const char* duckdb_path,
|
||||||
const char* duck_table,
|
const char* duck_table,
|
||||||
const char* sql_filter,
|
const char* sql_filter,
|
||||||
int64_t* out)
|
int64_t* out)
|
||||||
@@ -94,7 +94,7 @@ bool tableview_count(const char* duckdb_path,
|
|||||||
}
|
}
|
||||||
duckdb_result r;
|
duckdb_result r;
|
||||||
if (duckdb_query(h.cn, sql.c_str(), &r) == DuckDBError) {
|
if (duckdb_query(h.cn, sql.c_str(), &r) == DuckDBError) {
|
||||||
std::fprintf(stderr, "[tableview_count] %s\n",
|
std::fprintf(stderr, "[node_groups_count] %s\n",
|
||||||
duckdb_result_error(&r) ? duckdb_result_error(&r) : "?");
|
duckdb_result_error(&r) ? duckdb_result_error(&r) : "?");
|
||||||
duckdb_destroy_result(&r);
|
duckdb_destroy_result(&r);
|
||||||
return false;
|
return false;
|
||||||
@@ -106,7 +106,7 @@ bool tableview_count(const char* duckdb_path,
|
|||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
bool tableview_page(const char* duckdb_path,
|
bool node_groups_page(const char* duckdb_path,
|
||||||
const char* duck_table,
|
const char* duck_table,
|
||||||
const char* id_column,
|
const char* id_column,
|
||||||
const std::vector<std::string>& columns,
|
const std::vector<std::string>& columns,
|
||||||
@@ -114,7 +114,7 @@ bool tableview_page(const char* duckdb_path,
|
|||||||
const char* ops_db,
|
const char* ops_db,
|
||||||
const char* row_type,
|
const char* row_type,
|
||||||
int64_t offset, int64_t limit,
|
int64_t offset, int64_t limit,
|
||||||
std::vector<TablePageRow>* out)
|
std::vector<NodeGroupsRow>* out)
|
||||||
{
|
{
|
||||||
if (!out) return false;
|
if (!out) return false;
|
||||||
out->clear();
|
out->clear();
|
||||||
@@ -177,7 +177,7 @@ bool tableview_page(const char* duckdb_path,
|
|||||||
duckdb_result r;
|
duckdb_result r;
|
||||||
if (duckdb_query(h.cn, sel.c_str(), &r) == DuckDBError) {
|
if (duckdb_query(h.cn, sel.c_str(), &r) == DuckDBError) {
|
||||||
const char* e = duckdb_result_error(&r);
|
const char* e = duckdb_result_error(&r);
|
||||||
std::fprintf(stderr, "[tableview_page] FAIL: %s\n SQL: %s\n",
|
std::fprintf(stderr, "[node_groups_page] FAIL: %s\n SQL: %s\n",
|
||||||
e ? e : "?", sel.c_str());
|
e ? e : "?", sel.c_str());
|
||||||
duckdb_destroy_result(&r);
|
duckdb_destroy_result(&r);
|
||||||
return false;
|
return false;
|
||||||
@@ -186,7 +186,7 @@ bool tableview_page(const char* duckdb_path,
|
|||||||
idx_t cols = duckdb_column_count(&r);
|
idx_t cols = duckdb_column_count(&r);
|
||||||
out->reserve((size_t)rows);
|
out->reserve((size_t)rows);
|
||||||
for (idx_t row = 0; row < rows; ++row) {
|
for (idx_t row = 0; row < rows; ++row) {
|
||||||
TablePageRow tr;
|
NodeGroupsRow tr;
|
||||||
// col 0 = id
|
// col 0 = id
|
||||||
if (!duckdb_value_is_null(&r, 0, row)) {
|
if (!duckdb_value_is_null(&r, 0, row)) {
|
||||||
char* v = duckdb_value_varchar(&r, 0, row);
|
char* v = duckdb_value_varchar(&r, 0, row);
|
||||||
@@ -220,7 +220,7 @@ bool tableview_page(const char* duckdb_path,
|
|||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
bool tableview_create(const char* ops_db,
|
bool node_groups_create(const char* ops_db,
|
||||||
const char* name,
|
const char* name,
|
||||||
const char* duckdb_path,
|
const char* duckdb_path,
|
||||||
const char* duck_table,
|
const char* duck_table,
|
||||||
@@ -317,7 +317,7 @@ static std::string normalize_path(std::string p) {
|
|||||||
return p;
|
return p;
|
||||||
}
|
}
|
||||||
|
|
||||||
std::string tableview_resolve_path(const char* ops_db, const char* maybe_rel) {
|
std::string node_groups_resolve_path(const char* ops_db, const char* maybe_rel) {
|
||||||
if (!maybe_rel) return "";
|
if (!maybe_rel) return "";
|
||||||
if (is_absolute(maybe_rel)) return normalize_path(maybe_rel);
|
if (is_absolute(maybe_rel)) return normalize_path(maybe_rel);
|
||||||
std::string base = dirname_of(ops_db);
|
std::string base = dirname_of(ops_db);
|
||||||
@@ -325,7 +325,7 @@ std::string tableview_resolve_path(const char* ops_db, const char* maybe_rel) {
|
|||||||
return normalize_path(base + "/" + maybe_rel);
|
return normalize_path(base + "/" + maybe_rel);
|
||||||
}
|
}
|
||||||
|
|
||||||
bool tableview_refresh_counts(const char* ops_db,
|
bool node_groups_refresh_counts(const char* ops_db,
|
||||||
std::unordered_map<uint64_t, int64_t>* out)
|
std::unordered_map<uint64_t, int64_t>* out)
|
||||||
{
|
{
|
||||||
if (!ops_db || !out) return false;
|
if (!ops_db || !out) return false;
|
||||||
@@ -352,13 +352,13 @@ bool tableview_refresh_counts(const char* ops_db,
|
|||||||
const unsigned char* tab_p = sqlite3_column_text(st, 2);
|
const unsigned char* tab_p = sqlite3_column_text(st, 2);
|
||||||
const unsigned char* flt_p = sqlite3_column_text(st, 3);
|
const unsigned char* flt_p = sqlite3_column_text(st, 3);
|
||||||
if (!id_p || !path_p || !tab_p) continue;
|
if (!id_p || !path_p || !tab_p) continue;
|
||||||
std::string abs = tableview_resolve_path(ops_db, (const char*)path_p);
|
std::string abs = node_groups_resolve_path(ops_db, (const char*)path_p);
|
||||||
int64_t total = 0;
|
int64_t total = 0;
|
||||||
if (!tableview_count(abs.c_str(), (const char*)tab_p,
|
if (!node_groups_count(abs.c_str(), (const char*)tab_p,
|
||||||
flt_p ? (const char*)flt_p : nullptr,
|
flt_p ? (const char*)flt_p : nullptr,
|
||||||
&total)) {
|
&total)) {
|
||||||
std::fprintf(stderr,
|
std::fprintf(stderr,
|
||||||
"[tableview_refresh_counts] count failed for id=%s\n", id_p);
|
"[node_groups_refresh_counts] count failed for id=%s\n", id_p);
|
||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
out->emplace(fnv1a64((const char*)id_p), total);
|
out->emplace(fnv1a64((const char*)id_p), total);
|
||||||
@@ -415,7 +415,7 @@ std::vector<std::string> parse_json_string_array(const char* s) {
|
|||||||
|
|
||||||
} // namespace
|
} // namespace
|
||||||
|
|
||||||
bool tableview_list_columns(const char* duckdb_path,
|
bool node_groups_list_columns(const char* duckdb_path,
|
||||||
const char* duck_table,
|
const char* duck_table,
|
||||||
std::vector<std::string>* out)
|
std::vector<std::string>* out)
|
||||||
{
|
{
|
||||||
@@ -438,11 +438,11 @@ bool tableview_list_columns(const char* duckdb_path,
|
|||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
bool tableview_get_metadata(const char* ops_db, const char* entity_id,
|
bool node_groups_get_metadata(const char* ops_db, const char* entity_id,
|
||||||
TableMetadata* out)
|
NodeGroupsMeta* out)
|
||||||
{
|
{
|
||||||
if (!ops_db || !entity_id || !out) return false;
|
if (!ops_db || !entity_id || !out) return false;
|
||||||
*out = TableMetadata{};
|
*out = NodeGroupsMeta{};
|
||||||
out->entity_id = entity_id;
|
out->entity_id = entity_id;
|
||||||
sqlite3* db = nullptr;
|
sqlite3* db = nullptr;
|
||||||
if (sqlite3_open_v2(ops_db, &db, SQLITE_OPEN_READONLY, nullptr) != SQLITE_OK) {
|
if (sqlite3_open_v2(ops_db, &db, SQLITE_OPEN_READONLY, nullptr) != SQLITE_OK) {
|
||||||
@@ -486,7 +486,7 @@ bool tableview_get_metadata(const char* ops_db, const char* entity_id,
|
|||||||
if (out->label_column.empty()) out->label_column = "name";
|
if (out->label_column.empty()) out->label_column = "name";
|
||||||
out->columns = parse_json_string_array(cols_json.c_str());
|
out->columns = parse_json_string_array(cols_json.c_str());
|
||||||
out->expanded = (exp_json == "true" || exp_json == "1");
|
out->expanded = (exp_json == "true" || exp_json == "1");
|
||||||
out->duckdb_path_abs = tableview_resolve_path(ops_db, out->duckdb_path.c_str());
|
out->duckdb_path_abs = node_groups_resolve_path(ops_db, out->duckdb_path.c_str());
|
||||||
ok = true;
|
ok = true;
|
||||||
}
|
}
|
||||||
sqlite3_finalize(st);
|
sqlite3_finalize(st);
|
||||||
@@ -521,7 +521,7 @@ bool exec_metadata_patch(const char* ops_db, const char* entity_id,
|
|||||||
|
|
||||||
} // namespace
|
} // namespace
|
||||||
|
|
||||||
bool tableview_set_expanded(const char* ops_db, const char* entity_id,
|
bool node_groups_set_expanded(const char* ops_db, const char* entity_id,
|
||||||
bool expanded)
|
bool expanded)
|
||||||
{
|
{
|
||||||
if (!ops_db || !entity_id) return false;
|
if (!ops_db || !entity_id) return false;
|
||||||
@@ -531,7 +531,7 @@ bool tableview_set_expanded(const char* ops_db, const char* entity_id,
|
|||||||
return exec_metadata_patch(ops_db, entity_id, clause.c_str());
|
return exec_metadata_patch(ops_db, entity_id, clause.c_str());
|
||||||
}
|
}
|
||||||
|
|
||||||
bool tableview_set_columns(const char* ops_db, const char* entity_id,
|
bool node_groups_set_columns(const char* ops_db, const char* entity_id,
|
||||||
const std::vector<std::string>& columns)
|
const std::vector<std::string>& columns)
|
||||||
{
|
{
|
||||||
if (!ops_db || !entity_id) return false;
|
if (!ops_db || !entity_id) return false;
|
||||||
@@ -586,7 +586,7 @@ bool find_existing_promotion(const char* ops_db, const char* duckdb_path,
|
|||||||
|
|
||||||
} // namespace
|
} // namespace
|
||||||
|
|
||||||
bool tableview_promote_row(const char* ops_db,
|
bool node_groups_promote_row(const char* ops_db,
|
||||||
const char* table_entity_id,
|
const char* table_entity_id,
|
||||||
const char* duckdb_path,
|
const char* duckdb_path,
|
||||||
const char* duck_table,
|
const char* duck_table,
|
||||||
@@ -729,7 +729,7 @@ bool tableview_promote_row(const char* ops_db,
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
bool tableview_demote_row(const char* ops_db, const char* entity_id) {
|
bool node_groups_demote_row(const char* ops_db, const char* entity_id) {
|
||||||
if (!ops_db || !entity_id) return false;
|
if (!ops_db || !entity_id) return false;
|
||||||
sqlite3* db = nullptr;
|
sqlite3* db = nullptr;
|
||||||
if (sqlite3_open_v2(ops_db, &db, SQLITE_OPEN_READWRITE, nullptr) != SQLITE_OK) {
|
if (sqlite3_open_v2(ops_db, &db, SQLITE_OPEN_READWRITE, nullptr) != SQLITE_OK) {
|
||||||
@@ -774,7 +774,7 @@ const char* ingest_func_name(IngestKind k) {
|
|||||||
|
|
||||||
} // namespace
|
} // namespace
|
||||||
|
|
||||||
bool tableview_ingest_file(const char* duckdb_path,
|
bool node_groups_ingest_file(const char* duckdb_path,
|
||||||
const char* file_path,
|
const char* file_path,
|
||||||
const char* dest_table,
|
const char* dest_table,
|
||||||
IngestKind kind,
|
IngestKind kind,
|
||||||
+26
-26
@@ -4,10 +4,10 @@
|
|||||||
#include <unordered_map>
|
#include <unordered_map>
|
||||||
#include <vector>
|
#include <vector>
|
||||||
|
|
||||||
// Vista tabular respaldada por DuckDB (issue 0010). Cada nodo `Table` del
|
// NodeGroups — vista tabular respaldada por DuckDB (issue 0010, renombrada
|
||||||
// grafo apunta via metadata a un archivo `.duckdb` y a una tabla dentro
|
// en 0036a). Cada nodo `Table` del grafo apunta via metadata a un archivo
|
||||||
// de el. Las filas viven en DuckDB; el grafo solo materializa las que
|
// `.duckdb` y a una tabla dentro de el. Las filas viven en DuckDB; el grafo
|
||||||
// se "promueven" a entidades (issue 0011).
|
// solo materializa las que se "promueven" a entidades (issue 0011).
|
||||||
//
|
//
|
||||||
// Convencion de paths: `metadata.duckdb_path` es relativo al directorio del
|
// Convencion de paths: `metadata.duckdb_path` es relativo al directorio del
|
||||||
// proyecto (la raiz donde vive operations.db). El caller resuelve a path
|
// proyecto (la raiz donde vive operations.db). El caller resuelve a path
|
||||||
@@ -15,7 +15,7 @@
|
|||||||
|
|
||||||
namespace ge {
|
namespace ge {
|
||||||
|
|
||||||
struct TablePageRow {
|
struct NodeGroupsRow {
|
||||||
std::string id; // valor del id_column en duckdb (key natural)
|
std::string id; // valor del id_column en duckdb (key natural)
|
||||||
std::vector<std::string> values; // un valor por columna en `columns[]`
|
std::vector<std::string> values; // un valor por columna en `columns[]`
|
||||||
std::string promoted_entity_id; // "" si la fila no esta promovida; sino, ops.entities.id
|
std::string promoted_entity_id; // "" si la fila no esta promovida; sino, ops.entities.id
|
||||||
@@ -25,7 +25,7 @@ struct TablePageRow {
|
|||||||
// type_ref='Table' y metadata apuntando al duckdb_path/table_name. Genera
|
// type_ref='Table' y metadata apuntando al duckdb_path/table_name. Genera
|
||||||
// un id propio. Devuelve false si SQLite falla o si los argumentos basicos
|
// un id propio. Devuelve false si SQLite falla o si los argumentos basicos
|
||||||
// estan vacios.
|
// estan vacios.
|
||||||
bool tableview_create(const char* ops_db,
|
bool node_groups_create(const char* ops_db,
|
||||||
const char* name,
|
const char* name,
|
||||||
const char* duckdb_path,
|
const char* duckdb_path,
|
||||||
const char* duck_table,
|
const char* duck_table,
|
||||||
@@ -35,7 +35,7 @@ bool tableview_create(const char* ops_db,
|
|||||||
// Cuenta las filas de duckdb_path/duck_table aplicando opcionalmente
|
// Cuenta las filas de duckdb_path/duck_table aplicando opcionalmente
|
||||||
// `sql_filter` (clausula WHERE sin la palabra WHERE — vacio = sin filtro).
|
// `sql_filter` (clausula WHERE sin la palabra WHERE — vacio = sin filtro).
|
||||||
// Devuelve false en error de IO/parse.
|
// Devuelve false en error de IO/parse.
|
||||||
bool tableview_count(const char* duckdb_path,
|
bool node_groups_count(const char* duckdb_path,
|
||||||
const char* duck_table,
|
const char* duck_table,
|
||||||
const char* sql_filter,
|
const char* sql_filter,
|
||||||
int64_t* out);
|
int64_t* out);
|
||||||
@@ -44,7 +44,7 @@ bool tableview_count(const char* duckdb_path,
|
|||||||
// valores de `columns` resueltos a string + el flag `promoted_entity_id`
|
// valores de `columns` resueltos a string + el flag `promoted_entity_id`
|
||||||
// computado via LEFT JOIN contra ops.entities (DuckDB attach a SQLite).
|
// computado via LEFT JOIN contra ops.entities (DuckDB attach a SQLite).
|
||||||
// limit clampeado en [1,5000].
|
// limit clampeado en [1,5000].
|
||||||
bool tableview_page(const char* duckdb_path,
|
bool node_groups_page(const char* duckdb_path,
|
||||||
const char* duck_table,
|
const char* duck_table,
|
||||||
const char* id_column,
|
const char* id_column,
|
||||||
const std::vector<std::string>& columns,
|
const std::vector<std::string>& columns,
|
||||||
@@ -52,20 +52,20 @@ bool tableview_page(const char* duckdb_path,
|
|||||||
const char* ops_db, // para LEFT JOIN de promovidas
|
const char* ops_db, // para LEFT JOIN de promovidas
|
||||||
const char* row_type, // discriminante en ops.entities
|
const char* row_type, // discriminante en ops.entities
|
||||||
int64_t offset, int64_t limit,
|
int64_t offset, int64_t limit,
|
||||||
std::vector<TablePageRow>* out);
|
std::vector<NodeGroupsRow>* out);
|
||||||
|
|
||||||
// Smoke test: abre el .duckdb, corre `SELECT 42 AS x` y verifica que
|
// Smoke test: abre el .duckdb, corre `SELECT 42 AS x` y verifica que
|
||||||
// devuelve la fila esperada. Devuelve true si todo OK.
|
// devuelve la fila esperada. Devuelve true si todo OK.
|
||||||
bool tableview_smoke_test(const char* duckdb_path);
|
bool node_groups_smoke_test(const char* duckdb_path);
|
||||||
|
|
||||||
// Resuelve un path posiblemente relativo a la ubicacion de operations.db.
|
// Resuelve un path posiblemente relativo a la ubicacion de operations.db.
|
||||||
// Si es absoluto (empieza por '/' o '<letra>:' en Windows), se devuelve
|
// Si es absoluto (empieza por '/' o '<letra>:' en Windows), se devuelve
|
||||||
// tal cual.
|
// tal cual.
|
||||||
std::string tableview_resolve_path(const char* ops_db, const char* maybe_rel);
|
std::string node_groups_resolve_path(const char* ops_db, const char* maybe_rel);
|
||||||
|
|
||||||
// Refresca el cache de conteos de filas por nodo Table. Lee
|
// Refresca el cache de conteos de filas por nodo Table. Lee
|
||||||
// type_ref='Table' de operations.db, extrae metadata.duckdb_path/table_name,
|
// type_ref='Table' de operations.db, extrae metadata.duckdb_path/table_name,
|
||||||
// llama a tableview_count y guarda el resultado indexado por
|
// llama a node_groups_count y guarda el resultado indexado por
|
||||||
// fnv1a64(entity_id) — la misma key que usa graph_sources al setear
|
// fnv1a64(entity_id) — la misma key que usa graph_sources al setear
|
||||||
// node.user_data, asi que el render puede mirar directo por user_data.
|
// node.user_data, asi que el render puede mirar directo por user_data.
|
||||||
// Si una tabla falla, su entrada NO se inserta y se imprime un warning.
|
// Si una tabla falla, su entrada NO se inserta y se imprime un warning.
|
||||||
@@ -73,7 +73,7 @@ struct TableCounts {
|
|||||||
// user_data hash (fnv1a64 del entity id) -> total filas tras filter_sql.
|
// user_data hash (fnv1a64 del entity id) -> total filas tras filter_sql.
|
||||||
// -1 indica error/ausencia.
|
// -1 indica error/ausencia.
|
||||||
};
|
};
|
||||||
bool tableview_refresh_counts(const char* ops_db,
|
bool node_groups_refresh_counts(const char* ops_db,
|
||||||
std::unordered_map<uint64_t, int64_t>* out);
|
std::unordered_map<uint64_t, int64_t>* out);
|
||||||
|
|
||||||
// ----------------------------------------------------------------------------
|
// ----------------------------------------------------------------------------
|
||||||
@@ -81,11 +81,11 @@ bool tableview_refresh_counts(const char* ops_db,
|
|||||||
// ----------------------------------------------------------------------------
|
// ----------------------------------------------------------------------------
|
||||||
|
|
||||||
// Snapshot de la metadata relevante de un nodo Table. Caller-owned strings.
|
// Snapshot de la metadata relevante de un nodo Table. Caller-owned strings.
|
||||||
struct TableMetadata {
|
struct NodeGroupsMeta {
|
||||||
std::string entity_id;
|
std::string entity_id;
|
||||||
std::string name;
|
std::string name;
|
||||||
std::string duckdb_path; // tal como aparece en metadata (relativo o absoluto)
|
std::string duckdb_path; // tal como aparece en metadata (relativo o absoluto)
|
||||||
std::string duckdb_path_abs; // resuelto con tableview_resolve_path
|
std::string duckdb_path_abs; // resuelto con node_groups_resolve_path
|
||||||
std::string table_name;
|
std::string table_name;
|
||||||
std::string row_type;
|
std::string row_type;
|
||||||
std::string id_column;
|
std::string id_column;
|
||||||
@@ -98,17 +98,17 @@ struct TableMetadata {
|
|||||||
// Lee la metadata del nodo Table (entidad type_ref='Table' con id=`entity_id`).
|
// Lee la metadata del nodo Table (entidad type_ref='Table' con id=`entity_id`).
|
||||||
// Devuelve false si no existe o falla el JSON. Aplica defaults razonables a
|
// Devuelve false si no existe o falla el JSON. Aplica defaults razonables a
|
||||||
// los campos faltantes (id_column='id', label_column='name').
|
// los campos faltantes (id_column='id', label_column='name').
|
||||||
bool tableview_get_metadata(const char* ops_db, const char* entity_id,
|
bool node_groups_get_metadata(const char* ops_db, const char* entity_id,
|
||||||
TableMetadata* out);
|
NodeGroupsMeta* out);
|
||||||
|
|
||||||
// Persiste el flag `expanded` en la metadata. Idempotente. Devuelve false
|
// Persiste el flag `expanded` en la metadata. Idempotente. Devuelve false
|
||||||
// en error de IO/SQL.
|
// en error de IO/SQL.
|
||||||
bool tableview_set_expanded(const char* ops_db, const char* entity_id,
|
bool node_groups_set_expanded(const char* ops_db, const char* entity_id,
|
||||||
bool expanded);
|
bool expanded);
|
||||||
|
|
||||||
// Sobrescribe el array `columns` en la metadata. Llamar tras editar columnas
|
// Sobrescribe el array `columns` en la metadata. Llamar tras editar columnas
|
||||||
// desde la UI o tras tableview_create cuando se descubren columnas.
|
// desde la UI o tras node_groups_create cuando se descubren columnas.
|
||||||
bool tableview_set_columns(const char* ops_db, const char* entity_id,
|
bool node_groups_set_columns(const char* ops_db, const char* entity_id,
|
||||||
const std::vector<std::string>& columns);
|
const std::vector<std::string>& columns);
|
||||||
|
|
||||||
// Promueve una fila de DuckDB a entidad del grafo. Idempotente: si ya existe
|
// Promueve una fila de DuckDB a entidad del grafo. Idempotente: si ya existe
|
||||||
@@ -123,7 +123,7 @@ bool tableview_set_columns(const char* ops_db, const char* entity_id,
|
|||||||
// Si table_entity_id no es nulo/vacio, inserta tambien una relacion
|
// Si table_entity_id no es nulo/vacio, inserta tambien una relacion
|
||||||
// CONTAINS_ROW (idempotente) entre la tabla y la nueva entidad para que el
|
// CONTAINS_ROW (idempotente) entre la tabla y la nueva entidad para que el
|
||||||
// viewport pinte la arista de pertenencia.
|
// viewport pinte la arista de pertenencia.
|
||||||
bool tableview_promote_row(const char* ops_db,
|
bool node_groups_promote_row(const char* ops_db,
|
||||||
const char* table_entity_id,
|
const char* table_entity_id,
|
||||||
const char* duckdb_path,
|
const char* duckdb_path,
|
||||||
const char* duck_table,
|
const char* duck_table,
|
||||||
@@ -134,9 +134,9 @@ bool tableview_promote_row(const char* ops_db,
|
|||||||
|
|
||||||
// Borra la entidad. La fila DuckDB sigue intacta. Devuelve true si la fila
|
// Borra la entidad. La fila DuckDB sigue intacta. Devuelve true si la fila
|
||||||
// no existia (no-op idempotente).
|
// no existia (no-op idempotente).
|
||||||
bool tableview_demote_row(const char* ops_db, const char* entity_id);
|
bool node_groups_demote_row(const char* ops_db, const char* entity_id);
|
||||||
|
|
||||||
// Tipos de fichero soportados por tableview_ingest_file.
|
// Tipos de fichero soportados por node_groups_ingest_file.
|
||||||
enum IngestKind {
|
enum IngestKind {
|
||||||
INGEST_AUTO = 0, // detecta por extension
|
INGEST_AUTO = 0, // detecta por extension
|
||||||
INGEST_CSV = 1,
|
INGEST_CSV = 1,
|
||||||
@@ -147,15 +147,15 @@ enum IngestKind {
|
|||||||
// Importa un fichero CSV/Parquet/JSON al `.duckdb`. Crea el .duckdb si no
|
// Importa un fichero CSV/Parquet/JSON al `.duckdb`. Crea el .duckdb si no
|
||||||
// existe. Si la tabla destino existe, falla (no sobrescribe — explicit fail).
|
// existe. Si la tabla destino existe, falla (no sobrescribe — explicit fail).
|
||||||
// Por defecto INGEST_AUTO inspecciona la extension del path.
|
// Por defecto INGEST_AUTO inspecciona la extension del path.
|
||||||
bool tableview_ingest_file(const char* duckdb_path,
|
bool node_groups_ingest_file(const char* duckdb_path,
|
||||||
const char* file_path,
|
const char* file_path,
|
||||||
const char* dest_table,
|
const char* dest_table,
|
||||||
IngestKind kind,
|
IngestKind kind,
|
||||||
std::string* out_error);
|
std::string* out_error);
|
||||||
|
|
||||||
// Lista los nombres de columna de la tabla DuckDB. Para popular la lista
|
// Lista los nombres de columna de la tabla DuckDB. Para popular la lista
|
||||||
// `columns` por defecto en tableview_create.
|
// `columns` por defecto en node_groups_create.
|
||||||
bool tableview_list_columns(const char* duckdb_path,
|
bool node_groups_list_columns(const char* duckdb_path,
|
||||||
const char* duck_table,
|
const char* duck_table,
|
||||||
std::vector<std::string>* out);
|
std::vector<std::string>* out);
|
||||||
|
|
||||||
@@ -306,15 +306,15 @@ void views_toolbar(AppState& app) {
|
|||||||
{
|
{
|
||||||
char btn[64];
|
char btn[64];
|
||||||
std::snprintf(btn, sizeof(btn), TI_TABLE " Tables (%zu)",
|
std::snprintf(btn, sizeof(btn), TI_TABLE " Tables (%zu)",
|
||||||
app.table_windows.size());
|
app.node_groups_windows.size());
|
||||||
if (button(btn, ButtonVariant::Subtle)) {
|
if (button(btn, ButtonVariant::Subtle)) {
|
||||||
ImGui::OpenPopup("##tables_menu");
|
ImGui::OpenPopup("##tables_menu");
|
||||||
}
|
}
|
||||||
if (ImGui::BeginPopup("##tables_menu")) {
|
if (ImGui::BeginPopup("##tables_menu")) {
|
||||||
if (app.table_windows.empty()) {
|
if (app.node_groups_windows.empty()) {
|
||||||
ImGui::TextDisabled("(no expanded tables)");
|
ImGui::TextDisabled("(no open NodeGroups)");
|
||||||
} else {
|
} else {
|
||||||
for (auto& kv : app.table_windows) {
|
for (auto& kv : app.node_groups_windows) {
|
||||||
bool checked = kv.second.open;
|
bool checked = kv.second.open;
|
||||||
char lbl[160];
|
char lbl[160];
|
||||||
std::snprintf(lbl, sizeof(lbl), "%s (%lld rows)",
|
std::snprintf(lbl, sizeof(lbl), "%s (%lld rows)",
|
||||||
@@ -326,7 +326,7 @@ void views_toolbar(AppState& app) {
|
|||||||
}
|
}
|
||||||
ImGui::Separator();
|
ImGui::Separator();
|
||||||
if (ImGui::MenuItem(TI_X " Collapse all")) {
|
if (ImGui::MenuItem(TI_X " Collapse all")) {
|
||||||
for (auto& kv : app.table_windows) kv.second.open = false;
|
for (auto& kv : app.node_groups_windows) kv.second.open = false;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
ImGui::EndPopup();
|
ImGui::EndPopup();
|
||||||
@@ -1873,12 +1873,12 @@ void views_table(AppState& app) {
|
|||||||
// Table node UI fase 2 (issue 0011) — ventana expandida + import
|
// Table node UI fase 2 (issue 0011) — ventana expandida + import
|
||||||
// ----------------------------------------------------------------------------
|
// ----------------------------------------------------------------------------
|
||||||
|
|
||||||
void views_table_windows_sync(AppState& app, const char* ops_db) {
|
void views_node_groups_windows_sync(AppState& app, const char* ops_db) {
|
||||||
if (!app.graph || !ops_db) return;
|
if (!app.graph || !ops_db) return;
|
||||||
GraphData& g = *app.graph;
|
GraphData& g = *app.graph;
|
||||||
|
|
||||||
// Construir set de Tables expandidas con su metadata fresca.
|
// Construir set de Tables expandidas con su metadata fresca.
|
||||||
std::unordered_map<std::string, TableMetadata> live;
|
std::unordered_map<std::string, NodeGroupsMeta> live;
|
||||||
for (int i = 0; i < g.node_count; ++i) {
|
for (int i = 0; i < g.node_count; ++i) {
|
||||||
const GraphNode& n = g.nodes[i];
|
const GraphNode& n = g.nodes[i];
|
||||||
if (n.type_id >= (uint16_t)g.type_count) continue;
|
if (n.type_id >= (uint16_t)g.type_count) continue;
|
||||||
@@ -1906,8 +1906,8 @@ void views_table_windows_sync(AppState& app, const char* ops_db) {
|
|||||||
const unsigned char* p = sqlite3_column_text(st, 0);
|
const unsigned char* p = sqlite3_column_text(st, 0);
|
||||||
if (!p) continue;
|
if (!p) continue;
|
||||||
std::string id = (const char*)p;
|
std::string id = (const char*)p;
|
||||||
TableMetadata meta;
|
NodeGroupsMeta meta;
|
||||||
if (tableview_get_metadata(ops_db, id.c_str(), &meta)) {
|
if (node_groups_get_metadata(ops_db, id.c_str(), &meta)) {
|
||||||
live.emplace(id, std::move(meta));
|
live.emplace(id, std::move(meta));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -1915,8 +1915,8 @@ void views_table_windows_sync(AppState& app, const char* ops_db) {
|
|||||||
sqlite3_close(db);
|
sqlite3_close(db);
|
||||||
|
|
||||||
// Quitar las que ya no estan expanded.
|
// Quitar las que ya no estan expanded.
|
||||||
for (auto it = app.table_windows.begin(); it != app.table_windows.end(); ) {
|
for (auto it = app.node_groups_windows.begin(); it != app.node_groups_windows.end(); ) {
|
||||||
if (live.find(it->first) == live.end()) it = app.table_windows.erase(it);
|
if (live.find(it->first) == live.end()) it = app.node_groups_windows.erase(it);
|
||||||
else ++it;
|
else ++it;
|
||||||
}
|
}
|
||||||
// Anadir las nuevas o refrescar metadata. Tras cualquier sync forzamos
|
// Anadir las nuevas o refrescar metadata. Tras cualquier sync forzamos
|
||||||
@@ -1925,7 +1925,7 @@ void views_table_windows_sync(AppState& app, const char* ops_db) {
|
|||||||
// promote/demote/import — donde el flag promoted de cada fila puede
|
// promote/demote/import — donde el flag promoted de cada fila puede
|
||||||
// haber cambiado).
|
// haber cambiado).
|
||||||
for (auto& kv : live) {
|
for (auto& kv : live) {
|
||||||
auto& w = app.table_windows[kv.first];
|
auto& w = app.node_groups_windows[kv.first];
|
||||||
bool was_present = !w.meta.entity_id.empty();
|
bool was_present = !w.meta.entity_id.empty();
|
||||||
w.meta = std::move(kv.second);
|
w.meta = std::move(kv.second);
|
||||||
w.open = true;
|
w.open = true;
|
||||||
@@ -1938,18 +1938,18 @@ void views_table_windows_sync(AppState& app, const char* ops_db) {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
void views_table_window(AppState& app) {
|
void views_node_groups_window(AppState& app) {
|
||||||
if (app.table_windows.empty()) return;
|
if (app.node_groups_windows.empty()) return;
|
||||||
GraphData* g = app.graph;
|
GraphData* g = app.graph;
|
||||||
GraphViewportState* vp = app.viewport;
|
GraphViewportState* vp = app.viewport;
|
||||||
|
|
||||||
for (auto& kv : app.table_windows) {
|
for (auto& kv : app.node_groups_windows) {
|
||||||
TableMetadata& m = kv.second.meta;
|
NodeGroupsMeta& m = kv.second.meta;
|
||||||
AppState::TableWindowState& w = kv.second;
|
AppState::NodeGroupsWindowState& w = kv.second;
|
||||||
|
|
||||||
char title[160];
|
char title[160];
|
||||||
std::snprintf(title, sizeof(title), TI_TABLE " %s##te_%s",
|
std::snprintf(title, sizeof(title), TI_TABLE " NodeGroups: %s##te_%s",
|
||||||
m.name.empty() ? "Table" : m.name.c_str(),
|
m.name.empty() ? "(unnamed)" : m.name.c_str(),
|
||||||
m.entity_id.c_str());
|
m.entity_id.c_str());
|
||||||
ImGui::SetNextWindowSize(ImVec2(640, 460), ImGuiCond_FirstUseEver);
|
ImGui::SetNextWindowSize(ImVec2(640, 460), ImGuiCond_FirstUseEver);
|
||||||
if (!ImGui::Begin(title, &w.open)) { ImGui::End(); continue; }
|
if (!ImGui::Begin(title, &w.open)) { ImGui::End(); continue; }
|
||||||
@@ -1987,7 +1987,7 @@ void views_table_window(AppState& app) {
|
|||||||
// avanzamos offset.
|
// avanzamos offset.
|
||||||
const int64_t page_size = 200;
|
const int64_t page_size = 200;
|
||||||
for (int64_t i = 0; i < (int64_t)w.page.size(); ++i) {
|
for (int64_t i = 0; i < (int64_t)w.page.size(); ++i) {
|
||||||
const TablePageRow& row = w.page[i];
|
const NodeGroupsRow& row = w.page[i];
|
||||||
ImGui::TableNextRow();
|
ImGui::TableNextRow();
|
||||||
ImGui::PushID((int)(w.offset + i));
|
ImGui::PushID((int)(w.offset + i));
|
||||||
|
|
||||||
@@ -2080,7 +2080,7 @@ void views_table_window(AppState& app) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
// Cerrar la ventana = expanded=false. Lo procesa main.cpp leyendo
|
// Cerrar la ventana = expanded=false. Lo procesa main.cpp leyendo
|
||||||
// table_windows y comparando `open`.
|
// node_groups_windows y comparando `open`.
|
||||||
}
|
}
|
||||||
|
|
||||||
bool views_import_dataset_modal(AppState& app) {
|
bool views_import_dataset_modal(AppState& app) {
|
||||||
@@ -2132,7 +2132,7 @@ bool views_import_dataset_modal(AppState& app) {
|
|||||||
// Table node overlay (issue 0010)
|
// Table node overlay (issue 0010)
|
||||||
// ----------------------------------------------------------------------------
|
// ----------------------------------------------------------------------------
|
||||||
|
|
||||||
void views_table_overlay(AppState& app) {
|
void views_node_groups_overlay(AppState& app) {
|
||||||
if (!app.graph || !app.viewport) return;
|
if (!app.graph || !app.viewport) return;
|
||||||
GraphData& g = *app.graph;
|
GraphData& g = *app.graph;
|
||||||
if (g.type_count == 0) return;
|
if (g.type_count == 0) return;
|
||||||
@@ -2162,8 +2162,8 @@ void views_table_overlay(AppState& app) {
|
|||||||
if (vy < wmin.y - 100 || vy > wmax.y + 100) continue;
|
if (vy < wmin.y - 100 || vy > wmax.y + 100) continue;
|
||||||
|
|
||||||
int64_t count = -1;
|
int64_t count = -1;
|
||||||
auto it = app.table_node_counts.find(n.user_data);
|
auto it = app.node_groups_counts.find(n.user_data);
|
||||||
if (it != app.table_node_counts.end()) count = it->second;
|
if (it != app.node_groups_counts.end()) count = it->second;
|
||||||
if (count < 0) continue;
|
if (count < 0) continue;
|
||||||
|
|
||||||
char buf[64];
|
char buf[64];
|
||||||
|
|||||||
@@ -5,7 +5,7 @@
|
|||||||
|
|
||||||
#include "types_registry.h"
|
#include "types_registry.h"
|
||||||
#include "entity_ops.h"
|
#include "entity_ops.h"
|
||||||
#include "tableview.h"
|
#include "node_groups.h"
|
||||||
|
|
||||||
#include <cstdint>
|
#include <cstdint>
|
||||||
#include <unordered_map>
|
#include <unordered_map>
|
||||||
@@ -158,23 +158,24 @@ struct AppState {
|
|||||||
// ---- Table node (issue 0010) ------------------------------------------
|
// ---- Table node (issue 0010) ------------------------------------------
|
||||||
// Cache de conteo de filas por nodo Table indexado por user_data hash.
|
// Cache de conteo de filas por nodo Table indexado por user_data hash.
|
||||||
// Refrescado tras load_input y tras mutaciones que afecten a Tables.
|
// Refrescado tras load_input y tras mutaciones que afecten a Tables.
|
||||||
std::unordered_map<uint64_t, int64_t> table_node_counts;
|
std::unordered_map<uint64_t, int64_t> node_groups_counts;
|
||||||
|
|
||||||
// ---- Table node UI fase 2 (issue 0011) --------------------------------
|
// ---- NodeGroups window (issue 0011, renombrado en 0036a) --------------
|
||||||
// Estado runtime por ventana de Table expandida. Una entrada por
|
// Estado runtime por ventana de NodeGroups (un Table-typed expandido).
|
||||||
// entity_id de Table que el usuario haya expandido. La ventana se cierra
|
// Una entrada por entity_id de Table que el usuario haya expandido. La
|
||||||
// cuando set_expanded(false) — ya sea desde context menu o cerrando la
|
// ventana se cierra cuando set_expanded(false) — ya sea desde context
|
||||||
// ImGui window (que pone el flag a false automaticamente).
|
// menu o cerrando la ImGui window (que pone el flag a false
|
||||||
struct TableWindowState {
|
// automaticamente).
|
||||||
TableMetadata meta; // refrescada cada vez que entity cambia
|
struct NodeGroupsWindowState {
|
||||||
int64_t total_rows = 0;
|
NodeGroupsMeta meta; // refrescada cada vez que entity cambia
|
||||||
int64_t offset = 0;
|
int64_t total_rows = 0;
|
||||||
std::vector<TablePageRow> page;
|
int64_t offset = 0;
|
||||||
bool page_dirty = true;
|
std::vector<NodeGroupsRow> page;
|
||||||
bool open = true; // bound a ImGui::Begin
|
bool page_dirty = true;
|
||||||
std::string last_error; // ultimo error de query (vacio = OK)
|
bool open = true; // bound a ImGui::Begin
|
||||||
|
std::string last_error; // ultimo error de query (vacio = OK)
|
||||||
};
|
};
|
||||||
std::unordered_map<std::string, TableWindowState> table_windows;
|
std::unordered_map<std::string, NodeGroupsWindowState> node_groups_windows;
|
||||||
|
|
||||||
// Triggers consumidos por main.cpp tras click en filas.
|
// Triggers consumidos por main.cpp tras click en filas.
|
||||||
bool want_promote_row = false;
|
bool want_promote_row = false;
|
||||||
@@ -196,9 +197,9 @@ struct AppState {
|
|||||||
bool want_import = false;
|
bool want_import = false;
|
||||||
std::string import_error;
|
std::string import_error;
|
||||||
|
|
||||||
// Toggle expanded desde context menu del viewport.
|
// Toggle NodeGroups window desde context menu del viewport.
|
||||||
bool want_toggle_expanded = false;
|
bool want_toggle_nodegroups = false;
|
||||||
std::string toggle_expanded_id;
|
std::string toggle_nodegroups_id;
|
||||||
|
|
||||||
// ---- Table view (issue 0004) -------------------------------------------
|
// ---- Table view (issue 0004) -------------------------------------------
|
||||||
// Vista tabular dockeable. Tabs por type_ref del grafo activo + opcional
|
// Vista tabular dockeable. Tabs por type_ref del grafo activo + opcional
|
||||||
@@ -335,33 +336,34 @@ EntityRecord views_inspector_build_record(const AppState& app);
|
|||||||
// al cambiar de proyecto.
|
// al cambiar de proyecto.
|
||||||
void views_inspector_clear_draft(AppState& app);
|
void views_inspector_clear_draft(AppState& app);
|
||||||
|
|
||||||
// ---- Table node UI fase 2 (issue 0011) ----------------------------------
|
// ---- NodeGroups window (issue 0011, renombrado en 0036a) ----------------
|
||||||
|
|
||||||
// Renderiza una ventana ImGui dockeable por cada Table en table_windows
|
// Renderiza una ventana ImGui dockeable por cada NodeGroups en
|
||||||
// con `open=true`. Cabecera con nombres de columnas. Filas paginadas con
|
// node_groups_windows con `open=true`. Cabecera con nombres de columnas.
|
||||||
// ImGuiListClipper consumiendo el page cache; al cambiar el offset, marca
|
// Filas paginadas con ImGuiListClipper consumiendo el page cache; al
|
||||||
// dirty para que main.cpp refresque via tableview_page. Doble click en
|
// cambiar el offset, marca dirty para que main.cpp refresque via
|
||||||
// fila no promovida -> setea promote_table_id/promote_row_id; promovida
|
// node_groups_page. Doble click en fila no promovida -> setea
|
||||||
// -> focus_entity_id. Cerrar la ventana setea expanded=false en BD.
|
// promote_table_id/promote_row_id; promovida -> focus_entity_id. Cerrar la
|
||||||
void views_table_window(AppState& app);
|
// ventana setea expanded=false en BD.
|
||||||
|
void views_node_groups_window(AppState& app);
|
||||||
|
|
||||||
// Modal "Import dataset..." — formulario para crear una tabla DuckDB
|
// Modal "Import dataset..." — formulario para crear una tabla DuckDB
|
||||||
// desde CSV/Parquet/JSON y registrar el nodo Table correspondiente.
|
// desde CSV/Parquet/JSON y registrar el nodo Table correspondiente.
|
||||||
bool views_import_dataset_modal(AppState& app);
|
bool views_import_dataset_modal(AppState& app);
|
||||||
|
|
||||||
// Sincroniza table_windows con la metadata.expanded de cada nodo Table.
|
// Sincroniza node_groups_windows con la metadata.expanded de cada nodo
|
||||||
// Llamar tras load + tras mutaciones que cambien expanded. Crea entradas
|
// Table. Llamar tras load + tras mutaciones que cambien expanded. Crea
|
||||||
// para nuevos expanded y borra las que ya no aplican.
|
// entradas para nuevos expanded y borra las que ya no aplican.
|
||||||
void views_table_windows_sync(AppState& app, const char* ops_db);
|
void views_node_groups_windows_sync(AppState& app, const char* ops_db);
|
||||||
|
|
||||||
// ---- Table node overlay (issue 0010) ------------------------------------
|
// ---- Table node overlay (issue 0010) ------------------------------------
|
||||||
|
|
||||||
// Dibuja un overlay rectangulo redondeado sobre cada nodo `Table` del grafo
|
// Dibuja un overlay rectangulo redondeado sobre cada nodo `Table` del grafo
|
||||||
// con etiqueta "Table · N rows" leyendo de app.table_node_counts. Llamar
|
// con etiqueta "Table · N rows" leyendo de app.node_groups_counts. Llamar
|
||||||
// despues de graph_viewport(...) — usa GetItemRectMin/Max + GetWindowDrawList
|
// despues de graph_viewport(...) — usa GetItemRectMin/Max + GetWindowDrawList
|
||||||
// del item viewport. No interactua con eventos; el hit-testing del nodo
|
// del item viewport. No interactua con eventos; el hit-testing del nodo
|
||||||
// sigue usandolo el viewport circular de fondo.
|
// sigue usandolo el viewport circular de fondo.
|
||||||
void views_table_overlay(AppState& app);
|
void views_node_groups_overlay(AppState& app);
|
||||||
|
|
||||||
// ---- Table view (issue 0004) --------------------------------------------
|
// ---- Table view (issue 0004) --------------------------------------------
|
||||||
|
|
||||||
|
|||||||
Reference in New Issue
Block a user