diff options
| author | tv <tv@krebsco.de> | 2026-11-03 01:16:41 +0100 |
|---|---|---|
| committer | tv <tv@krebsco.de> | 2026-11-03 01:16:41 +0100 |
| commit | 72d05e54de4a051148578d8445ad4527550052ff (patch) | |
| tree | 3e357016e3e81b50983c737cf0cef6e15033a360 | |
| parent | d2c8d34cb086fd49cfece13d60c22967064ea34a (diff) | |
dashboard: neater graph title & legend
| -rw-r--r-- | docs/dashboard.md | 17 | ||||
| -rw-r--r-- | src/api/dashboard/dashboard.html | 54 |
2 files changed, 46 insertions, 25 deletions
diff --git a/docs/dashboard.md b/docs/dashboard.md index 25bcfe8..a9e4567 100644 --- a/docs/dashboard.md +++ b/docs/dashboard.md @@ -51,11 +51,11 @@ the whole width; drag the space right of the map back to bring it again. A double-click there sets that space back to its default. The sizes are kept in the browser, for the next time the page is opened. -The `▾` before a box's title collapses it to its title (`▸` opens it again). A -collapsed box isn't drawn at all, so it costs the browser nothing while the node -updates; opened, it shows what's current. The open boxes take the room the collapsed -ones leave: in the right column they share it as before (the peers take all of it -when they're the only one open); the map collapsed, the right column takes the width. +The `▾` before a box's title collapses it to its title (`▸` opens it again); the map +always stays open. A collapsed box isn't drawn at all, so it costs the browser nothing +while the node updates; opened, it shows what's current. The open boxes take the room +the collapsed ones leave: in the right column they share it as before (the peers take +all of it when they're the only one open). The right column all collapsed keeps its width: its titles stay at the top, with a hint that dragging the space beside them to the right hides the column. Which boxes are collapsed is kept in the browser too. @@ -117,7 +117,8 @@ legend and the sliders greyed meanwhile); the choice is kept in the browser. A label on the map that changes, a round trip or a node's name, breathes once: it glows around its new text and fades (not where the system asks for reduced motion). -`links: live` beside the box's title says the members tell this node of their links' +The map's controls are at its top right, over the map as the legend is (below): +`links: live` says the members tell this node of their links' changes as they happen. Clicked, it's `links: every 30 s`: the members are only asked every 30 s, which costs the swarm fewer messages and leaves the map up to 30 s behind (this node's own links stay current). It's the node's choice, for every page open on @@ -125,8 +126,8 @@ it, until clicked again or the node restarts. The legend, over the top of the map, names the transports that appear; its contents are on an underlay of their own shape, as the map's labels, so they read over what is -under them, and the map is dragged through it as anywhere else. `hide legend` beside -the box's title hides it (`show legend` brings it back); the map stays as it is. +under them, and the map is dragged through it as anywhere else. `hide legend`, among the +map's controls, hides it (`show legend` brings it back); the map stays as it is. `layout: static`, beside it, is the default: nodes move only when the map is laid out (below). A click switches to `layout: dynamic`: each measured link is as diff --git a/src/api/dashboard/dashboard.html b/src/api/dashboard/dashboard.html index 25837c8..9132275 100644 --- a/src/api/dashboard/dashboard.html +++ b/src/api/dashboard/dashboard.html @@ -172,16 +172,24 @@ /* After the sliders, as tall; the kinds and the sliders greyed while the dots are off. */ .legend button.toggle { float: none; margin-left: 0; line-height: 1em; } .legend.off .message, .legend.off .slider { opacity: .45; } - /* The map and, over its top, the legend: shown or hidden, it takes no room from the - map, which stays as it is. What is under it shows through; its contents are on an - underlay of their own shape (as the map's labels), and only its controls take the - pointer: the map under the rest is dragged as anywhere else. */ + /* The map and, over its top, its controls (links, layout, legend) and the legend: + they take no room from the map, which stays as it is, the legend shown or hidden. + What is under them shows through; their contents are on an underlay of their own + shape (as the map's labels), and only buttons and sliders take the pointer: the map + under the rest is dragged as anywhere else. */ .map-area { position: relative; flex: 1; min-height: 0; display: flex; } - #legends { position: absolute; top: 0; left: 0; right: 0; pointer-events: none; } - #legends .legend > * { filter: drop-shadow(2px 0 0 var(--panel)) + .map-overlay { position: absolute; top: 0; left: 0; right: 0; pointer-events: none; } + /* The controls at the top right, on the legend's first row, which leaves them room + (`--controls-width`, as wide as they are); the rows below take the whole width. + As tall as a row of the legend. */ + .map-controls { position: absolute; top: 0; right: 0; display: flex; gap: 6px; } + #legends .legend:first-child { padding-right: calc(var(--controls-width, 0px) + 1em); } + .map-controls button.toggle { float: none; margin-left: 0; box-sizing: border-box; + height: 1.4em; line-height: calc(1.4em - 2px); padding-block: 0; } + .map-overlay .legend > *, .map-controls > * { filter: drop-shadow(2px 0 0 var(--panel)) drop-shadow(-2px 0 0 var(--panel)) drop-shadow(0 2px 0 var(--panel)) drop-shadow(0 -2px 0 var(--panel)); } - #legends button, #legends input, #legends label { pointer-events: auto; } + .map-overlay button, .map-overlay input, .map-overlay label { pointer-events: auto; } /* Hides the legend, and shows it again. */ button.toggle { font: inherit; font-size: 12px; text-transform: none; letter-spacing: 0; float: right; margin-left: 6px; padding: 0 6px; color: var(--muted); background: none; @@ -250,10 +258,13 @@ </header> <main> <section id="map"> - <h2>Swarm <button id="legend-toggle" class="toggle">hide legend</button> - <button id="rtt-toggle" class="toggle">layout: static</button> - <button id="links-toggle" class="toggle">links: live</button></h2> <div class="map-area"> + <div class="map-overlay"> + <div class="map-controls"> + <button id="links-toggle" class="toggle">links: live</button> + <button id="rtt-toggle" class="toggle">layout: static</button> + <button id="legend-toggle" class="toggle">hide legend</button> + </div> <div id="legends"> <div class="legend"> <span class="node self">this node</span> @@ -272,6 +283,7 @@ double-click it for its dashboard</span> </div> </div> + </div> <svg id="graph"><g id="view"><g id="flows" pointer-events="none"></g><g id="flashes" pointer-events="none"></g></g></svg> </div> </section> @@ -1501,6 +1513,15 @@ function release() { } document.addEventListener("selectionchange", release); +// The legend's first row leaves the controls room, however wide they are (their texts +// change). +{ + const controls = document.querySelector(".map-controls"); + const overlay = document.querySelector(".map-overlay"); + const room = () => overlay.style.setProperty("--controls-width", controls.offsetWidth + "px"); + room(); + if (window.ResizeObserver) new ResizeObserver(room).observe(controls); +} // The legend, hidden and shown again, over the map: the map stays as it is. const legendToggle = document.getElementById("legend-toggle"); function showLegend(shown) { @@ -2360,12 +2381,9 @@ function applySizes() { const wasHidden = main.classList.contains("right-hidden"); main.classList.toggle("right-hidden", sizes.columns === 1); main.classList.toggle("right-shut", sizes.columns !== 1 && right.every(shut)); - // What's collapsed gives its room to what isn't: the map collapsed, the right column - // takes the width but the map's title. The right column all collapsed keeps its - // width, resized as ever (dragged to the side, it goes). - const mapShut = shut("map"), rightShut = right.every(shut); + // The right column all collapsed keeps its width, resized as ever (dragged to the + // side, it goes); the map is never collapsed. if (sizes.columns === 1) style.setProperty("--columns", "minmax(0, 1fr) 0"); - else if (mapShut && !rightShut) style.setProperty("--columns", "max-content minmax(0, 1fr)"); else if (sizes.columns === null) style.removeProperty("--columns"); else style.setProperty("--columns", "minmax(0, " + sizes.columns + "fr) minmax(0, " + (1 - sizes.columns) + "fr)"); @@ -2479,7 +2497,10 @@ try { collapsed.add(id); } } catch (_) {} -for (const section of document.querySelectorAll("main > section")) { +// The map isn't collapsed: it is what the page is about (a choice kept from before isn't +// taken). +collapsed.delete("map"); +for (const section of document.querySelectorAll("main > section:not(#map)")) { const button = el("button", { class: "collapse" }); section.querySelector("h2").prepend(button); const show = () => { @@ -2497,7 +2518,6 @@ for (const section of document.querySelectorAll("main > section")) { applySizes(); if (collapsed.has(section.id)) return; if (section.id === "console-box") showConsole(); - if (section.id === "map") layoutAgain(); release(); render(); }); |
