From 7c36223de0ec4a89519d4babbda4092268911115 Mon Sep 17 00:00:00 2001 From: Daniel Redetzke Date: Mon, 5 Oct 2026 20:35:23 +0300 Subject: [PATCH] Live page: upload gets a light area too Download and upload are both lines over light, see-through areas, so upload no longer looks less important when it is the larger one. --- app.css | 4 +++- app.js | 16 ++++++++++------ 2 files changed, 13 insertions(+), 7 deletions(-) diff --git a/app.css b/app.css index 0cb1941..d16f3f0 100644 --- a/app.css +++ b/app.css @@ -387,7 +387,9 @@ pre.log.tall { max-height: calc(100vh - 260px); min-height: 420px; } .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; } -.chart .larea { fill: var(--down); opacity: .15; } +.chart .larea { opacity: .15; } +.chart .larea.down { fill: var(--down); } +.chart .larea.up { fill: var(--up); } .chart .ldown, .chart .lup { fill: none; stroke-width: 1.75; stroke-linejoin: round; stroke-linecap: round; vector-effect: non-scaling-stroke; } .chart .ldown { stroke: var(--down); } .chart .lup { stroke: var(--up); } diff --git a/app.js b/app.js index c02fed1..df7dcd6 100644 --- a/app.js +++ b/app.js @@ -1148,7 +1148,8 @@ const calm = () => window.matchMedia('(prefers-reduced-motion: reduce)').matches; - // liveChart draws download as a filled area and upload as a line. It is + // liveChart draws download and upload as lines over light, see-through + // areas, so neither looks less important where they overlap. It is // built once and updated in place: each new step enters just beyond the // right edge and the chart slides left by one step over the step's length, // so it moves steadily instead of jumping. Hover shows the values at a @@ -1156,11 +1157,12 @@ function liveChart() { const W = 1000, H = 100; let pts = [], size = 60, step = 2, off = 0, dx = W / 59, t0 = 0, moving = false; - const area = svg('path', { class: 'larea' }); + const downArea = svg('path', { class: 'larea down' }); + const upArea = svg('path', { class: 'larea up' }); const down = svg('path', { class: 'ldown' }); const up = svg('path', { class: 'lup' }); const cursor = svg('line', { class: 'cursor', x1: 0, x2: 0, y1: 0, y2: H, visibility: 'hidden' }); - const g = svg('g', null, area, down, up, cursor); + const g = svg('g', null, downArea, upArea, down, up, cursor); const plot = svg('svg', { viewBox: '0 0 ' + W + ' ' + H, preserveAspectRatio: 'none', 'aria-hidden': 'true' }, g); const ylTop = h('div', { class: 'yl top' }), ylMid = h('div', { class: 'yl mid' }); const at = (p) => new Date(p.t * 1000).toLocaleTimeString(undefined, { hour: '2-digit', minute: '2-digit', second: '2-digit' }); @@ -1206,10 +1208,12 @@ const top = niceTop(Math.max(0, ...pts.map((p) => Math.max(p.down, p.up))) || 1e6); const line = (k) => curvePath(pts.map((p, i) => [x(i), H - p[k] / top * H])); if (n > 1) { - const dl = line('down'); - area.setAttribute('d', dl + 'L' + x(n - 1).toFixed(1) + ',' + H + 'L' + x(0).toFixed(1) + ',' + H + 'Z'); + const dl = line('down'), ul = line('up'); + const base = 'L' + x(n - 1).toFixed(1) + ',' + H + 'L' + x(0).toFixed(1) + ',' + H + 'Z'; + downArea.setAttribute('d', dl + base); + upArea.setAttribute('d', ul + base); down.setAttribute('d', dl); - up.setAttribute('d', line('up')); + up.setAttribute('d', ul); } // Axis values are round: no ".0". ylTop.textContent = fmtRate(top).replace('.0 ', ' ');