diff --git a/internal/control/ui.html b/internal/control/ui.html index 8445eca..ff13122 100644 --- a/internal/control/ui.html +++ b/internal/control/ui.html @@ -1077,6 +1077,8 @@ input.input-error {
HTTP
--
Runtime
--
+
Runtime Signal
--
+
Runtime Alert
--
Audio Buffer
--
Last Update
--
@@ -1714,7 +1716,7 @@ function render() { updateText('info-fmmod', fmtBool(cfg.fm?.fmModulationEnabled)); updateText('info-live', engine.state ? `${String(engine.state).toUpperCase()} / ${state.server.runtimeOk ? 'runtime ok' : 'runtime pending'}` : (state.server.configOk ? 'config only' : '--')); - updateHealth(audioStream); + updateHealth(engine, audioStream); updateMeters(engine, driver, audioStream); drawSparkline('spark-audio', state.charts.audio, 'good', 1); drawSparkline('spark-underruns', state.charts.underruns, underruns > 0 ? 'err' : 'warn'); @@ -1731,7 +1733,8 @@ function renderToggle(key, toggleId, labelId) { updateText(labelId, busy ? '...' : (on ? 'ON' : 'OFF')); } -function updateHealth(audioStream) { +function updateHealth(engine, audioStream) { + engine = engine || {}; updateText('health-http', state.server.configOk ? 'OK' : 'OFFLINE'); $('health-http').className = 'val ' + (state.server.configOk ? 'good' : 'err'); @@ -1739,6 +1742,31 @@ function updateHealth(audioStream) { updateText('health-runtime', runtimeState); $('health-runtime').className = 'val ' + (state.server.runtimeOk ? 'good' : 'warn'); + const runtimeIndicator = engine.runtimeIndicator; + const indicatorLabels = { + normal: 'Normal', + degraded: 'Degraded', + queueCritical: 'Queue critical', + }; + const indicatorText = indicatorLabels[runtimeIndicator] || (runtimeIndicator ? runtimeIndicator : '--'); + let indicatorSeverity = ''; + if (runtimeIndicator === 'queueCritical') indicatorSeverity = 'err'; + else if (runtimeIndicator === 'degraded') indicatorSeverity = 'warn'; + else if (runtimeIndicator === 'normal') indicatorSeverity = 'good'; + const indicatorEl = $('health-indicator'); + if (indicatorEl) { + indicatorEl.className = 'val' + (indicatorSeverity ? ' ' + indicatorSeverity : ''); + } + updateText('health-indicator', indicatorText); + + const runtimeAlertRaw = (engine.runtimeAlert || '').trim(); + const hasAlert = !!runtimeAlertRaw; + const alertEl = $('health-alert'); + if (alertEl) { + alertEl.className = 'val ' + (hasAlert ? 'warn' : 'good'); + } + updateText('health-alert', hasAlert ? runtimeAlertRaw : 'None'); + let audioLabel = 'N/A'; let audioClass = 'val'; if (audioStream) {