fix(recorders): kill the timer/status flap by computing live values inline + skipping unchanged DOM rebuilds

This commit is contained in:
Zac Gaetano 2026-05-18 09:47:03 -04:00
parent 57116dde42
commit 230944fc4b

View file

@ -421,6 +421,15 @@
} }
empty.style.display = 'none'; empty.style.display = 'none';
// Skip full DOM rebuild if structure is unchanged — the per-second timer
// and the signal poll will update the dynamic fields in place. Rebuilding
// every 5s was tearing down the live <video> element and the timer span.
const sig = pState.recorders.map(r => r.id + ':' + r.status + ':' + (r.live_asset_id || '') + ':' + (r.started_at || '')).join('|');
if (sig === pState._lastRenderSig && grid.children.length === pState.recorders.length) {
return;
}
pState._lastRenderSig = sig;
grid.innerHTML = pState.recorders.map(rec => { grid.innerHTML = pState.recorders.map(rec => {
const isRecording = rec.status === 'recording'; const isRecording = rec.status === 'recording';
const cfg = rec.source_config || {}; const cfg = rec.source_config || {};
@ -480,11 +489,11 @@
<div class="recorder-status-row"> <div class="recorder-status-row">
<span class="status-dot ${statusDotClass}" id="statusDot-${rec.id}"></span> <span class="status-dot ${statusDotClass}" id="statusDot-${rec.id}"></span>
<span class="text-sm" id="statusText-${rec.id}" style="color:${isRecording ? 'var(--accent)' : rec.status === 'error' ? 'var(--status-red)' : 'var(--text-secondary)'}"> <span class="text-sm" id="statusText-${rec.id}" style="color:${isRecording ? 'var(--accent)' : rec.status === 'error' ? 'var(--status-red)' : 'var(--text-secondary)'}">
${isRecording ? 'Connecting...' : rec.status === 'error' ? 'Error' : 'Idle'} ${(() => { if (rec.status === 'error') return 'Error'; if (!isRecording) return 'Idle'; const sg = (pState.signals[rec.id]||{}).signal; if (sg === 'lost') return 'Signal lost'; if (sg === 'error') return 'Connection error'; if (sg === 'connecting') return 'Connecting...'; return 'Recording'; })()}
</span> </span>
${isRecording ? `<span class="recorder-timer" id="timer-${rec.id}">00:00:00</span>` : ''} ${isRecording ? `<span class="recorder-timer" id="timer-${rec.id}">${rec.started_at ? formatDur(Math.max(0, Math.floor((Date.now() - new Date(rec.started_at).getTime())/1000))) : "00:00:00"}</span>` : ''}
</div> </div>
${isRecording ? `<div class="signal-strip" id="signalStrip-${rec.id}"><div class="signal-strip-fill"></div></div><div class="recorder-status-row" style="font-size:var(--text-xs);"><span id="signal-${rec.id}" style="color:var(--text-tertiary);font-family:var(--font-mono);letter-spacing:0.02em">Connecting…</span></div>` : ''} ${isRecording ? `<div class="signal-strip" id="signalStrip-${rec.id}"><div class="signal-strip-fill"></div></div><div class="recorder-status-row" style="font-size:var(--text-xs);"><span id="signal-${rec.id}" style="color:var(--text-tertiary);font-family:var(--font-mono);letter-spacing:0.02em">${(pState.signals[rec.id]||{}).signal === "lost" ? "No signal — stream dropped" : (pState.signals[rec.id]||{}).signal === "error" ? "Capture error" : "Receiving stream"}</span></div>` : ''}
${isRecording && rec.live_asset_id ? `<div class="recorder-preview"><video id="livevideo-${rec.id}" data-live-id="${rec.live_asset_id}" muted playsinline autoplay></video><div class="recorder-preview-stamp"><span class="recorder-preview-dot"></span>LIVE</div></div>` : ''} ${isRecording && rec.live_asset_id ? `<div class="recorder-preview"><video id="livevideo-${rec.id}" data-live-id="${rec.live_asset_id}" muted playsinline autoplay></video><div class="recorder-preview-stamp"><span class="recorder-preview-dot"></span>LIVE</div></div>` : ''}
<div class="recorder-source"> <div class="recorder-source">