diff --git a/webui/static/api-monitor.js b/webui/static/api-monitor.js index 1b4215b1..fa653f5a 100644 --- a/webui/static/api-monitor.js +++ b/webui/static/api-monitor.js @@ -236,6 +236,7 @@ function _renderEqualizerBars(grid, data) {
+
0
@@ -275,6 +276,26 @@ function _renderEqualizerBars(grid, data) { const fill = bar.querySelector('.rate-eq-fill'); if (fill) fill.style.height = `${pct * 100}%`; + // Peak-hold tick — the VU-meter idiom: a thin marker sticks at the + // recent maximum, holds ~1.2s, then falls a few %/update until it + // rests on the live fill. A burst stays readable after it's over. + const nowTs = performance.now(); + let peak = prev.peak ?? pct; + let peakAt = prev.peakAt ?? nowTs; + if (pct >= peak) { + peak = pct; + peakAt = nowTs; + } else if (nowTs - peakAt > 1200) { + peak = Math.max(pct, peak - 0.045); + } + const peakEl = bar.querySelector('.rate-eq-peak'); + if (peakEl) { + peakEl.style.bottom = `${peak * 100}%`; + // Only show while meaningfully above the live fill — idle bars + // shouldn't carry a stray line. + peakEl.classList.toggle('visible', peak > pct + 0.015 && peak > 0.06); + } + // The reflection puddle (CSS ::after on the track) fades in // proportional to the real (unclamped) rate so idle services // don't pollute the row with a floor of glow. Bound to a @@ -329,7 +350,7 @@ function _renderEqualizerBars(grid, data) { bar.classList.toggle('active', value > 0 || wStatus === 'running'); bar.classList.toggle('rate-limited', isRateLimited); - _eqDisplay[svc] = { value, pct }; + _eqDisplay[svc] = { value, pct, peak, peakAt }; } } diff --git a/webui/static/style.css b/webui/static/style.css index 4c153f12..a67d3318 100644 --- a/webui/static/style.css +++ b/webui/static/style.css @@ -63242,6 +63242,29 @@ body.reduce-effects .dash-card::after { opacity: 0.85; } +/* Peak-hold tick — sticks at the recent maximum and slowly falls, like a + * hardware VU meter's peak LED. Position (bottom %) is driven from JS; + * only visible while it's meaningfully above the live fill so idle bars + * don't carry a stray line. */ +.rate-eq-peak { + position: absolute; + left: 10%; + right: 10%; + bottom: 4%; + height: 2px; + border-radius: 1px; + background: color-mix(in srgb, var(--eq-accent) 55%, white 45%); + box-shadow: 0 0 6px color-mix(in srgb, var(--eq-accent) 70%, transparent); + opacity: 0; + transition: bottom 0.9s linear, opacity 0.4s; + pointer-events: none; + z-index: 2; +} + +.rate-eq-peak.visible { + opacity: 0.75; +} + /* Vertical shimmer scan — runs continuously when active */ .rate-eq-shimmer { position: absolute;