fix(dashboard): use vis.DataSet diffing to maintain zoom and physics simulation, and fix broken right-column flex HTML markup

This commit is contained in:
Riz Ashraf committed 2026-09-12 11:11:52 +01:00
1 parent 981a49327c
commit 5d37af1d74
1 file changed
+33 -24
+33 -24
View File
@@ -62,10 +62,7 @@
<button onclick="resetGraph()" title="Reset View">⌂</button>
</div>
<div id="network-container"></div>
</div>
</div>
<div class="right-column">
</div><div class="right-column">
<div class="panel search-panel">
<h2>Omni-Search</h2>
<input type="text" id="search-box" placeholder="Search memory (e.g., 'database')..." onkeyup="handleSearch(event)">
@@ -105,45 +102,54 @@
network.fit({ animation: { duration: 500, easingFunction: 'easeInOutQuad' } });
}
// Graph Initialization
// Graph Initialization
let network = null;
let nodesData = new vis.DataSet();
let edgesData = new vis.DataSet();
async function loadGraph() {
try {
const res = await fetch('/api/graph');
const data = await res.json();
const nodes = [];
const edges = [];
const newNodes = [];
const newEdges = [];
const nodeIds = new Set();
for (const [name, entity] of Object.entries(data.entities)) {
nodes.push({ id: name, label: name, title: entity.observations.join('\n') });
newNodes.push({ id: name, label: name, title: entity.observations.join('\n') });
nodeIds.add(name);
}
data.relations.forEach(r => {
if (nodeIds.has(r.from) && nodeIds.has(r.to)) {
edges.push({ from: r.from, to: r.to, label: r.relationType, arrows: 'to' });
newEdges.push({ id: r.from + '-' + r.to + '-' + r.relationType, from: r.from, to: r.to, label: r.relationType, arrows: 'to' });
}
});
const container = document.getElementById('network-container');
const graphData = { nodes: new vis.DataSet(nodes), edges: new vis.DataSet(edges) };
const options = {
nodes: { shape: 'dot', size: 16, font: { size: 12, color: '#333' }, color: { background: '#3498db', border: '#2980b9' } },
edges: { color: { color: '#bdc3c7' }, font: { size: 10, align: 'middle' } },
physics: { barnesHut: { gravitationalConstant: -2000, centralGravity: 0.3, springLength: 95 } }
};
// Sync nodes
const currentNodes = nodesData.getIds();
const toRemoveNodes = currentNodes.filter(id => !nodeIds.has(id));
nodesData.remove(toRemoveNodes);
nodesData.update(newNodes);
if (network) {
network.setData(graphData);
} else {
network = new vis.Network(container, graphData, options);
// Sync edges
const currentEdges = edgesData.getIds();
const newEdgeIds = new Set(newEdges.map(e => e.id));
const toRemoveEdges = currentEdges.filter(id => !newEdgeIds.has(id));
edgesData.remove(toRemoveEdges);
edgesData.update(newEdges);
if (!network) {
const container = document.getElementById('network-container');
const options = {
nodes: { shape: 'dot', size: 16, font: { size: 12, color: '#333' }, color: { background: '#3498db', border: '#2980b9' } },
edges: { color: { color: '#bdc3c7' }, font: { size: 10, align: 'middle' } },
physics: { barnesHut: { gravitationalConstant: -2000, centralGravity: 0.3, springLength: 95 } }
};
network = new vis.Network(container, { nodes: nodesData, edges: edgesData }, options);
}
} catch (err) {
console.error("Failed to load graph", err); document.getElementById("network-container").innerHTML = "<div style=\"color:red;padding:20px;\">Error loading graph: " + err.message + "</div>";
}
}
} catch (err) { console.error("Failed to load graph", err); document.getElementById("network-container").innerHTML = "<div style=`"color:red;padding:20px;`">Error loading graph: " + err.message + "</div>"; } }
// Kanban Board Initialization
async function loadTasks() {
@@ -228,3 +234,6 @@