From 8d97f6c61ee30f4a0140f18e02c91e0ec30fd1c3 Mon Sep 17 00:00:00 2001 From: Daniel Redetzke Date: Mon, 5 Oct 2026 14:44:18 +0300 Subject: [PATCH] Live page: 10-second averages for the figures and the table The big figures, the per-peer speeds and the busiest-first order average the last 5 steps instead of swinging with every burst, and the figures sit in fixed-width columns so they no longer push each other around. The charts still show every step. --- app.css | 2 +- app.js | 16 +++++++++++----- 2 files changed, 12 insertions(+), 6 deletions(-) diff --git a/app.css b/app.css index 4b5f7fa..0cb1941 100644 --- a/app.css +++ b/app.css @@ -383,7 +383,7 @@ dialog::backdrop { background: rgba(22, 23, 26, .55); } pre.log.tall { max-height: calc(100vh - 260px); min-height: 420px; } /* live */ -.livenow { display: flex; flex-wrap: wrap; gap: 12px 40px; margin-top: 14px; } +.livenow { display: grid; grid-template-columns: repeat(auto-fill, minmax(190px, 1fr)); gap: 12px 24px; margin-top: 14px; } .livenow .k { font-size: 13px; color: var(--ink-2); } .livenow .v { font-size: 30px; font-weight: 600; letter-spacing: -0.01em; margin-top: 4px; font-variant-numeric: tabular-nums; } .livenow .v small { font-size: 16px; color: var(--ink-3); font-weight: 500; margin-left: 6px; } diff --git a/app.js b/app.js index b563000..28d2717 100644 --- a/app.js +++ b/app.js @@ -1119,6 +1119,10 @@ // Below this a peer counts as idle: keepalives and background chatter. const IDLE_BPS = 2000; + // The figures and the table average the last few steps so they do not + // swing with every burst; the charts show each step. + const AVG_STEPS = 5; + const calm = () => window.matchMedia('(prefers-reduced-motion: reduce)').matches; // countTo moves the number in el from its last value to v over a short @@ -1251,17 +1255,19 @@ } }, 'Pause'); const subText = () => paused ? ['Paused · the chart keeps the moment you paused'] - : [h('span', { class: 'dot ok pulse' }), ' Updated every ' + live.step + ' s · speeds are averages over the step']; + : [h('span', { class: 'dot ok pulse' }), ' Updated every ' + live.step + ' s · figures are ' + AVG_STEPS * live.step + '-second averages']; const sub = h('p', { class: 'sub livesub' }, subText()); const draw = (slide) => { const pts = live.points; const sumAt = (p) => Object.values(p.peers).reduce((a, [d, u]) => ({ down: a.down + d, up: a.up + u }), { down: 0, up: 0 }); const series = pts.map((p) => ({ t: p.t, ...sumAt(p) })); - const now = series[series.length - 1] || { down: 0, up: 0 }; - const last = pts.length ? pts[pts.length - 1].peers : {}; - countTo(down, now.down); - countTo(up, now.up); + const recent = pts.slice(-AVG_STEPS); + const avg = (f) => recent.length ? recent.reduce((a, p) => a + f(p), 0) / recent.length : 0; + const last = {}; + for (const p of peers) last[p.id] = [avg((x) => (x.peers[p.id] || [0, 0])[0]), avg((x) => (x.peers[p.id] || [0, 0])[1])]; + countTo(down, avg((p) => sumAt(p).down)); + countTo(up, avg((p) => sumAt(p).up)); active.replaceChildren(String(peers.filter((p) => { const r = last[p.id]; return r && r[0] + r[1] >= IDLE_BPS; }).length), h('small', null, '/ ' + peers.filter((p) => p.stats.online).length + ' online')); lc.update(series, live.size, live.step, slide);