From a824ff9914d983ddfa4556ac051ea2092660cf8d Mon Sep 17 00:00:00 2001 From: Aaron Kimbrell Date: Tue, 29 Sep 2026 18:41:35 -0500 Subject: [PATCH] feat(dashboard): drag the Network page's boxes around; links coloured against the busiest link The layout the viewer makes is kept in the browser, with a Reset layout button. A link's colour now compares it with the busiest link right now instead of its own recent peak, which made steady links look saturated. Co-Authored-By: Claude Opus 5.5 --- dDashboardServer/static/js/network-graph.js | 12 ++++- dDashboardServer/templates/network.jinja2 | 56 +++++++++++++++++---- tests/dWebTests/network-graph.test.mjs | 8 +++ 3 files changed, 65 insertions(+), 11 deletions(-) diff --git a/dDashboardServer/static/js/network-graph.js b/dDashboardServer/static/js/network-graph.js index fea145a67..a02365084 100644 --- a/dDashboardServer/static/js/network-graph.js +++ b/dDashboardServer/static/js/network-graph.js @@ -188,9 +188,10 @@ /** * Positions: columns across `width` (node centres), rows in each column spread over the tallest column's height. + * `moved` ({id: {fx, y}}, optional) places nodes the viewer dragged: fx is a fraction of the width, y in pixels. * Returns {width, height, nodeWidth, nodeHeight, nodes: {id: {x, y}}, paths: {edgeId: {fwd, back}}}. */ - function layout(graph, totalWidth) { + function layout(graph, totalWidth, moved) { var nodeWidth = Math.max(110, Math.min(170, Math.floor(totalWidth / 6.4))), nodeHeight = 54, rowHeight = 74, pad = 16; var byColumn = COLUMNS.map(function () { return []; }); graph.nodes.forEach(function (n) { byColumn[n.column].push(n); }); @@ -203,6 +204,13 @@ var step = (height - pad * 2) / Math.max(column.length, 1); column.forEach(function (n, i) { at[n.id] = { x: xs[c], y: Math.round(pad + step * (i + 0.5)), column: c }; }); }); + Object.keys(moved || {}).forEach(function (id) { + var m = moved[id], a = at[id]; + if (!a || !(m.fx >= 0) || !(m.y >= 0)) return; + a.x = Math.round(Math.min(totalWidth - pad - nodeWidth / 2, Math.max(pad + nodeWidth / 2, m.fx * totalWidth))); + a.y = Math.round(Math.max(pad + nodeHeight / 2, m.y)); + height = Math.max(height, a.y + nodeHeight / 2 + pad); + }); var paths = {}; graph.edges.forEach(function (e) { var a = at[e.from], b = at[e.to]; @@ -215,7 +223,7 @@ // A curve from a's side to b's side, moved `offset` px to its left (so the two directions sit side by side) function curve(a, b, nodeWidth, offset) { var half = nodeWidth / 2, x1, x2, c1, c2; - if (a.x === b.x) { + if (Math.abs(a.x - b.x) < nodeWidth) { // Same column: bulge out on the side with room (left of the rightmost column, right of the others) var side = a.column === COLUMNS.length - 1 ? -1 : 1; x1 = x2 = a.x + side * half; diff --git a/dDashboardServer/templates/network.jinja2 b/dDashboardServer/templates/network.jinja2 index 04542a2c6..2051fce16 100644 --- a/dDashboardServer/templates/network.jinja2 +++ b/dDashboardServer/templates/network.jinja2 @@ -4,7 +4,7 @@ {% block css %}