feat: docking host + add-node toolbar + node context menu
- Dockspace host (PassthruCentralNode) bajo la toolbar para que las
ventanas Viewport/Legend/Inspector/Stats puedan dockearse dentro de la
app principal.
- Toolbar: input "Add node" con auto-deteccion de tipo (text/email/
ip_address/url/domain/phone). Insert en operations.db + reload.
- Context menu (right-click sobre nodo): Change type, Duplicate, Delete,
submenu "Run enricher" (placeholder hasta issues 0001-0003).
- Inspector: vecinos ahora muestran etiqueta de relacion ("-> employs",
"<- owns") usando rel_types[].name como label de arista.
- Default relation label k_default_relation_name="RELATED_TO" para
relaciones creadas sin nombre semantico explicito.
- Indice EntityIndex (FNV1a hash -> sql id) reconstruido tras cada load
para resolver mutaciones desde el grafo en memoria.
Issues planteadas para iteraciones siguientes:
- 0001: chat con Claude sobre el grafo (HTTP + tool-use)
- 0002: enricher GLiNER+GLiREL desde nodo texto
- 0003: enricher web (fetch URL/dominio + extract text)
- 0004: vista tabla por tipo de entidad
This commit is contained in:
@@ -1,4 +1,5 @@
|
||||
#include "views.h"
|
||||
#include "entity_ops.h"
|
||||
|
||||
#include "viz/graph_types.h"
|
||||
#include "viz/graph_viewport.h"
|
||||
@@ -93,6 +94,24 @@ void views_toolbar(AppState& app) {
|
||||
if (button(TI_FOLDER " Open file...", ButtonVariant::Secondary)) {
|
||||
app.show_open_modal = true;
|
||||
}
|
||||
ImGui::SameLine();
|
||||
|
||||
// Add node — input + auto-deteccion de tipo. Enter o boton "Add" lo
|
||||
// confirman; main.cpp inserta en operations.db y dispara reload.
|
||||
ImGui::SetNextItemWidth(220);
|
||||
DetectedType dt = detect_type(app.add_buf);
|
||||
ImGuiInputTextFlags flags = ImGuiInputTextFlags_EnterReturnsTrue;
|
||||
char hint[64];
|
||||
std::snprintf(hint, sizeof(hint), "Add node (%s)...", detected_type_name(dt));
|
||||
if (ImGui::InputTextWithHint("##addnode", hint, app.add_buf, sizeof(app.add_buf), flags)) {
|
||||
app.want_add_node = true;
|
||||
}
|
||||
ImGui::SameLine();
|
||||
if (button(TI_PLUS " Add", ButtonVariant::Primary)) {
|
||||
app.want_add_node = true;
|
||||
}
|
||||
ImGui::SameLine();
|
||||
ImGui::TextDisabled("[%s]", detected_type_name(dt));
|
||||
toolbar_separator();
|
||||
|
||||
ImGui::TextUnformatted("Layout:");
|
||||
@@ -268,12 +287,17 @@ void views_inspector(AppState& app) {
|
||||
for (int e = 0; e < g.edge_count && neighbor_count < 64; ++e) {
|
||||
const GraphEdge& edge = g.edges[e];
|
||||
int other = -1;
|
||||
if (edge.source == (uint32_t)idx) other = (int)edge.target;
|
||||
else if (edge.target == (uint32_t)idx) other = (int)edge.source;
|
||||
const char* arrow = " ";
|
||||
if (edge.source == (uint32_t)idx) { other = (int)edge.target; arrow = "->"; }
|
||||
else if (edge.target == (uint32_t)idx) { other = (int)edge.source; arrow = "<-"; }
|
||||
if (other < 0 || other >= g.node_count) continue;
|
||||
const char* olbl = graph::graph_label(&g, g.nodes[other].label_idx);
|
||||
const char* rname = (edge.type_id < (uint16_t)g.rel_type_count &&
|
||||
g.rel_types[edge.type_id].name)
|
||||
? g.rel_types[edge.type_id].name
|
||||
: k_default_relation_name;
|
||||
char buf[256];
|
||||
std::snprintf(buf, sizeof(buf), "[%d] %s", other,
|
||||
std::snprintf(buf, sizeof(buf), "%s %s [%d] %s", arrow, rname, other,
|
||||
olbl && *olbl ? olbl : "(unnamed)");
|
||||
if (ImGui::Selectable(buf)) {
|
||||
graph_viewport_clear_selection(g, *app.viewport);
|
||||
|
||||
Reference in New Issue
Block a user