refactor(0036a): rename Table-expanded -> NodeGroups (paperwork)
Rename masivo sin cambio de comportamiento. Habilita 0036b-f que ya
asumen la nueva convencion.
Archivos:
- tableview.{cpp,h} -> node_groups.{cpp,h} (git mv para preservar history)
- CMakeLists.txt: tableview.cpp -> node_groups.cpp
Tipos:
- TableWindowState -> NodeGroupsWindowState (views.h)
- TableMetadata -> NodeGroupsMeta (node_groups.h)
- TablePageRow -> NodeGroupsRow (node_groups.h)
Campos AppState:
- table_windows -> node_groups_windows
- table_node_counts -> node_groups_counts
- toggle_expanded_id -> toggle_nodegroups_id
- want_toggle_expanded -> want_toggle_nodegroups
Funciones (window por contenedor — NO el panel generico Table):
- tableview_create / count / page / smoke_test / resolve_path /
refresh_counts / list_columns / get_metadata / set_expanded /
set_columns / promote_row / demote_row / ingest_file
-> prefijo node_groups_*
- views_table_window -> views_node_groups_window
- views_table_windows_sync -> views_node_groups_windows_sync
- views_table_overlay -> views_node_groups_overlay
Strings de UI:
- "Expand table" / "Collapse table" -> "Open NodeGroups" / "Close NodeGroups"
- Window title "<icon> <name>" -> "<icon> NodeGroups: <name>"
- Tooltip "(no expanded tables)" -> "(no open NodeGroups)"
- Logs [tableview_*] -> [node_groups_*]
Preservados intencionalmente (no son cambio de identificadores C++):
- CLI flag --test-tableview (cambiarlo seria cambio de behavior publico)
- Valor 'tableview' en columna entities.source (cambiarlo afectaria
datos persistidos en BD)
NO tocado:
- Panel generico Table (views_table, panel_table, table_rows,
table_show_all, table_search_buf, table_filter_*, table_col_filters,
table_active_tab, TableRow, table_filter_group_*, etc.)
- issues/completed/* (historia)
Verificacion:
- Build C++ Linux + Windows: green sin warnings nuevos.
- pytest WSL: 89 passed.
- pytest Windows: 78 passed + 11 skipped.
- git grep audit: solo residuos en issues/ (historia) + CLI flag y
source DB value preservados.
Refs: issues/0036a-rename-nodegroups.md
This commit is contained in:
+1
-1
@@ -23,7 +23,7 @@ add_imgui_app(graph_explorer
|
||||
layout_store.cpp
|
||||
entity_ops.cpp
|
||||
project_manager.cpp
|
||||
tableview.cpp
|
||||
node_groups.cpp
|
||||
jobs.cpp
|
||||
enrichers.cpp
|
||||
chat.cpp
|
||||
|
||||
@@ -429,7 +429,7 @@ def cmd_table_list(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())
|
||||
try:
|
||||
row = cn.execute(
|
||||
|
||||
@@ -33,7 +33,7 @@
|
||||
|
||||
#include "../../../../cpp/vendor/sqlite3/sqlite3.h"
|
||||
|
||||
#include "tableview.h"
|
||||
#include "node_groups.h"
|
||||
#include "duckdb.h"
|
||||
|
||||
#include <cstdio>
|
||||
@@ -707,15 +707,15 @@ static bool load_input(bool first_load) {
|
||||
|
||||
// Cache de conteos de Table nodes (issue 0010).
|
||||
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;
|
||||
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,
|
||||
"[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
|
||||
// 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.
|
||||
@@ -882,15 +882,15 @@ static void render_context_menu() {
|
||||
const char* sql_id = ge::entity_index_lookup(g_idx, n.user_data);
|
||||
|
||||
if (is_table && sql_id) {
|
||||
// Determinar estado expanded actual sin ir a BD: mira table_windows.
|
||||
bool currently_expanded =
|
||||
g_app.table_windows.find(sql_id) != g_app.table_windows.end();
|
||||
const char* lbl_exp = currently_expanded
|
||||
? TI_X " Collapse table"
|
||||
: TI_TABLE " Expand table";
|
||||
// Determinar estado actual sin ir a BD: mira node_groups_windows.
|
||||
bool currently_open =
|
||||
g_app.node_groups_windows.find(sql_id) != g_app.node_groups_windows.end();
|
||||
const char* lbl_exp = currently_open
|
||||
? TI_X " Close NodeGroups"
|
||||
: TI_TABLE " Open NodeGroups";
|
||||
if (ImGui::MenuItem(lbl_exp)) {
|
||||
g_app.want_toggle_expanded = true;
|
||||
g_app.toggle_expanded_id = sql_id;
|
||||
g_app.want_toggle_nodegroups = true;
|
||||
g_app.toggle_nodegroups_id = sql_id;
|
||||
}
|
||||
ImGui::Separator();
|
||||
}
|
||||
@@ -1580,7 +1580,7 @@ static void render() {
|
||||
|
||||
// Reaplica types.yaml + atlas. Sin esto, despues de cualquier
|
||||
// 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.
|
||||
if (!g_app.parsed_types.entities.empty() ||
|
||||
!g_app.parsed_types.relations.empty()) {
|
||||
@@ -1592,10 +1592,10 @@ static void render() {
|
||||
}
|
||||
|
||||
// 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.
|
||||
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).
|
||||
std::vector<ge::EntityRowSnapshot> snap;
|
||||
@@ -1688,30 +1688,30 @@ static void render() {
|
||||
}
|
||||
|
||||
// ---- 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()) {
|
||||
std::string id = g_app.toggle_expanded_id;
|
||||
bool currently = g_app.table_windows.find(id) != g_app.table_windows.end();
|
||||
ge::tableview_set_expanded(g_input_path.c_str(), id.c_str(), !currently);
|
||||
ge::views_table_windows_sync(g_app, g_input_path.c_str());
|
||||
g_app.want_toggle_expanded = false;
|
||||
g_app.toggle_expanded_id.clear();
|
||||
std::string id = g_app.toggle_nodegroups_id;
|
||||
bool currently = g_app.node_groups_windows.find(id) != g_app.node_groups_windows.end();
|
||||
ge::node_groups_set_expanded(g_input_path.c_str(), id.c_str(), !currently);
|
||||
ge::views_node_groups_windows_sync(g_app, g_input_path.c_str());
|
||||
g_app.want_toggle_nodegroups = false;
|
||||
g_app.toggle_nodegroups_id.clear();
|
||||
}
|
||||
// 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()) {
|
||||
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 = g_app.table_windows.erase(it);
|
||||
it = g_app.node_groups_windows.erase(it);
|
||||
} else ++it;
|
||||
}
|
||||
// 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;
|
||||
if (!w.page_dirty) continue;
|
||||
const auto& m = w.meta;
|
||||
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.filter_sql.empty() ? nullptr : m.filter_sql.c_str(),
|
||||
&w.total_rows);
|
||||
@@ -1725,14 +1725,14 @@ static void render() {
|
||||
}
|
||||
if (m.columns.empty()) {
|
||||
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)) {
|
||||
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);
|
||||
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(),
|
||||
w.meta.columns,
|
||||
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()
|
||||
&& !g_input_path.empty()) {
|
||||
ge::TableMetadata m;
|
||||
if (ge::tableview_get_metadata(g_input_path.c_str(),
|
||||
ge::NodeGroupsMeta m;
|
||||
if (ge::node_groups_get_metadata(g_input_path.c_str(),
|
||||
g_app.promote_table_id.c_str(), &m)) {
|
||||
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(),
|
||||
m.duckdb_path_abs.c_str(),
|
||||
m.table_name.c_str(),
|
||||
@@ -1763,8 +1763,8 @@ static void render() {
|
||||
new_id, sizeof(new_id))) {
|
||||
std::fprintf(stdout, "[promote] %s -> %s\n",
|
||||
g_app.promote_row_id.c_str(), new_id);
|
||||
auto it = g_app.table_windows.find(g_app.promote_table_id);
|
||||
if (it != g_app.table_windows.end()) it->second.page_dirty = true;
|
||||
auto it = g_app.node_groups_windows.find(g_app.promote_table_id);
|
||||
if (it != g_app.node_groups_windows.end()) it->second.page_dirty = true;
|
||||
reload_after_mutation();
|
||||
g_app.want_focus_entity = true;
|
||||
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()
|
||||
&& !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())) {
|
||||
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();
|
||||
}
|
||||
g_app.want_demote_entity = false;
|
||||
@@ -1806,17 +1806,17 @@ static void render() {
|
||||
if (g_app.want_import) {
|
||||
g_app.want_import = false;
|
||||
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);
|
||||
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_table_buf,
|
||||
ge::INGEST_AUTO, &err)) {
|
||||
g_app.import_error = "Ingest failed: " + err;
|
||||
} else {
|
||||
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_duckdb_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 (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;
|
||||
const char* lbl = graph::graph_label(&g_graph, g_graph.nodes[n].label_idx);
|
||||
g_app.table_filter_group_name = lbl ? lbl : sql_id;
|
||||
@@ -2007,7 +2007,7 @@ static void render() {
|
||||
&get_label_cb, nullptr);
|
||||
}
|
||||
// Table node overlay (issue 0010) — encima de las labels.
|
||||
ge::views_table_overlay(g_app);
|
||||
ge::views_node_groups_overlay(g_app);
|
||||
}
|
||||
ImGui::End();
|
||||
} else {
|
||||
@@ -2051,7 +2051,7 @@ static void render() {
|
||||
ge::views_table(g_app);
|
||||
|
||||
// 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);
|
||||
|
||||
// Jobs panel (issue 0026) — flotante, dockeable.
|
||||
@@ -2181,7 +2181,7 @@ int main(int argc, char** argv) {
|
||||
return test_types_yaml_roundtrip(argv[++i]);
|
||||
} else if (std::strcmp(a, "--test-duckdb") == 0 && i + 1 < argc) {
|
||||
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);
|
||||
return 2;
|
||||
}
|
||||
@@ -2209,25 +2209,25 @@ int main(int argc, char** argv) {
|
||||
duckdb_disconnect(&cn); duckdb_close(&db);
|
||||
|
||||
int64_t total = 0;
|
||||
if (!ge::tableview_count(p, "people", nullptr, &total) || total != 1000000) {
|
||||
std::fprintf(stderr, "[tableview_count] expected 1000000, got %lld\n",
|
||||
if (!ge::node_groups_count(p, "people", nullptr, &total) || total != 1000000) {
|
||||
std::fprintf(stderr, "[node_groups_count] expected 1000000, got %lld\n",
|
||||
(long long)total);
|
||||
return 2;
|
||||
}
|
||||
std::vector<std::string> cols = { "name", "age" };
|
||||
std::vector<ge::TablePageRow> page;
|
||||
if (!ge::tableview_page(p, "people", "id", cols, nullptr,
|
||||
std::vector<ge::NodeGroupsRow> page;
|
||||
if (!ge::node_groups_page(p, "people", "id", cols, nullptr,
|
||||
nullptr, nullptr, 500000, 10, &page)) {
|
||||
std::fprintf(stderr, "[tableview_page] failed\n");
|
||||
std::fprintf(stderr, "[node_groups_page] failed\n");
|
||||
return 2;
|
||||
}
|
||||
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());
|
||||
return 2;
|
||||
}
|
||||
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(),
|
||||
page[0].values.size() > 0 ? page[0].values[0].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 "../../../../cpp/vendor/sqlite3/sqlite3.h"
|
||||
@@ -62,7 +62,7 @@ bool duck_query_silent(duckdb_connection cn, const char* sql) {
|
||||
|
||||
} // namespace
|
||||
|
||||
bool tableview_smoke_test(const char* duckdb_path) {
|
||||
bool node_groups_smoke_test(const char* duckdb_path) {
|
||||
DuckHandle h;
|
||||
if (!h.open(duckdb_path)) return false;
|
||||
duckdb_result r;
|
||||
@@ -76,7 +76,7 @@ bool tableview_smoke_test(const char* duckdb_path) {
|
||||
return ok;
|
||||
}
|
||||
|
||||
bool tableview_count(const char* duckdb_path,
|
||||
bool node_groups_count(const char* duckdb_path,
|
||||
const char* duck_table,
|
||||
const char* sql_filter,
|
||||
int64_t* out)
|
||||
@@ -94,7 +94,7 @@ bool tableview_count(const char* duckdb_path,
|
||||
}
|
||||
duckdb_result r;
|
||||
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_destroy_result(&r);
|
||||
return false;
|
||||
@@ -106,7 +106,7 @@ bool tableview_count(const char* duckdb_path,
|
||||
return true;
|
||||
}
|
||||
|
||||
bool tableview_page(const char* duckdb_path,
|
||||
bool node_groups_page(const char* duckdb_path,
|
||||
const char* duck_table,
|
||||
const char* id_column,
|
||||
const std::vector<std::string>& columns,
|
||||
@@ -114,7 +114,7 @@ bool tableview_page(const char* duckdb_path,
|
||||
const char* ops_db,
|
||||
const char* row_type,
|
||||
int64_t offset, int64_t limit,
|
||||
std::vector<TablePageRow>* out)
|
||||
std::vector<NodeGroupsRow>* out)
|
||||
{
|
||||
if (!out) return false;
|
||||
out->clear();
|
||||
@@ -177,7 +177,7 @@ bool tableview_page(const char* duckdb_path,
|
||||
duckdb_result r;
|
||||
if (duckdb_query(h.cn, sel.c_str(), &r) == DuckDBError) {
|
||||
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());
|
||||
duckdb_destroy_result(&r);
|
||||
return false;
|
||||
@@ -186,7 +186,7 @@ bool tableview_page(const char* duckdb_path,
|
||||
idx_t cols = duckdb_column_count(&r);
|
||||
out->reserve((size_t)rows);
|
||||
for (idx_t row = 0; row < rows; ++row) {
|
||||
TablePageRow tr;
|
||||
NodeGroupsRow tr;
|
||||
// col 0 = id
|
||||
if (!duckdb_value_is_null(&r, 0, row)) {
|
||||
char* v = duckdb_value_varchar(&r, 0, row);
|
||||
@@ -220,7 +220,7 @@ bool tableview_page(const char* duckdb_path,
|
||||
return true;
|
||||
}
|
||||
|
||||
bool tableview_create(const char* ops_db,
|
||||
bool node_groups_create(const char* ops_db,
|
||||
const char* name,
|
||||
const char* duckdb_path,
|
||||
const char* duck_table,
|
||||
@@ -317,7 +317,7 @@ static std::string normalize_path(std::string 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 (is_absolute(maybe_rel)) return normalize_path(maybe_rel);
|
||||
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);
|
||||
}
|
||||
|
||||
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)
|
||||
{
|
||||
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* flt_p = sqlite3_column_text(st, 3);
|
||||
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;
|
||||
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,
|
||||
&total)) {
|
||||
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;
|
||||
}
|
||||
out->emplace(fnv1a64((const char*)id_p), total);
|
||||
@@ -415,7 +415,7 @@ std::vector<std::string> parse_json_string_array(const char* s) {
|
||||
|
||||
} // namespace
|
||||
|
||||
bool tableview_list_columns(const char* duckdb_path,
|
||||
bool node_groups_list_columns(const char* duckdb_path,
|
||||
const char* duck_table,
|
||||
std::vector<std::string>* out)
|
||||
{
|
||||
@@ -438,11 +438,11 @@ bool tableview_list_columns(const char* duckdb_path,
|
||||
return true;
|
||||
}
|
||||
|
||||
bool tableview_get_metadata(const char* ops_db, const char* entity_id,
|
||||
TableMetadata* out)
|
||||
bool node_groups_get_metadata(const char* ops_db, const char* entity_id,
|
||||
NodeGroupsMeta* out)
|
||||
{
|
||||
if (!ops_db || !entity_id || !out) return false;
|
||||
*out = TableMetadata{};
|
||||
*out = NodeGroupsMeta{};
|
||||
out->entity_id = entity_id;
|
||||
sqlite3* db = nullptr;
|
||||
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";
|
||||
out->columns = parse_json_string_array(cols_json.c_str());
|
||||
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;
|
||||
}
|
||||
sqlite3_finalize(st);
|
||||
@@ -521,7 +521,7 @@ bool exec_metadata_patch(const char* ops_db, const char* entity_id,
|
||||
|
||||
} // 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)
|
||||
{
|
||||
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());
|
||||
}
|
||||
|
||||
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)
|
||||
{
|
||||
if (!ops_db || !entity_id) return false;
|
||||
@@ -586,7 +586,7 @@ bool find_existing_promotion(const char* ops_db, const char* duckdb_path,
|
||||
|
||||
} // 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* duckdb_path,
|
||||
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;
|
||||
sqlite3* db = nullptr;
|
||||
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
|
||||
|
||||
bool tableview_ingest_file(const char* duckdb_path,
|
||||
bool node_groups_ingest_file(const char* duckdb_path,
|
||||
const char* file_path,
|
||||
const char* dest_table,
|
||||
IngestKind kind,
|
||||
+26
-26
@@ -4,10 +4,10 @@
|
||||
#include <unordered_map>
|
||||
#include <vector>
|
||||
|
||||
// Vista tabular respaldada por DuckDB (issue 0010). Cada nodo `Table` del
|
||||
// grafo apunta via metadata a un archivo `.duckdb` y a una tabla dentro
|
||||
// de el. Las filas viven en DuckDB; el grafo solo materializa las que
|
||||
// se "promueven" a entidades (issue 0011).
|
||||
// NodeGroups — vista tabular respaldada por DuckDB (issue 0010, renombrada
|
||||
// en 0036a). Cada nodo `Table` del grafo apunta via metadata a un archivo
|
||||
// `.duckdb` y a una tabla dentro de el. Las filas viven en DuckDB; el grafo
|
||||
// solo materializa las que se "promueven" a entidades (issue 0011).
|
||||
//
|
||||
// Convencion de paths: `metadata.duckdb_path` es relativo al directorio del
|
||||
// proyecto (la raiz donde vive operations.db). El caller resuelve a path
|
||||
@@ -15,7 +15,7 @@
|
||||
|
||||
namespace ge {
|
||||
|
||||
struct TablePageRow {
|
||||
struct NodeGroupsRow {
|
||||
std::string id; // valor del id_column en duckdb (key natural)
|
||||
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
|
||||
@@ -25,7 +25,7 @@ struct TablePageRow {
|
||||
// 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
|
||||
// estan vacios.
|
||||
bool tableview_create(const char* ops_db,
|
||||
bool node_groups_create(const char* ops_db,
|
||||
const char* name,
|
||||
const char* duckdb_path,
|
||||
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
|
||||
// `sql_filter` (clausula WHERE sin la palabra WHERE — vacio = sin filtro).
|
||||
// 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* sql_filter,
|
||||
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`
|
||||
// computado via LEFT JOIN contra ops.entities (DuckDB attach a SQLite).
|
||||
// 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* id_column,
|
||||
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* row_type, // discriminante en ops.entities
|
||||
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
|
||||
// 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.
|
||||
// Si es absoluto (empieza por '/' o '<letra>:' en Windows), se devuelve
|
||||
// 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
|
||||
// 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
|
||||
// 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.
|
||||
@@ -73,7 +73,7 @@ struct TableCounts {
|
||||
// user_data hash (fnv1a64 del entity id) -> total filas tras filter_sql.
|
||||
// -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);
|
||||
|
||||
// ----------------------------------------------------------------------------
|
||||
@@ -81,11 +81,11 @@ bool tableview_refresh_counts(const char* ops_db,
|
||||
// ----------------------------------------------------------------------------
|
||||
|
||||
// Snapshot de la metadata relevante de un nodo Table. Caller-owned strings.
|
||||
struct TableMetadata {
|
||||
struct NodeGroupsMeta {
|
||||
std::string entity_id;
|
||||
std::string name;
|
||||
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 row_type;
|
||||
std::string id_column;
|
||||
@@ -98,17 +98,17 @@ struct TableMetadata {
|
||||
// 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
|
||||
// los campos faltantes (id_column='id', label_column='name').
|
||||
bool tableview_get_metadata(const char* ops_db, const char* entity_id,
|
||||
TableMetadata* out);
|
||||
bool node_groups_get_metadata(const char* ops_db, const char* entity_id,
|
||||
NodeGroupsMeta* out);
|
||||
|
||||
// Persiste el flag `expanded` en la metadata. Idempotente. Devuelve false
|
||||
// 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);
|
||||
|
||||
// Sobrescribe el array `columns` en la metadata. Llamar tras editar columnas
|
||||
// desde la UI o tras tableview_create cuando se descubren columnas.
|
||||
bool tableview_set_columns(const char* ops_db, const char* entity_id,
|
||||
// desde la UI o tras node_groups_create cuando se descubren columnas.
|
||||
bool node_groups_set_columns(const char* ops_db, const char* entity_id,
|
||||
const std::vector<std::string>& columns);
|
||||
|
||||
// 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
|
||||
// CONTAINS_ROW (idempotente) entre la tabla y la nueva entidad para que el
|
||||
// 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* duckdb_path,
|
||||
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
|
||||
// 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 {
|
||||
INGEST_AUTO = 0, // detecta por extension
|
||||
INGEST_CSV = 1,
|
||||
@@ -147,15 +147,15 @@ enum IngestKind {
|
||||
// Importa un fichero CSV/Parquet/JSON al `.duckdb`. Crea el .duckdb si no
|
||||
// existe. Si la tabla destino existe, falla (no sobrescribe — explicit fail).
|
||||
// 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* dest_table,
|
||||
IngestKind kind,
|
||||
std::string* out_error);
|
||||
|
||||
// Lista los nombres de columna de la tabla DuckDB. Para popular la lista
|
||||
// `columns` por defecto en tableview_create.
|
||||
bool tableview_list_columns(const char* duckdb_path,
|
||||
// `columns` por defecto en node_groups_create.
|
||||
bool node_groups_list_columns(const char* duckdb_path,
|
||||
const char* duck_table,
|
||||
std::vector<std::string>* out);
|
||||
|
||||
@@ -306,15 +306,15 @@ void views_toolbar(AppState& app) {
|
||||
{
|
||||
char btn[64];
|
||||
std::snprintf(btn, sizeof(btn), TI_TABLE " Tables (%zu)",
|
||||
app.table_windows.size());
|
||||
app.node_groups_windows.size());
|
||||
if (button(btn, ButtonVariant::Subtle)) {
|
||||
ImGui::OpenPopup("##tables_menu");
|
||||
}
|
||||
if (ImGui::BeginPopup("##tables_menu")) {
|
||||
if (app.table_windows.empty()) {
|
||||
ImGui::TextDisabled("(no expanded tables)");
|
||||
if (app.node_groups_windows.empty()) {
|
||||
ImGui::TextDisabled("(no open NodeGroups)");
|
||||
} else {
|
||||
for (auto& kv : app.table_windows) {
|
||||
for (auto& kv : app.node_groups_windows) {
|
||||
bool checked = kv.second.open;
|
||||
char lbl[160];
|
||||
std::snprintf(lbl, sizeof(lbl), "%s (%lld rows)",
|
||||
@@ -326,7 +326,7 @@ void views_toolbar(AppState& app) {
|
||||
}
|
||||
ImGui::Separator();
|
||||
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();
|
||||
@@ -1873,12 +1873,12 @@ void views_table(AppState& app) {
|
||||
// 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;
|
||||
GraphData& g = *app.graph;
|
||||
|
||||
// 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) {
|
||||
const GraphNode& n = g.nodes[i];
|
||||
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);
|
||||
if (!p) continue;
|
||||
std::string id = (const char*)p;
|
||||
TableMetadata meta;
|
||||
if (tableview_get_metadata(ops_db, id.c_str(), &meta)) {
|
||||
NodeGroupsMeta meta;
|
||||
if (node_groups_get_metadata(ops_db, id.c_str(), &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);
|
||||
|
||||
// Quitar las que ya no estan expanded.
|
||||
for (auto it = app.table_windows.begin(); it != app.table_windows.end(); ) {
|
||||
if (live.find(it->first) == live.end()) it = app.table_windows.erase(it);
|
||||
for (auto it = app.node_groups_windows.begin(); it != app.node_groups_windows.end(); ) {
|
||||
if (live.find(it->first) == live.end()) it = app.node_groups_windows.erase(it);
|
||||
else ++it;
|
||||
}
|
||||
// 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
|
||||
// haber cambiado).
|
||||
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();
|
||||
w.meta = std::move(kv.second);
|
||||
w.open = true;
|
||||
@@ -1938,18 +1938,18 @@ void views_table_windows_sync(AppState& app, const char* ops_db) {
|
||||
}
|
||||
}
|
||||
|
||||
void views_table_window(AppState& app) {
|
||||
if (app.table_windows.empty()) return;
|
||||
void views_node_groups_window(AppState& app) {
|
||||
if (app.node_groups_windows.empty()) return;
|
||||
GraphData* g = app.graph;
|
||||
GraphViewportState* vp = app.viewport;
|
||||
|
||||
for (auto& kv : app.table_windows) {
|
||||
TableMetadata& m = kv.second.meta;
|
||||
AppState::TableWindowState& w = kv.second;
|
||||
for (auto& kv : app.node_groups_windows) {
|
||||
NodeGroupsMeta& m = kv.second.meta;
|
||||
AppState::NodeGroupsWindowState& w = kv.second;
|
||||
|
||||
char title[160];
|
||||
std::snprintf(title, sizeof(title), TI_TABLE " %s##te_%s",
|
||||
m.name.empty() ? "Table" : m.name.c_str(),
|
||||
std::snprintf(title, sizeof(title), TI_TABLE " NodeGroups: %s##te_%s",
|
||||
m.name.empty() ? "(unnamed)" : m.name.c_str(),
|
||||
m.entity_id.c_str());
|
||||
ImGui::SetNextWindowSize(ImVec2(640, 460), ImGuiCond_FirstUseEver);
|
||||
if (!ImGui::Begin(title, &w.open)) { ImGui::End(); continue; }
|
||||
@@ -1987,7 +1987,7 @@ void views_table_window(AppState& app) {
|
||||
// avanzamos offset.
|
||||
const int64_t page_size = 200;
|
||||
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::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
|
||||
// table_windows y comparando `open`.
|
||||
// node_groups_windows y comparando `open`.
|
||||
}
|
||||
|
||||
bool views_import_dataset_modal(AppState& app) {
|
||||
@@ -2132,7 +2132,7 @@ bool views_import_dataset_modal(AppState& app) {
|
||||
// Table node overlay (issue 0010)
|
||||
// ----------------------------------------------------------------------------
|
||||
|
||||
void views_table_overlay(AppState& app) {
|
||||
void views_node_groups_overlay(AppState& app) {
|
||||
if (!app.graph || !app.viewport) return;
|
||||
GraphData& g = *app.graph;
|
||||
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;
|
||||
|
||||
int64_t count = -1;
|
||||
auto it = app.table_node_counts.find(n.user_data);
|
||||
if (it != app.table_node_counts.end()) count = it->second;
|
||||
auto it = app.node_groups_counts.find(n.user_data);
|
||||
if (it != app.node_groups_counts.end()) count = it->second;
|
||||
if (count < 0) continue;
|
||||
|
||||
char buf[64];
|
||||
|
||||
@@ -5,7 +5,7 @@
|
||||
|
||||
#include "types_registry.h"
|
||||
#include "entity_ops.h"
|
||||
#include "tableview.h"
|
||||
#include "node_groups.h"
|
||||
|
||||
#include <cstdint>
|
||||
#include <unordered_map>
|
||||
@@ -158,23 +158,24 @@ struct AppState {
|
||||
// ---- Table node (issue 0010) ------------------------------------------
|
||||
// Cache de conteo de filas por nodo Table indexado por user_data hash.
|
||||
// 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) --------------------------------
|
||||
// Estado runtime por ventana de Table expandida. Una entrada por
|
||||
// entity_id de Table que el usuario haya expandido. La ventana se cierra
|
||||
// cuando set_expanded(false) — ya sea desde context menu o cerrando la
|
||||
// ImGui window (que pone el flag a false automaticamente).
|
||||
struct TableWindowState {
|
||||
TableMetadata meta; // refrescada cada vez que entity cambia
|
||||
int64_t total_rows = 0;
|
||||
int64_t offset = 0;
|
||||
std::vector<TablePageRow> page;
|
||||
bool page_dirty = true;
|
||||
bool open = true; // bound a ImGui::Begin
|
||||
std::string last_error; // ultimo error de query (vacio = OK)
|
||||
// ---- NodeGroups window (issue 0011, renombrado en 0036a) --------------
|
||||
// Estado runtime por ventana de NodeGroups (un Table-typed expandido).
|
||||
// Una entrada por entity_id de Table que el usuario haya expandido. La
|
||||
// ventana se cierra cuando set_expanded(false) — ya sea desde context
|
||||
// menu o cerrando la ImGui window (que pone el flag a false
|
||||
// automaticamente).
|
||||
struct NodeGroupsWindowState {
|
||||
NodeGroupsMeta meta; // refrescada cada vez que entity cambia
|
||||
int64_t total_rows = 0;
|
||||
int64_t offset = 0;
|
||||
std::vector<NodeGroupsRow> page;
|
||||
bool page_dirty = true;
|
||||
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.
|
||||
bool want_promote_row = false;
|
||||
@@ -196,9 +197,9 @@ struct AppState {
|
||||
bool want_import = false;
|
||||
std::string import_error;
|
||||
|
||||
// Toggle expanded desde context menu del viewport.
|
||||
bool want_toggle_expanded = false;
|
||||
std::string toggle_expanded_id;
|
||||
// Toggle NodeGroups window desde context menu del viewport.
|
||||
bool want_toggle_nodegroups = false;
|
||||
std::string toggle_nodegroups_id;
|
||||
|
||||
// ---- Table view (issue 0004) -------------------------------------------
|
||||
// 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.
|
||||
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
|
||||
// con `open=true`. Cabecera con nombres de columnas. Filas paginadas con
|
||||
// ImGuiListClipper consumiendo el page cache; al cambiar el offset, marca
|
||||
// dirty para que main.cpp refresque via tableview_page. Doble click en
|
||||
// fila no promovida -> setea promote_table_id/promote_row_id; promovida
|
||||
// -> focus_entity_id. Cerrar la ventana setea expanded=false en BD.
|
||||
void views_table_window(AppState& app);
|
||||
// Renderiza una ventana ImGui dockeable por cada NodeGroups en
|
||||
// node_groups_windows con `open=true`. Cabecera con nombres de columnas.
|
||||
// Filas paginadas con ImGuiListClipper consumiendo el page cache; al
|
||||
// cambiar el offset, marca dirty para que main.cpp refresque via
|
||||
// node_groups_page. Doble click en fila no promovida -> setea
|
||||
// promote_table_id/promote_row_id; promovida -> focus_entity_id. Cerrar la
|
||||
// ventana setea expanded=false en BD.
|
||||
void views_node_groups_window(AppState& app);
|
||||
|
||||
// Modal "Import dataset..." — formulario para crear una tabla DuckDB
|
||||
// desde CSV/Parquet/JSON y registrar el nodo Table correspondiente.
|
||||
bool views_import_dataset_modal(AppState& app);
|
||||
|
||||
// Sincroniza table_windows con la metadata.expanded de cada nodo Table.
|
||||
// Llamar tras load + tras mutaciones que cambien expanded. Crea entradas
|
||||
// para nuevos expanded y borra las que ya no aplican.
|
||||
void views_table_windows_sync(AppState& app, const char* ops_db);
|
||||
// Sincroniza node_groups_windows con la metadata.expanded de cada nodo
|
||||
// Table. Llamar tras load + tras mutaciones que cambien expanded. Crea
|
||||
// entradas para nuevos expanded y borra las que ya no aplican.
|
||||
void views_node_groups_windows_sync(AppState& app, const char* ops_db);
|
||||
|
||||
// ---- Table node overlay (issue 0010) ------------------------------------
|
||||
|
||||
// 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
|
||||
// del item viewport. No interactua con eventos; el hit-testing del nodo
|
||||
// sigue usandolo el viewport circular de fondo.
|
||||
void views_table_overlay(AppState& app);
|
||||
void views_node_groups_overlay(AppState& app);
|
||||
|
||||
// ---- Table view (issue 0004) --------------------------------------------
|
||||
|
||||
|
||||
Reference in New Issue
Block a user