From e08ac33700b8f52bb15e585a24615a76bae9bec0 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=C3=81lvaro=20Tejero=20Cantero?= Date: Thu, 16 Jul 2026 19:01:42 +0200 Subject: [PATCH] :lipstick: Graph console QoL round MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit "Show result in graph view" moves into an actions bar above the results table; results scroll inside a 45vh container (client and server render paths); the Loaded-session fieldset gains a live "Graph size" line that stays fresh through skipped repaints; IsInstanceOf mid-edge label becomes the spelled-out rel name (∈ read as membership, not derivation) with the legend falling back to the dash-arrow for long syms. Signed-off-by: Álvaro Tejero Cantero --- .../app/templates/graph-console.tmpl | 27 ++++++++++++++++--- 1 file changed, 23 insertions(+), 4 deletions(-) diff --git a/backend/resources/app/templates/graph-console.tmpl b/backend/resources/app/templates/graph-console.tmpl index f45928b951..6cc4b66add 100644 --- a/backend/resources/app/templates/graph-console.tmpl +++ b/backend/resources/app/templates/graph-console.tmpl @@ -59,6 +59,7 @@ Graph Console target="_blank">{{session.name}} ({{session.file-id}})
Loaded at revision: {{session.revn}}
Graph revision: {% if session.graph-revn %}{{session.graph-revn}}{% else %}{{session.revn}}{% endif %}
+ Graph size:
Schema: {{session.schema-version}}
Loaded at: {{session.loaded-at}}

@@ -125,6 +126,7 @@ Graph Console {% if query-result %}
Results ({{query-result.row-count}} rows{% if query-result.truncated? %}, truncated{% endif %}) +
@@ -143,6 +145,7 @@ Graph Console {% endfor %}
+
{% endif %} @@ -374,9 +377,12 @@ Graph Console // rel label rendered mid-edge (abacus viewer EDGE_SYM convention) — // dash variants alone cannot carry the growing rel roster. IsChildOf // is the unlabeled default (the background tree structure). + // `sym` may be a full rel name: no compact glyph reads as "derived from + // a template" (∈ wrongly connotes membership), so IsInstanceOf spells + // itself out; the legend falls back to the dash-arrow for long syms. const EDGE_STYLES = { "IsChildOf": { stroke: "#b3b0a8" }, - "IsInstanceOf": { stroke: "#8b98a9", lineDash: [4, 3], sym: "∈" } + "IsInstanceOf": { stroke: "#8b98a9", lineDash: [4, 3], sym: "IsInstanceOf" } }; const FALLBACK_EDGE_STYLE = { stroke: "#b3b0a8" }; // --- graph diff: add/remove marks with step fade -------------------- @@ -918,9 +924,10 @@ Graph Console }); Object.keys(rels).sort().forEach(function (rel) { const s = edgeStyle(rel); + const glyph = (s.sym && s.sym.length <= 2) ? s.sym : (s.lineDash ? "⇢" : "→"); items.push('' + '' - + escapeHtml(s.sym || (s.lineDash ? "⇢" : "→")) + ' ' + + escapeHtml(glyph) + ' ' + escapeHtml(rel) + ""); }); if (anyLiveMarks()) { @@ -1009,6 +1016,13 @@ Graph Console return resp.json(); }) .then(function (data) { + // Session-fieldset size line stays live even through skipped + // repaints (unlike the status line under the canvas). + const sizeEl = document.getElementById("graph-size"); + if (sizeEl) { + sizeEl.textContent = data.nodes.length + " nodes, " + + data.edges.length + " edges"; + } // Skip the repaint when the display projection is unchanged: a // change burst that only touches non-projected attrs (e.g. moving // shapes around) bumps revn but not the picture. @@ -1304,10 +1318,15 @@ Graph Console } const truncated = result["truncated?"] ? ", truncated" : ""; + // Actions bar sits above the table so a long result cannot push the + // "Show result in graph view" button out of sight; the table itself + // scrolls inside a capped container. let html = "
Results (" + escapeHtml(String(result["row-count"])) + " rows" + truncated + ")" + + "
" + + "
" + "" + ""; @@ -1332,7 +1351,7 @@ Graph Console html += ""; }); - html += "
"; + html += ""; output.innerHTML = html; // Offer to view the result as an induced subgraph: any UUID appearing @@ -1350,7 +1369,7 @@ Graph Console btn.type = "button"; btn.textContent = "Show result in graph view (" + ids.size + " ids)"; btn.addEventListener("click", function () { applyQueryFilter(ids); }); - output.appendChild(btn); + document.getElementById("graph-result-actions").appendChild(btn); } }