Bläddra i källkod

refactor: merge flow into live overview layout

main
Jan 4 veckor sedan
förälder
incheckning
a9f735d984
1 ändrade filer med 49 tillägg och 63 borttagningar
  1. +49
    -63
      internal/control/ui.html

+ 49
- 63
internal/control/ui.html Visa fil

@@ -62,6 +62,11 @@ button,input,select{font:inherit}button{user-select:none}
.flow-master-state.idle{color:var(--text-dim)}
.flow-master-sub{margin-top:8px;font-size:12px;color:var(--text-dim)}
.flow-topbar{display:grid;grid-template-columns:repeat(4,minmax(0,1fr));gap:10px}
.flow-inline-top{display:flex;justify-content:space-between;gap:18px;align-items:flex-start;flex-wrap:wrap;padding:2px 2px 0}
.flow-inline-source,.flow-inline-alert{display:flex;gap:8px;align-items:baseline;min-width:0}
.flow-inline-label{font-size:10px;text-transform:uppercase;letter-spacing:.08em;color:var(--text-dim)}
.flow-inline-value{font-size:13px;font-weight:700;color:var(--text)}
.flow-inline-alert .flow-inline-value{color:var(--text-dim)}
.flow-summary{padding:12px;border:1px solid var(--border);border-radius:var(--radius);background:var(--surface2)}
.flow-summary-label{font-size:9px;text-transform:uppercase;letter-spacing:.08em;color:var(--text-dim);margin-bottom:6px}
.flow-summary-value{font-size:16px;font-weight:700}
@@ -122,6 +127,11 @@ button,input,select{font:inherit}button{user-select:none}
/* Cards */
.card{background:var(--surface);border:1px solid var(--border);border-radius:var(--radius);box-shadow:var(--shadow)}
.hero{padding:16px}
.tab-panel[data-tab-panel="live"] .tab-columns.two{grid-template-columns:1fr}
.tab-panel[data-tab-panel="live"] .stack{width:100%}
.tab-panel[data-tab-panel="live"] .stack>.card{width:100%}
.tab-panel[data-tab-panel="live"] .flow-board{margin-top:0}
.tab-panel[data-tab-panel="live"] .sidebar-card{height:fit-content}
/* TX Bar */
.tx-bar{position:relative;z-index:1;display:grid;grid-template-columns:minmax(180px,250px) 1fr auto;gap:14px;align-items:center}
.freq-display-label{font-size:10px;text-transform:uppercase;letter-spacing:.08em;color:var(--text-dim)}
@@ -327,8 +337,7 @@ input.input-error{border-color:var(--red);box-shadow:0 0 0 3px rgba(176,48,48,.1
<div class="flow-banner-text" id="flow-banner-text">No active issues.</div>
</div>
<div class="tab-bar">
<button class="tab-btn" data-tab="flow" type="button">Flow</button>
<button class="tab-btn active" data-tab="overview" type="button">Overview</button>
<button class="tab-btn active" data-tab="live" type="button">Live</button>
<button class="tab-btn" data-tab="tx" type="button">TX Control</button>
<button class="tab-btn" data-tab="rds" type="button">RDS</button>
<button class="tab-btn" data-tab="ingest" type="button">Ingest</button>
@@ -336,35 +345,8 @@ input.input-error{border-color:var(--red);box-shadow:0 0 0 3px rgba(176,48,48,.1
<button class="tab-btn" data-tab="activity" type="button">Activity</button>
</div>
<div class="tab-panels">
<!-- FLOW TAB -->
<section class="tab-panel" data-tab-panel="flow">
<div class="card flow-board">
<div class="flow-master">
<div class="flow-master-hero">
<div class="flow-master-label">On-Air Master Status</div>
<div class="flow-master-state idle" id="flow-master-state">--</div>
<div class="flow-master-sub" id="flow-master-sub">Waiting for runtime telemetry.</div>
</div>
<div class="flow-topbar">
<div class="flow-summary"><div class="flow-summary-label">Applied Frequency</div><div class="flow-summary-value" id="flow-top-applied">--</div></div>
<div class="flow-summary"><div class="flow-summary-label">Target Frequency</div><div class="flow-summary-value" id="flow-top-target">--</div></div>
<div class="flow-summary"><div class="flow-summary-label">Program Source</div><div class="flow-summary-value compact" id="flow-top-source">--</div></div>
<div class="flow-summary"><div class="flow-summary-label">Active Alert</div><div class="flow-summary-value compact" id="flow-top-alert">--</div></div>
</div>
</div>
<div class="flow-chain" id="flow-chain"></div>
<div class="flow-bottom">
<div class="flow-summary"><div class="flow-summary-label">Queue Health</div><div class="flow-summary-value" id="flow-bottom-queue">--</div></div>
<div class="flow-summary"><div class="flow-summary-label">Ingest Runtime</div><div class="flow-summary-value" id="flow-bottom-ingest">--</div></div>
<div class="flow-summary"><div class="flow-summary-label">Runtime Age</div><div class="flow-summary-value" id="flow-bottom-age">--</div></div>
<div class="flow-summary"><div class="flow-summary-label">Last Update</div><div class="flow-summary-value" id="flow-bottom-update">--</div></div>
</div>
</div>
</section>
<div class="flow-tooltip" id="flow-tooltip"></div>
<div class="flow-popover" id="flow-popover"></div>
<!-- OVERVIEW TAB -->
<section class="tab-panel active" data-tab-panel="overview">
<!-- LIVE TAB -->
<section class="tab-panel active" data-tab-panel="live">
<div class="tab-columns two">
<div class="stack">
<div class="card hero" id="hero-card">
@@ -384,39 +366,45 @@ input.input-error{border-color:var(--red);box-shadow:0 0 0 3px rgba(176,48,48,.1
</div>
<div class="tx-state-wrap">
<div class="tx-state idle" id="tx-state">IDLE</div>
<div class="status-hint" id="t-uptime">--</div>
<div class="status-hint" id="tx-hint">Waiting for runtime telemetry</div>
</div>
</div>
<div class="quick-grid">
<div class="quick-item"><div class="label">Chunks</div><div class="value" id="t-chunks">--</div></div>
<div class="quick-item"><div class="label">Samples</div><div class="value" id="t-samples">--</div></div>
<div class="quick-item"><div class="label">Underruns</div><div class="value" id="t-underruns">--</div></div>
<div class="quick-item"><div class="label">Uptime</div><div class="value" id="t-uptime">--</div></div>
<div class="quick-item"><div class="label">Rate</div><div class="value" id="t-rate">--</div></div>
</div>
<div class="signal-grid">
<div class="signal-card"><div class="signal-head"><div class="signal-title">Audio Buffer</div><div class="signal-value" id="meter-audio-text">--</div></div><div class="meter"><div class="meter-fill" id="meter-audio-fill"></div></div><svg class="spark good" id="spark-audio" viewBox="0 0 160 34" preserveAspectRatio="none"></svg></div>
<div class="signal-card"><div class="signal-head"><div class="signal-title">Stream Health</div><div class="signal-value" id="meter-stream-text">--</div></div><div class="meter"><div class="meter-fill" id="meter-stream-fill"></div></div><svg class="spark warn" id="spark-underruns" viewBox="0 0 160 34" preserveAspectRatio="none"></svg></div>
<div class="signal-card"><div class="signal-head"><div class="signal-title">TX Activity</div><div class="signal-value" id="meter-tx-text">--</div></div><div class="meter"><div class="meter-fill" id="meter-tx-fill"></div></div><svg class="spark good" id="spark-tx" viewBox="0 0 160 34" preserveAspectRatio="none"></svg></div>
<div class="signal-card"><div class="signal-head"><div class="signal-title">Audio Buffer</div><div class="signal-value" id="meter-audio-text">--</div></div><div class="meter"><div class="meter-fill" id="meter-audio-fill"></div></div></div>
<div class="signal-card"><div class="signal-head"><div class="signal-title">Stream Health</div><div class="signal-value" id="meter-stream-text">--</div></div><div class="meter"><div class="meter-fill" id="meter-stream-fill"></div></div></div>
<div class="signal-card"><div class="signal-head"><div class="signal-title">TX Activity</div><div class="signal-value" id="meter-tx-text">--</div></div><div class="meter"><div class="meter-fill" id="meter-tx-fill"></div></div></div>
</div>
<div class="hifi-grid">
<div class="hifi-card">
<div class="hifi-title">Audio RMS</div>
<div class="hifi-row"><div class="hifi-label">L</div><div class="hifi-meter"><div class="hifi-fill" id="audio-l-rms-fill"></div><div class="hifi-peak" id="audio-l-rms-marker"></div></div><div class="hifi-value" id="audio-l-rms-text">--</div></div>
<div class="hifi-row"><div class="hifi-label">R</div><div class="hifi-meter"><div class="hifi-fill" id="audio-r-rms-fill"></div><div class="hifi-peak" id="audio-r-rms-marker"></div></div><div class="hifi-value" id="audio-r-rms-text">--</div></div>
</div>
<div class="hifi-card">
<div class="hifi-title">Audio Peak</div>
<div class="hifi-row"><div class="hifi-label">L</div><div class="hifi-meter"><div class="hifi-fill" id="audio-l-peak-fill"></div><div class="hifi-peak" id="audio-l-peak-marker"></div></div><div class="hifi-value" id="audio-l-peak-text">--</div></div>
<div class="hifi-row"><div class="hifi-label">R</div><div class="hifi-meter"><div class="hifi-fill" id="audio-r-peak-fill"></div><div class="hifi-peak" id="audio-r-peak-marker"></div></div><div class="hifi-value" id="audio-r-peak-text">--</div></div>
</div>
<div class="hifi-card">
<div class="hifi-title">MPX Peak</div>
<div class="hifi-row"><div class="hifi-label">MPX</div><div class="hifi-meter"><div class="hifi-fill" id="mpx-peak-fill"></div><div class="hifi-peak" id="mpx-peak-marker"></div></div><div class="hifi-value" id="mpx-peak-text">--</div></div>
</div>
</div>
<div class="card flow-board">
<div class="flow-inline-top">
<div class="flow-inline-source"><span class="flow-inline-label">Program Source</span><span class="flow-inline-value" id="flow-top-source">--</span></div>
<div class="flow-inline-alert"><span class="flow-inline-label">Active Alert</span><span class="flow-inline-value" id="flow-top-alert">--</span></div>
</div>
<div class="flow-chain" id="flow-chain"></div>
<div class="flow-bottom">
<div class="flow-summary"><div class="flow-summary-label">Queue Health</div><div class="flow-summary-value" id="flow-bottom-queue">--</div></div>
<div class="flow-summary"><div class="flow-summary-label">Ingest Runtime</div><div class="flow-summary-value" id="flow-bottom-ingest">--</div></div>
<div class="flow-summary"><div class="flow-summary-label">Runtime Age</div><div class="flow-summary-value" id="flow-bottom-age">--</div></div>
<div class="flow-summary"><div class="flow-summary-label">Last Update</div><div class="flow-summary-value" id="flow-bottom-update">--</div></div>
</div>
</div>
<div class="hifi-grid">
<div class="hifi-card">
<div class="hifi-title">Audio RMS</div>
<div class="hifi-row"><div class="hifi-label">L</div><div class="hifi-meter"><div class="hifi-fill" id="audio-l-rms-fill"></div><div class="hifi-peak" id="audio-l-rms-marker"></div></div><div class="hifi-value" id="audio-l-rms-text">--</div></div>
<div class="hifi-row"><div class="hifi-label">R</div><div class="hifi-meter"><div class="hifi-fill" id="audio-r-rms-fill"></div><div class="hifi-peak" id="audio-r-rms-marker"></div></div><div class="hifi-value" id="audio-r-rms-text">--</div></div>
</div>
<div class="hifi-card">
<div class="hifi-title">Audio Peak</div>
<div class="hifi-row"><div class="hifi-label">L</div><div class="hifi-meter"><div class="hifi-fill" id="audio-l-peak-fill"></div><div class="hifi-peak" id="audio-l-peak-marker"></div></div><div class="hifi-value" id="audio-l-peak-text">--</div></div>
<div class="hifi-row"><div class="hifi-label">R</div><div class="hifi-meter"><div class="hifi-fill" id="audio-r-peak-fill"></div><div class="hifi-peak" id="audio-r-peak-marker"></div></div><div class="hifi-value" id="audio-r-peak-text">--</div></div>
</div>
<div class="hifi-card">
<div class="hifi-title">MPX Peak</div>
<div class="hifi-row"><div class="hifi-label">MPX</div><div class="hifi-meter"><div class="hifi-fill" id="mpx-peak-fill"></div><div class="hifi-peak" id="mpx-peak-marker"></div></div><div class="hifi-value" id="mpx-peak-text">--</div></div>
</div>
</div>
</div>
<div class="stack">
<div class="card sidebar-card">
<div class="sidebar-section">
@@ -447,6 +435,8 @@ input.input-error{border-color:var(--red);box-shadow:0 0 0 3px rgba(176,48,48,.1
</div>
</div>
</section>
<div class="flow-tooltip" id="flow-tooltip"></div>
<div class="flow-popover" id="flow-popover"></div>

<!-- TX CONTROL TAB -->
<section class="tab-panel" data-tab-panel="tx">
@@ -1219,17 +1209,13 @@ function _render(){
setText('freq-desired',isFinite(desired)?`Desired ${desired.toFixed(1)} MHz`:'Desired --');
$('freq-note')?.classList.toggle('mismatch',isFinite(applied)&&isFinite(desired)&&!nearEq(applied,desired,.001));

// ── Overview: quick stats
setText('t-chunks',fmt(eng.chunksProduced));setText('t-samples',fmt(eng.totalSamples));
setText('t-uptime',fmtTime(eng.uptimeSeconds));
setText('t-rate',drv.effectiveSampleRateHz?(drv.effectiveSampleRateHz/1000).toFixed(0)+'k':'--');
const ur=eng.underruns??drv.underruns;const urEl=$('t-underruns');if(urEl){urEl.textContent=ur==null?'--':String(ur);urEl.className='value'+(ur>0?' err':ur===0?' good':'');}
setText('t-uptime',eng.uptimeSeconds?`Uptime ${fmtTime(eng.uptimeSeconds)}`:'');

// ── Overview: TX state
const txSt=normState(eng.state);
$('tx-state').textContent=S.txBusy?'WORKING':txSt.toUpperCase();
$('tx-state').className='tx-state '+(S.txBusy?'working':txSt);
setText('tx-hint',eng.lastError?`Last error: ${eng.lastError}`:S.txBusy?'Command in progress':'Runtime polled every 1s');
setText('tx-hint',eng.lastError?`Last error: ${eng.lastError}`:S.txBusy?'Command in progress':'');
const canStopStates=['running','arming','prebuffering','degraded','muted','faulted','stopping'];
const startDis=S.txBusy||txSt==='running';
const stopDis=S.txBusy||!canStopStates.includes(txSt);


Laddar…
Avbryt
Spara