feat: sleep duration timer in stats panel
Track sleeping_since (ISO timestamp) in ServerState; auto-cleared by _set() whenever state leaves sleeping. Frontend runs a 1s interval showing elapsed time as "Xs", "Xm Ys", or "Xh Ym" in a new ASLEEP stat row (visible only during state-sleeping). Timer survives page reload via server-side timestamp. Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
This commit is contained in:
@@ -14,8 +14,9 @@
|
||||
const svPrompt = document.getElementById('sv-prompt');
|
||||
const svSpeed = document.getElementById('sv-speed');
|
||||
const svTokens = document.getElementById('sv-tokens');
|
||||
const svLoadStage = document.getElementById('sv-load-stage');
|
||||
const svReqs = document.getElementById('sv-reqs');
|
||||
const svLoadStage = document.getElementById('sv-load-stage');
|
||||
const svSleepTimer = document.getElementById('sv-sleep-timer');
|
||||
const svReqs = document.getElementById('sv-reqs');
|
||||
|
||||
// ── State class management ────────────────────────────────────────────────
|
||||
const ALL_STATES = [
|
||||
@@ -47,7 +48,7 @@
|
||||
}
|
||||
|
||||
function update(msg) {
|
||||
const { state, model, loading, metrics, request_count } = msg;
|
||||
const { state, model, loading, metrics, request_count, sleeping_since } = msg;
|
||||
|
||||
applyState(state);
|
||||
|
||||
@@ -92,6 +93,13 @@
|
||||
svPrompt.textContent = svSpeed.textContent = svTokens.textContent = '—';
|
||||
}
|
||||
|
||||
// Sleep timer
|
||||
if (state === 'sleeping' && sleeping_since) {
|
||||
startSleepTimer(sleeping_since);
|
||||
} else {
|
||||
stopSleepTimer();
|
||||
}
|
||||
|
||||
// Pulse dot on message
|
||||
wsDot.classList.remove('pulse');
|
||||
void wsDot.offsetWidth; // force reflow to restart animation
|
||||
@@ -130,6 +138,40 @@
|
||||
document.addEventListener('touchmove', e => { if (dragging) { e.preventDefault(); onDrag(e.touches[0].clientX); } }, { passive: false });
|
||||
document.addEventListener('touchend', endDrag);
|
||||
|
||||
// ── Sleep timer ───────────────────────────────────────────────────────────
|
||||
let sleepingSince = null;
|
||||
let sleepInterval = null;
|
||||
|
||||
function formatDuration(sec) {
|
||||
const h = Math.floor(sec / 3600);
|
||||
const m = Math.floor((sec % 3600) / 60);
|
||||
const s = sec % 60;
|
||||
if (h > 0) return h + 'h ' + m + 'm';
|
||||
if (m > 0) return m + 'm ' + s + 's';
|
||||
return s + 's';
|
||||
}
|
||||
|
||||
function tickSleepTimer() {
|
||||
if (!sleepingSince) return;
|
||||
const elapsed = Math.floor((Date.now() - new Date(sleepingSince).getTime()) / 1000);
|
||||
svSleepTimer.textContent = formatDuration(elapsed);
|
||||
}
|
||||
|
||||
function startSleepTimer(since) {
|
||||
if (sleepingSince === since) return;
|
||||
sleepingSince = since;
|
||||
clearInterval(sleepInterval);
|
||||
tickSleepTimer();
|
||||
sleepInterval = setInterval(tickSleepTimer, 1000);
|
||||
}
|
||||
|
||||
function stopSleepTimer() {
|
||||
sleepingSince = null;
|
||||
clearInterval(sleepInterval);
|
||||
sleepInterval = null;
|
||||
svSleepTimer.textContent = '—';
|
||||
}
|
||||
|
||||
// ── HTTP polling fallback ─────────────────────────────────────────────────
|
||||
let pollTimer = null;
|
||||
const POLL_MS = 2000;
|
||||
|
||||
@@ -147,6 +147,10 @@
|
||||
<span class="sk">STAGE</span>
|
||||
<span class="sv" id="sv-load-stage">—</span>
|
||||
</div>
|
||||
<div class="stat-row" id="row-sleep-timer">
|
||||
<span class="sk">ASLEEP</span>
|
||||
<span class="sv" id="sv-sleep-timer">—</span>
|
||||
</div>
|
||||
<div class="stat-row" id="row-reqs">
|
||||
<span class="sk">REQS</span>
|
||||
<span class="sv" id="sv-reqs">0</span>
|
||||
|
||||
@@ -340,6 +340,8 @@ body.state-sleeping #row-speed,
|
||||
body.state-sleeping #row-tokens,
|
||||
body.state-sleeping #row-load-stage { display: none; }
|
||||
|
||||
body:not(.state-sleeping) #row-sleep-timer { display: none; }
|
||||
|
||||
body.state-waiting #row-prompt,
|
||||
body.state-waiting #row-speed,
|
||||
body.state-waiting #row-tokens,
|
||||
|
||||
Reference in New Issue
Block a user