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> <button onclick="resetGraph()" title="Reset View">⌂</button>
</div> </div>
<div id="network-container"></div> <div id="network-container"></div>
</div> </div><div class="right-column">
</div>
<div class="right-column">
<div class="panel search-panel"> <div class="panel search-panel">
<h2>Omni-Search</h2> <h2>Omni-Search</h2>
<input type="text" id="search-box" placeholder="Search memory (e.g., 'database')..." onkeyup="handleSearch(event)"> <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' } }); network.fit({ animation: { duration: 500, easingFunction: 'easeInOutQuad' } });
} }
// Graph Initialization // Graph Initialization
let network = null; let network = null;
let nodesData = new vis.DataSet();
let edgesData = new vis.DataSet();
async function loadGraph() { async function loadGraph() {
try { try {
const res = await fetch('/api/graph'); const res = await fetch('/api/graph');
const data = await res.json(); const data = await res.json();
const nodes = []; const newNodes = [];
const edges = []; const newEdges = [];
const nodeIds = new Set(); const nodeIds = new Set();
for (const [name, entity] of Object.entries(data.entities)) { 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); nodeIds.add(name);
} }
data.relations.forEach(r => { data.relations.forEach(r => {
if (nodeIds.has(r.from) && nodeIds.has(r.to)) { 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'); // Sync nodes
const graphData = { nodes: new vis.DataSet(nodes), edges: new vis.DataSet(edges) }; const currentNodes = nodesData.getIds();
const options = { const toRemoveNodes = currentNodes.filter(id => !nodeIds.has(id));
nodes: { shape: 'dot', size: 16, font: { size: 12, color: '#333' }, color: { background: '#3498db', border: '#2980b9' } }, nodesData.remove(toRemoveNodes);
edges: { color: { color: '#bdc3c7' }, font: { size: 10, align: 'middle' } }, nodesData.update(newNodes);
physics: { barnesHut: { gravitationalConstant: -2000, centralGravity: 0.3, springLength: 95 } }
}; // 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) { if (!network) {
network.setData(graphData); const container = document.getElementById('network-container');
} else { const options = {
network = new vis.Network(container, graphData, 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) { } 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>"; } }
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 // Kanban Board Initialization
async function loadTasks() { async function loadTasks() {
@@ -228,3 +234,6 @@