Преглед изворни кода

Refine ferrite.fm control UI structure

main
Jan пре 1 месец
родитељ
комит
2bf9b96797
1 измењених фајлова са 10 додато и 27 уклоњено
  1. +10
    -27
      internal/control/ui.html

+ 10
- 27
internal/control/ui.html Прегледај датотеку

@@ -3,7 +3,7 @@
<head>
<meta charset="utf-8">
<meta name="viewport" content="width=device-width, initial-scale=1">
<title>fm-rds-tx</title>
<title>ferrite.fm</title>
<style>
@import url('https://fonts.googleapis.com/css2?family=Inter:wght@400;500;600;700;800&family=JetBrains+Mono:wght@400;600&display=swap');
:root {
@@ -228,7 +228,7 @@ input.input-error{border-color:var(--red);box-shadow:0 0 0 3px rgba(176,48,48,.1
<div class="app">
<div class="header">
<div class="header-main">
<h1>FM-RDS-TX Control Plane</h1>
<h1>ferrite.fm Control Plane</h1>
<div class="header-note">Operate confidently: tune fast, inspect state instantly, diagnose only when needed.</div>
<div class="header-sub">
<div class="badge"><span>Backend</span><strong id="badge-backend">--</strong></div>
@@ -237,6 +237,8 @@ input.input-error{border-color:var(--red);box-shadow:0 0 0 3px rgba(176,48,48,.1
</div>
</div>
<div class="header-status">
<button class="danger-btn" id="header-danger-stop" type="button">Emergency Stop TX</button>
<button class="danger-btn" id="header-danger-reset-fault" type="button">Reset Fault</button>
<div class="led" id="led-conn"></div>
<div class="status-text" id="conn-label">connecting</div>
</div>
@@ -454,24 +456,6 @@ input.input-error{border-color:var(--red);box-shadow:0 0 0 3px rgba(176,48,48,.1
<div class="section-note reset-hint">Structural clipper changes (iterations, knee, look-ahead) are persisted to config and require TX restart.</div>
</div>
</div>
<!-- Danger -->
<div class="card panel" data-panel-key="danger">
<div class="panel-head" data-panel><h2>Danger Zone</h2><div class="meta">emergency</div><span class="chevron">▼</span></div>
<div class="panel-body">
<div class="actions-row" style="margin-top:0"><button class="danger-btn" id="danger-stop" type="button">Emergency Stop TX</button><button class="danger-btn" id="danger-reset-fault" type="button">Reset Fault</button></div>
<div class="section-note reset-hint" id="reset-hint">Reset Fault moves the runtime back to DEGRADED while the queue settles.</div>
</div>
</div>
<!-- Shortcuts -->
<div class="card panel" data-panel-key="shortcuts">
<div class="panel-head" data-panel><h2>Shortcuts</h2><div class="meta">keyboard</div><span class="chevron">▼</span></div>
<div class="panel-body">
<div class="shortcuts-grid">
<div><div class="shortcut-line"><span class="name">Start TX</span><span class="keys"><span class="kbd">t</span></span></div><div class="shortcut-line"><span class="name">Stop TX</span><span class="keys"><span class="kbd">T</span></span></div><div class="shortcut-line"><span class="name">Refresh</span><span class="keys"><span class="kbd">r</span></span></div></div>
<div><div class="shortcut-line"><span class="name">Next Preset</span><span class="keys"><span class="kbd">]</span></span></div><div class="shortcut-line"><span class="name">Prev Preset</span><span class="keys"><span class="kbd">[</span></span></div><div class="shortcut-line"><span class="name">Apply Draft</span><span class="keys"><span class="kbd">Enter</span></span></div></div>
</div>
</div>
</div>
</div>
</div>
</section>
@@ -1076,8 +1060,7 @@ function _render(){
setText('compclip-meta',S.cfgDirty['compclip']?'Immediate + restart-only · draft pending':'Immediate + restart-only');
$('compclip-apply').disabled=!S.cfgDirty['compclip'];$('compclip-reset').disabled=!S.cfgDirty['compclip'];
// Danger
$('danger-stop').disabled=S.txBusy;const rfl=$('danger-reset-fault');if(rfl){rfl.disabled=S.faultBusy||!S.server.runtimeOk;rfl.textContent=S.faultBusy?'Resetting...':'Reset Fault';}
const rh=$('reset-hint');if(rh){const sn=normState(eng.state);rh.textContent=sn==='faulted'?'Faulted: reset moves runtime back to DEGRADED.':sn==='muted'||sn==='degraded'?'Reset Fault holds at DEGRADED until queue recovers.':'Manual fault reset drops to DEGRADED while queue recovers.';}
const stopButtons=[$('header-danger-stop')].filter(Boolean);stopButtons.forEach(btn=>{btn.disabled=S.txBusy;btn.textContent='Emergency Stop TX';});const resetButtons=[$('header-danger-reset-fault')].filter(Boolean);resetButtons.forEach(btn=>{btn.disabled=S.faultBusy||!S.server.runtimeOk;btn.textContent=S.faultBusy?'Resetting...':'Reset Fault';});

// ── RDS tab
syncToggle('tog-rds','rds-label','rdsEnabled');
@@ -1217,8 +1200,8 @@ function bindAll(){
$('compclip-apply').addEventListener('click',()=>applyCfgSection('compclip'));$('compclip-reset').addEventListener('click',()=>{cfgClear('compclip');toast('Draft reset','info');});
// TX
$('btn-start').addEventListener('click',()=>txAction('start'));$('btn-stop').addEventListener('click',()=>txAction('stop'));
$('danger-stop').addEventListener('click',()=>txAction('stop'));$('btn-refresh').addEventListener('click',manualRefresh);
$('danger-reset-fault').addEventListener('click',()=>resetFault());
$('header-danger-stop').addEventListener('click',()=>txAction('stop'));$('btn-refresh').addEventListener('click',manualRefresh);
$('header-danger-reset-fault').addEventListener('click',()=>resetFault());
// RDS
$('rds-pi').addEventListener('input',e=>{const v=e.target.value.toUpperCase().replace(/[^0-9A-F]/g,'').slice(0,4);e.target.value=v;S.cfgErrors=S.cfgErrors||{};S.cfgErrors.pi=validate('pi',v);cfgSetDirty('pi',v);});
$('rds-pty').addEventListener('change',e=>cfgSetDirty('pty',Number(e.target.value)));
@@ -1255,7 +1238,7 @@ function bindAll(){
// Log
$('btn-clear-log').addEventListener('click',()=>{$('log').innerHTML='<div class="empty-log">No activity recorded yet.</div>';toast('Activity log cleared','info');});
// Keyboard
window.addEventListener('keydown',e=>{const typing=(()=>{const t=e.target;if(!t)return false;const tag=(t.tagName||'').toLowerCase();return tag==='input'||tag==='textarea'||t.isContentEditable;})();if(typing){if(e.key==='Enter'){e.preventDefault();if(S.dirty.has('frequencyMHz'))applySection('freq');else if(secDirty('rds'))applySection('rds');}return;}if(e.key==='t'&&!e.shiftKey){e.preventDefault();txAction('start');}else if(e.key==='T'||(e.key==='t'&&e.shiftKey)){e.preventDefault();txAction('stop');}else if(e.key.toLowerCase()==='r'){e.preventDefault();manualRefresh();}else if(e.key==='['){e.preventDefault();cyclePreset(-1);}else if(e.key===']'){e.preventDefault();cyclePreset(1);}else if(e.key==='Enter'){e.preventDefault();if(S.dirty.has('frequencyMHz'))applySection('freq');else if(secDirty('rds'))applySection('rds');}});
window.addEventListener('keydown',e=>{const typing=(()=>{const t=e.target;if(!t)return false;const tag=(t.tagName||'').toLowerCase();return tag==='input'||tag==='textarea'||t.isContentEditable;})();if(typing&&e.key==='Enter'){e.preventDefault();if(S.dirty.has('frequencyMHz'))applySection('freq');else if(secDirty('rds'))applySection('rds');}});
// Responsive
const respHandler=()=>{if(!mobileMq.matches)S.mobilePanelsApplied=false;else applyMobilePanels();};if(mobileMq.addEventListener)mobileMq.addEventListener('change',respHandler);else mobileMq.addListener(respHandler);
}
@@ -1265,11 +1248,11 @@ function startPollers(){if(S.pollersStarted)return;S.pollersStarted=true;setInte

async function init(){
bindAll();render();
log('fm-rds-tx control UI booting','info');
log('ferrite.fm control UI booting','info');
await Promise.allSettled([loadConfig({silent:false}),loadRuntime({silent:true})]);
render();startPollers();
log('Polling active: runtime 1s · config 8s','ok');
log('Keyboard shortcuts ready','info');
log('UI ready','info');
}
init();
</script>


Loading…
Откажи
Сачувај