Rate monitor: rate-limit bans drain down as a visible countdown
A banned service used to just tint red. The payload carries the seconds
remaining (rl_remaining), so the bar now locks into a cooldown state: the live
VU dims, a red column drains away as the ban ticks down (largest remaining
seen is latched as the denominator — only remaining is sent), and an m:ss
timer counts to recovery. The moment the ban expires the track flashes green
('recovered') and the VU takes the stage back. 'Back in 4:00', not
'something's red'.
This commit is contained in:
parent
12ad373a83
commit
09af31154e
2 changed files with 105 additions and 1 deletions
|
|
@ -237,6 +237,10 @@ function _renderEqualizerBars(grid, data) {
|
|||
<div class="rate-eq-tip"></div>
|
||||
</div>
|
||||
<div class="rate-eq-peak"></div>
|
||||
<div class="rate-eq-cooldown">
|
||||
<div class="rate-eq-cooldown-fill"></div>
|
||||
<div class="rate-eq-cooldown-time"></div>
|
||||
</div>
|
||||
<div class="rate-eq-value">0</div>
|
||||
</div>
|
||||
<div class="rate-eq-meta">
|
||||
|
|
@ -350,7 +354,36 @@ function _renderEqualizerBars(grid, data) {
|
|||
bar.classList.toggle('active', value > 0 || wStatus === 'running');
|
||||
bar.classList.toggle('rate-limited', isRateLimited);
|
||||
|
||||
_eqDisplay[svc] = { value, pct, peak, peakAt };
|
||||
// Cooldown drain — a ban isn't just "red", it's a COUNTDOWN. The
|
||||
// payload only carries seconds remaining, so latch the largest value
|
||||
// seen this ban as the denominator; the red column then drains away
|
||||
// as the ban ticks down and the timer shows m:ss until recovery.
|
||||
const rlRemaining = isRateLimited ? Math.max(0, Math.round(d.rl_remaining || 0)) : 0;
|
||||
let rlTotal = prev.rlTotal || 0;
|
||||
const cooling = rlRemaining > 0;
|
||||
if (cooling) {
|
||||
rlTotal = Math.max(rlTotal, rlRemaining);
|
||||
const cdFill = bar.querySelector('.rate-eq-cooldown-fill');
|
||||
if (cdFill) cdFill.style.height = `${(rlRemaining / rlTotal) * 100}%`;
|
||||
const cdTime = bar.querySelector('.rate-eq-cooldown-time');
|
||||
if (cdTime) {
|
||||
const m = Math.floor(rlRemaining / 60);
|
||||
const s = String(rlRemaining % 60).padStart(2, '0');
|
||||
cdTime.textContent = `${m}:${s}`;
|
||||
}
|
||||
} else {
|
||||
rlTotal = 0;
|
||||
// Recovery moment: the ban just ended — flash the bar back alive.
|
||||
if (prev.cooling) {
|
||||
bar.classList.remove('recovered');
|
||||
void bar.offsetWidth; // restart the animation
|
||||
bar.classList.add('recovered');
|
||||
window.setTimeout(() => bar.classList.remove('recovered'), 1300);
|
||||
}
|
||||
}
|
||||
bar.classList.toggle('cooldown', cooling);
|
||||
|
||||
_eqDisplay[svc] = { value, pct, peak, peakAt, rlTotal, cooling };
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -63265,6 +63265,77 @@ body.reduce-effects .dash-card::after {
|
|||
opacity: 0.75;
|
||||
}
|
||||
|
||||
/* Cooldown drain — while a service is rate-limit banned, the track shows a
|
||||
* red column that DRAINS as the ban ticks down plus an m:ss countdown.
|
||||
* "Back in 4:00", not just "something's red". JS drives the fill height
|
||||
* (remaining/total) and timer text; .cooldown gates visibility. */
|
||||
.rate-eq-cooldown {
|
||||
position: absolute;
|
||||
inset: 0;
|
||||
opacity: 0;
|
||||
pointer-events: none;
|
||||
transition: opacity 0.4s;
|
||||
z-index: 3;
|
||||
}
|
||||
|
||||
.rate-eq.cooldown .rate-eq-cooldown {
|
||||
opacity: 1;
|
||||
}
|
||||
|
||||
.rate-eq.cooldown .rate-eq-fill,
|
||||
.rate-eq.cooldown .rate-eq-peak {
|
||||
opacity: 0.18; /* the live VU yields the stage to the countdown */
|
||||
}
|
||||
|
||||
.rate-eq-cooldown-fill {
|
||||
position: absolute;
|
||||
left: 0;
|
||||
right: 0;
|
||||
bottom: 0;
|
||||
height: 100%;
|
||||
background: linear-gradient(180deg,
|
||||
rgba(239, 68, 68, 0.55) 0%,
|
||||
rgba(239, 68, 68, 0.28) 60%,
|
||||
rgba(239, 68, 68, 0.12) 100%);
|
||||
border-radius: 0 0 11px 11px;
|
||||
box-shadow: inset 0 1px 0 rgba(255, 120, 120, 0.5);
|
||||
transition: height 0.9s linear;
|
||||
}
|
||||
|
||||
.rate-eq-cooldown-time {
|
||||
position: absolute;
|
||||
left: 0;
|
||||
right: 0;
|
||||
top: 50%;
|
||||
transform: translateY(-50%);
|
||||
text-align: center;
|
||||
font-size: 11px;
|
||||
font-weight: 700;
|
||||
font-variant-numeric: tabular-nums;
|
||||
color: rgba(255, 200, 200, 0.95);
|
||||
text-shadow: 0 0 8px rgba(239, 68, 68, 0.8);
|
||||
}
|
||||
|
||||
/* Recovery flash — the moment a ban expires the track blinks back alive */
|
||||
.rate-eq.recovered .rate-eq-track {
|
||||
animation: rate-eq-recovered 1.2s ease-out;
|
||||
}
|
||||
|
||||
@keyframes rate-eq-recovered {
|
||||
0% {
|
||||
border-color: rgba(74, 222, 128, 0.9);
|
||||
box-shadow:
|
||||
inset 0 1px 0 rgba(255, 255, 255, 0.04),
|
||||
0 0 26px rgba(74, 222, 128, 0.55);
|
||||
}
|
||||
100% {
|
||||
border-color: rgba(255, 255, 255, 0.06);
|
||||
box-shadow:
|
||||
inset 0 1px 0 rgba(255, 255, 255, 0.04),
|
||||
inset 0 -10px 24px rgba(0, 0, 0, 0.35);
|
||||
}
|
||||
}
|
||||
|
||||
/* Vertical shimmer scan — runs continuously when active */
|
||||
.rate-eq-shimmer {
|
||||
position: absolute;
|
||||
|
|
|
|||
Loading…
Reference in a new issue