aboutsummaryrefslogtreecommitdiffstats
path: root/src/api
diff options
context:
space:
mode:
authortv <tv@krebsco.de>2026-10-31 22:56:00 +0100
committertv <tv@krebsco.de>2026-10-31 22:56:00 +0100
commit7e90c16f6b52690548277a63f20ced63b0f9f2b7 (patch)
treecb3b1ab374e3a90646a90384920f8857c6bc5f87 /src/api
parent7cbf2366ab50c47b9111dfe7bab95f0808a25eac (diff)
dashboard: rearrange only new nodes in static layout
Diffstat (limited to 'src/api')
-rw-r--r--src/api/dashboard/dashboard.html27
1 files changed, 20 insertions, 7 deletions
diff --git a/src/api/dashboard/dashboard.html b/src/api/dashboard/dashboard.html
index 5854f61..1b4c2ba 100644
--- a/src/api/dashboard/dashboard.html
+++ b/src/api/dashboard/dashboard.html
@@ -460,14 +460,15 @@ function idealLength(count, width, height) {
return Math.min(width, height) / Math.max(2, Math.sqrt(count) * 1.5);
}
// `pairs`: [a, b, length], the length the link pulls towards (else `idealLength`).
-function layout(nodes, pairs, width, height) {
+// `only`, if given: the nodes laid out; the others stay where they are.
+function layout(nodes, pairs, width, height, only) {
for (const id of nodes) {
if (!positions.has(id)) {
positions.set(id, { x: width / 2 + (Math.random() - .5) * width / 2,
y: height / 2 + (Math.random() - .5) * height / 2 });
}
}
- for (let round = 0; round < 120; round++) relax(nodes, pairs, width, height, 1, 10);
+ for (let round = 0; round < 120; round++) relax(nodes, pairs, width, height, 1, 10, only);
}
// One round of the layout: every node moved by `gain` of the forces on it, at most
// `most` px. Returns how far the furthest moved. A gain of 1 overshoots where a node
@@ -650,6 +651,13 @@ function show(root, children) {
}
let lastShape = "";
+// The whole map laid out again with the next render (the window or the box resized),
+// not only nodes new to it.
+let relayout = true;
+function layoutAgain() {
+ relayout = true;
+ lastShape = "";
+}
function renderGraph() {
const root = document.getElementById("graph");
const width = root.clientWidth || 800, height = root.clientHeight || 560;
@@ -658,13 +666,18 @@ function renderGraph() {
const { intended, measured } = edges();
renderLegend(measured);
const pairs = lengths(intended, measured, nodes.length, width, height);
- // Laid out anew only when who's there or who links to whom changed.
+ // Laid out when who's there or who links to whom changed: only the nodes new to the
+ // map, the others staying where they are (links that came, after the first reports,
+ // don't move them); all of them at first, and when the map was resized.
const shape = nodes.join(",") + "|" + [...intended.keys(), ...measured.keys()].sort().join(",");
if (shape !== lastShape) {
for (const id of [...positions.keys()]) {
if (!nodes.includes(id)) { positions.delete(id); pinned.delete(id); }
}
- layout(nodes, pairs, width, height);
+ const fresh = new Set(nodes.filter(id => !positions.has(id)));
+ if (relayout) layout(nodes, pairs, width, height);
+ else if (fresh.size > 0) layout(nodes, pairs, width, height, fresh);
+ relayout = false;
lastShape = shape;
}
settleTo = { nodes, pairs, width, height };
@@ -1492,7 +1505,7 @@ connection.addEventListener("click", () => {
});
// Ages count on between events; nothing else changes without one.
setInterval(() => { if (live) render(true); }, 1000);
-window.addEventListener("resize", () => { lastShape = ""; render(); });
+window.addEventListener("resize", () => { layoutAgain(); render(); });
// --- Sizes: the space between boxes dragged to resize them, double-clicked to reset;
// kept in this browser ---
@@ -1575,7 +1588,7 @@ const resized = () => {
if (!resizing) return;
resizing = null;
main.classList.remove("resize-columns", "resize-rows");
- lastShape = "";
+ layoutAgain();
render();
};
main.addEventListener("pointerup", resized);
@@ -1585,7 +1598,7 @@ main.addEventListener("dblclick", event => {
if (!gap) return;
sizes[gap] = null;
applySizes();
- lastShape = "";
+ layoutAgain();
render();
});
</script>