aboutsummaryrefslogtreecommitdiffstats
path: root/src/api/dashboard/dashboard.html
diff options
context:
space:
mode:
Diffstat (limited to 'src/api/dashboard/dashboard.html')
-rw-r--r--src/api/dashboard/dashboard.html143
1 files changed, 31 insertions, 112 deletions
diff --git a/src/api/dashboard/dashboard.html b/src/api/dashboard/dashboard.html
index 5b776cd..57a3a0f 100644
--- a/src/api/dashboard/dashboard.html
+++ b/src/api/dashboard/dashboard.html
@@ -197,11 +197,6 @@
/* 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 LINKS kind, a toggle: shown (in the console, as dots, in the rates) or not. */
- .legend button.message::before { content: ""; display: inline-block; vertical-align: middle;
- margin-right: 4px; }
- .legend button.message.hidden-kind { text-decoration: line-through; }
- #console.no-links > div.links-kind { display: none; }
/* 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
@@ -316,7 +311,6 @@
<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="tree-toggle" class="toggle">tree: off</button>
<button id="legend-toggle" class="toggle">hide legend</button>
@@ -324,8 +318,8 @@
<div id="legends">
<div class="legend">
<span class="node self">this node</span>
- <span class="node">reports its links</span>
- <span class="node silent">reports none (no LINKS_REPLY)</span>
+ <span class="node">its links known</span>
+ <span class="node silent">its links unknown (not subscribed)</span>
<span class="node dormant-node">dormant (to this node)</span>
<span class="node clock-off">clock off the swarm's</span>
<span class="node focus">in focus (click a node)</span>
@@ -398,7 +392,7 @@
<div id="column-hint" class="note">Drag the space left of these boxes to the right to
hide the column.</div>
<section id="console-box">
- <h2>Console <label class="choice" title="Only the messages with the node or over the link in focus (clicked on the map or in the peers); all while nothing is"><input type="checkbox" id="console-filter">filter selected</label><span id="console-following" class="following" title="Scrolled to the bottom: new messages are scrolled to as they come; scroll up to stop">following…</span><button id="console-clear" class="toggle inline" title="Empty this page's console: what comes from now on fills it again">clear</button><span id="console-rates" class="rates" title="Messages and bytes per second sent and received, as load averages are: over the last 1, 5 and 15 minutes (exponentially weighted), since the page was opened; with the filter, those with the node or over the link in focus; without LINKS while their kind is off in the map's legend (they go only while a dashboard asks)"></span></h2>
+ <h2>Console <label class="choice" title="Only the messages with the node or over the link in focus (clicked on the map or in the peers); all while nothing is"><input type="checkbox" id="console-filter">filter selected</label><span id="console-following" class="following" title="Scrolled to the bottom: new messages are scrolled to as they come; scroll up to stop">following…</span><button id="console-clear" class="toggle inline" title="Empty this page's console: what comes from now on fills it again">clear</button><span id="console-rates" class="rates" title="Messages and bytes per second sent and received, as load averages are: over the last 1, 5 and 15 minutes (exponentially weighted), since the page was opened; with the filter, those with the node or over the link in focus"></span></h2>
<div class="scroll" id="console-scroll"><div id="console" class="console"></div>
<div id="console-none" class="note" hidden>No information available</div></div>
</section>
@@ -1953,7 +1947,6 @@ function render(ages = false) {
const skew = state.stats.clock;
document.getElementById("clock-warning").textContent = skew && skew.off
? "clock " + seconds(skew.offsetMs) + " off its peers'" : "";
- showLinksToggle();
held = false;
const boxes = [["peers-box", renderPeers], ["figures-box", renderStats]];
// Its ages are in tooltips only: drawn on events, not every second.
@@ -1994,21 +1987,6 @@ legendToggle.addEventListener("click", () => {
});
// Hidden as on the page it was opened from (`handOver`).
if (handOver && handOver.legend === false) showLegend(false);
-// The members' links subscribed to (`live`: each tells of a change as it happens), or
-// only asked for every 30 s; the node's choice, for every page (`POST
-// /admin/dashboard/links`), shown as the state has it.
-const linksToggle = document.getElementById("links-toggle");
-function showLinksToggle() {
- const live = !state || state.graph.live !== false;
- linksToggle.textContent = live ? "links: live" : "links: every 30 s";
- linksToggle.title = live
- ? "The members tell of their links' changes as they happen. Click to only ask them every 30 s (fewer messages; for every page on this node)"
- : "The members are asked for their links every 30 s. Click to have them tell of changes as they happen (for every page on this node)";
-}
-linksToggle.addEventListener("click", () => {
- const live = !state || state.graph.live !== false;
- fetch("dashboard/links?live=" + !live, { method: "POST" }).catch(() => {});
-});
// A writer's tree, shown and hidden (`treeShown`).
const treeToggle = document.getElementById("tree-toggle");
function showTreeToggle() {
@@ -2089,21 +2067,10 @@ function handOff(id) {
focused: focused === id ? null : focused,
settings,
legend: !document.getElementById("legends").hidden,
- links: state ? state.graph.live !== false : undefined,
figures: [...open],
document: [...documentOpen],
}));
}
-// Links live or every 30 s as on the page it was opened from (`handOver`), once its
-// first state says otherwise. The node's choice: for every page open on it.
-let linksHandedOver = false;
-function handOverLinks() {
- if (linksHandedOver || !handOver || typeof handOver.links !== "boolean") return;
- linksHandedOver = true;
- if ((state.graph.live !== false) !== handOver.links) {
- fetch("dashboard/links?live=" + handOver.links, { method: "POST" }).catch(() => {});
- }
-}
// `[x, y]` turned by `angle`.
function turned(x, y, angle) {
const c = Math.cos(angle), s = Math.sin(angle);
@@ -2744,8 +2711,7 @@ function messages(byKind) {
// The colour of a kind of message (`traffic`'s kinds), on the map and in the console;
// the replies as their requests.
const KIND_COLOURS = [["sync", "#ea580c"], ["blob", "#a16207"], ["hello", "#16a34a"],
- ["status", "#e11d48"], ["identity", "#0d9488"], ["paths", "var(--muted)"],
- ["links", "#db2777"]];
+ ["status", "#e11d48"], ["identity", "#0d9488"], ["paths", "var(--muted)"]];
const kindColour = kind => (KIND_COLOURS.find(([name]) => kind === name ||
kind === name + "Reply") || [null, "var(--fg)"])[1];
// How large the dots are drawn, times `dotRadius` (the slider after their legend, kept
@@ -2759,39 +2725,6 @@ let flowSpeed = .3;
try { flowSpeed = Number(localStorage.getItem("continuum-dot-speed")) || .3; } catch (_) {}
flowSpeed = Math.min(.9, Math.max(.1, flowSpeed));
let dotSlider = null, speedSlider = null;
-// LINKS and their replies (`traffic`'s `links`), and whether they are shown
-// (`linksToggleInLegend`).
-const isLinks = kind => kind === "links";
-let linksShown = true;
-try { linksShown = localStorage.getItem("continuum-links-shown") !== "off"; } catch (_) {}
-// The LINKS kind in the legend, a toggle: whether LINKS and their replies, which go only
-// while a dashboard asks for the members' links, are shown: in the console, as dots on
-// the map, and counted in the console's rates. Off, what is shown is the swarm's own
-// traffic (kept in this browser).
-function linksToggleInLegend(colour) {
- const button = el("button", { class: "toggle message", style: "--colour: " + colour }, "links");
- const show = () => {
- button.classList.toggle("hidden-kind", !linksShown);
- document.getElementById("console").classList.toggle("no-links", !linksShown);
- button.title = linksShown
- ? "LINKS go only while a dashboard asks for the members' links. Click to leave them out: of the console, the dots and the rates"
- : "Left out of the console, the dots and the rates. Click to show them again";
- };
- button.addEventListener("click", () => {
- linksShown = !linksShown;
- try { localStorage.setItem("continuum-links-shown", linksShown ? "on" : "off"); } catch (_) {}
- if (!linksShown) {
- for (let i = flows.length - 1; i >= 0; i--) if (isLinks(flows[i].kind)) flows.splice(i, 1);
- }
- show();
- showRates();
- filterConsole();
- const scroll = document.getElementById("console-scroll");
- scroll.scrollTop = scroll.scrollHeight;
- });
- show();
- return button;
-}
function showMessagesLegend() {
const legend = document.getElementById("messages-legend");
legend.classList.toggle("off", !flowsShown);
@@ -2811,9 +2744,8 @@ function showMessagesLegend() {
try { localStorage.setItem("continuum-dot-speed", String(flowSpeed)); } catch (_) {}
});
// Before the toggle, which ends the line.
- legend.prepend(...KIND_COLOURS.map(([kind, colour]) => kind === "links"
- ? linksToggleInLegend(colour)
- : el("span", { class: "message", style: "--colour: " + colour },
+ legend.prepend(...KIND_COLOURS.map(([kind, colour]) =>
+ el("span", { class: "message", style: "--colour: " + colour },
kind === "paths" ? "paths (pings)" : kind)),
el("label", { class: "slider" }, "size", slider),
el("label", { class: "slider" }, "speed", speed));
@@ -2876,7 +2808,7 @@ function addFlows(node, current, before, skip) {
for (const [kind, count] of Object.entries(counts[way])) {
const earlier = was[way][kind] || { messages: 0, bytes: 0 };
const grown = Math.max(0, count.messages - earlier.messages);
- if (grown > 0 && (linksShown || !isLinks(kind))) {
+ if (grown > 0) {
kinds[kind] = { messages: grown, bytes: Math.max(0, count.bytes - earlier.bytes) };
total += grown;
}
@@ -2923,7 +2855,6 @@ function remoteMessagesArrived(id, entry, list) {
const now = performance.now();
const links = new Map();
for (const message of kept) {
- if (!linksShown && isLinks(message.counted)) continue;
const key = message.from + " " + message.to;
const link = links.get(key) || { from: message.from, to: message.to, kinds: {}, total: 0 };
const kind = link.kinds[message.counted] || (link.kinds[message.counted] = { messages: 0, bytes: 0 });
@@ -2949,44 +2880,37 @@ function remoteMessagesArrived(id, entry, list) {
// weighs in by how long ago it was, `e^(-age/window)`), from the counts each update
// brings. The first sample is taken as it is, so they needn't climb from 0 first. ---
const RATE_WINDOWS_S = [60, 300, 900];
-// By peer, and in all (`null`), with LINKS (`all`) and without (`withoutLinks`): the
-// averages (`messages`, `bytes`: per window); and when the counts were last taken.
+// By peer, and in all (`null`): the averages (`messages`, `bytes`: per window); and when
+// the counts were last taken.
const rates = new Map();
let ratesAt = null;
function measureRates(current) {
const now = performance.now();
if (lastTraffic !== null && ratesAt !== null && now > ratesAt) {
const secs = (now - ratesAt) / 1000;
- // Of `what` (`messages`, `bytes`), sent and received, by how much each grew; LINKS
- // left out if `withoutLinks`.
- const grown = (counts, before, what, withoutLinks) => ["sent", "received"].reduce((sum, way) => {
- const total = of => Object.entries(of[way]).reduce((sum, [kind, count]) =>
- withoutLinks && isLinks(kind) ? sum : sum + count[what], 0);
+ // Of `what` (`messages`, `bytes`), sent and received, by how much each grew.
+ const grown = (counts, before, what) => ["sent", "received"].reduce((sum, way) => {
+ const total = of => Object.values(of[way]).reduce((sum, count) => sum + count[what], 0);
return sum + Math.max(0, total(counts) - total(before));
}, 0);
const average = (known, rate) => known ? known.map((average, i) =>
rate + (average - rate) * Math.exp(-secs / RATE_WINDOWS_S[i])) : RATE_WINDOWS_S.map(() => rate);
- const sample = (key, variant, messages, bytes) => {
- const known = rates.get(key) || {};
- const was = known[variant];
- known[variant] = { messages: average(was && was.messages, messages / secs),
- bytes: average(was && was.bytes, bytes / secs) };
- rates.set(key, known);
+ const sample = (key, messages, bytes) => {
+ const was = rates.get(key);
+ rates.set(key, { messages: average(was && was.messages, messages / secs),
+ bytes: average(was && was.bytes, bytes / secs) });
};
- for (const variant of ["all", "withoutLinks"]) {
- const withoutLinks = variant === "withoutLinks";
- let messagesInAll = 0, bytesInAll = 0;
- for (const [id, counts] of current) {
- const before = lastTraffic.get(id);
- if (!before) continue;
- const messages = grown(counts, before, "messages", withoutLinks);
- const bytes = grown(counts, before, "bytes", withoutLinks);
- sample(id, variant, messages, bytes);
- messagesInAll += messages;
- bytesInAll += bytes;
- }
- sample(null, variant, messagesInAll, bytesInAll);
+ let messagesInAll = 0, bytesInAll = 0;
+ for (const [id, counts] of current) {
+ const before = lastTraffic.get(id);
+ if (!before) continue;
+ const messages = grown(counts, before, "messages");
+ const bytes = grown(counts, before, "bytes");
+ sample(id, messages, bytes);
+ messagesInAll += messages;
+ bytesInAll += bytes;
}
+ sample(null, messagesInAll, bytesInAll);
// Peers gone from the list: their averages too.
for (const key of [...rates.keys()]) if (key !== null && !current.has(key)) rates.delete(key);
}
@@ -2994,13 +2918,11 @@ function measureRates(current) {
showRates();
}
// The averages shown: in all, or with the filter, of the peer in focus; none (–) for a
-// link between other nodes, a node that is no peer, or before two updates came. With
-// LINKS or without (`linksShown`).
+// link between other nodes, a node that is no peer, or before two updates came.
function showRates() {
const filtering = consoleFiltered && focused !== null;
const peer = filtering ? peerOf(focused) : null;
- const both = filtering && peer === null ? undefined : rates.get(peer);
- const shown = both && both[linksShown ? "all" : "withoutLinks"];
+ const shown = filtering && peer === null ? undefined : rates.get(peer);
const format = rate => rate < 100 ? rate.toFixed(2) : String(Math.round(rate));
// Bytes in the unit the largest of the three needs, all three alike.
const largest = shown ? Math.max(...shown.bytes) : 0;
@@ -3109,8 +3031,7 @@ function filterConsole() {
const filtering = consoleFiltered && focused !== null;
log.classList.toggle("filtering", filtering);
document.getElementById("console-none").hidden =
- !filtering || log.querySelector(":scope > div.peer-focused" +
- (linksShown ? "" : ":not(.links-kind)")) !== null;
+ !filtering || log.querySelector(":scope > div.peer-focused") !== null;
const view = filtering ? focused : null;
if (view !== consoleView) {
consoleView = view;
@@ -3128,7 +3049,6 @@ function consoleLine(message) {
"data-peer": message.node ? "" : message.peer,
"data-from": message.from || "", "data-to": message.to || "",
class: (shownMessage && shownMessage.seq === message.seq ? "focused" : "") +
- (isLinks(message.counted) ? " links-kind" : "") +
(relatedMarked !== null && message.related === relatedMarked ? " related" : "") +
peerClasses(message),
title: message.way === "refused" ? "refused: the transport wouldn't send it"
@@ -3273,7 +3193,6 @@ function connect() {
status("live", true); pulse(); render(); trafficArrived(); epochsArrived();
remakesArrived();
if (treeShown && writerTree === null) loadTree();
- handOverLinks();
if (token === null && tokenTimer === null) obtainTokens();
for (const id of subscriptions) if (!subscribed.has(id)) follow(id);
});
@@ -3451,8 +3370,8 @@ document.addEventListener("visibilitychange", () => {
}
}
});
-// A click on the status: live, everything now (a tick on the node, the members asked
-// again, what's open of the document read again); otherwise, reconnecting now.
+// A click on the status: live, everything now (a tick on the node, what's open of the
+// document read again); otherwise, reconnecting now.
connection.title = "Click to refresh now";
connection.style.cursor = "pointer";
connection.addEventListener("click", () => {