diff --git a/backend/resources/app/templates/graph-console.tmpl b/backend/resources/app/templates/graph-console.tmpl
index 605c6965de..9e5557a00a 100644
--- a/backend/resources/app/templates/graph-console.tmpl
+++ b/backend/resources/app/templates/graph-console.tmpl
@@ -865,7 +865,8 @@ Graph Console
return [
{ id: "auto-fit", value: "auto-fit" },
{ id: "request-fullscreen", value: "expand" },
- { id: "exit-fullscreen", value: "restore" }
+ { id: "exit-fullscreen", value: "restore" },
+ { id: "export", value: "export-png" }
];
},
onClick: function (value) {
@@ -875,8 +876,21 @@ Graph Console
setExpanded(true);
} else if (value === "restore") {
setExpanded(false);
+ } else if (value === "export-png") {
+ exportGraphPng();
}
}
+ }, {
+ key: "tooltip",
+ type: "tooltip",
+ getContent: function (_evt, items) {
+ return Promise.resolve(items.map(function (it) {
+ const d = it.data || {};
+ return "
" + escapeHtml(d.table || "") + " "
+ + escapeHtml(d.label || "") + "
"
+ + escapeHtml(String(it.id)) + "
";
+ }).join(""));
+ }
}]
};
if (layoutCfg) opts.layout = layoutCfg;
@@ -896,6 +910,23 @@ Graph Console
// marker set (hexagon→circle, star→cross), breaking glyph identity.
// Entries reflect the *displayed* data only: node tables in roster
// order (unknown tables appended with the fallback style), then rels.
+ // Clean graph-only capture (no page chrome): the whole laid-out graph
+ // regardless of viewport, downloaded as PNG. Also the fast path for
+ // agents debugging the console — no full-page screenshot needed.
+ function exportGraphPng() {
+ if (!g6graph) return;
+ g6graph.toDataURL({ mode: "overall" })
+ .then(function (dataUrl) {
+ const a = document.createElement("a");
+ a.href = dataUrl;
+ a.download = "graph-" + (lastGraphData ? lastGraphData.revn : "view") + ".png";
+ a.click();
+ })
+ .catch(function (err) {
+ graphViewStatus.textContent = "export error: " + err;
+ });
+ }
+
function renderGraphLegend(data) {
const el = document.getElementById("graph-legend");
if (!el) return;