diff --git a/services/web-ui/public/recorders.html b/services/web-ui/public/recorders.html index 9e65b45..9b23f5f 100644 --- a/services/web-ui/public/recorders.html +++ b/services/web-ui/public/recorders.html @@ -421,6 +421,15 @@ } 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 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 => { const isRecording = rec.status === 'recording'; const cfg = rec.source_config || {}; @@ -480,11 +489,11 @@ - ${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'; })()} - ${isRecording ? `00:00:00` : ''} + ${isRecording ? `${rec.started_at ? formatDur(Math.max(0, Math.floor((Date.now() - new Date(rec.started_at).getTime())/1000))) : "00:00:00"}` : ''} - ${isRecording ? `Connecting…` : ''} + ${isRecording ? `${(pState.signals[rec.id]||{}).signal === "lost" ? "No signal — stream dropped" : (pState.signals[rec.id]||{}).signal === "error" ? "Capture error" : "Receiving stream"}` : ''} ${isRecording && rec.live_asset_id ? `LIVE` : ''}