feat: add VU meter to status bar

Shows input (green) and output (amber) level bars in the status bar,
updated every 2s from /api/state. Scales factor 300x to make small
signals visible, capped at 100%.
This commit is contained in:
2026-06-17 17:48:44 -04:00
parent 1619b2e90e
commit fab8564d09
+12
View File
@@ -18,6 +18,12 @@ body{display:flex;flex-direction:column;max-height:100vh;user-select:none;-webki
.status-channel{font-size:10px;font-weight:700;letter-spacing:.04em;text-transform:uppercase;color:var(--amber);padding:2px 6px;border-radius:4px;background:rgba(232,160,48,.12);white-space:nowrap} .status-channel{font-size:10px;font-weight:700;letter-spacing:.04em;text-transform:uppercase;color:var(--amber);padding:2px 6px;border-radius:4px;background:rgba(232,160,48,.12);white-space:nowrap}
.status-preset{flex:1;overflow:hidden;text-overflow:ellipsis;white-space:nowrap;font-size:11px;font-weight:600;min-width:0} .status-preset{flex:1;overflow:hidden;text-overflow:ellipsis;white-space:nowrap;font-size:11px;font-weight:600;min-width:0}
.status-vol{font-size:10px;font-weight:700;color:var(--amber);white-space:nowrap;font-variant-numeric:tabular-nums} .status-vol{font-size:10px;font-weight:700;color:var(--amber);white-space:nowrap;font-variant-numeric:tabular-nums}
.vu-pair{display:flex;align-items:center;gap:2px;margin-left:4px;flex-shrink:0}
.vu-track{width:28px;height:5px;background:rgba(255,255,255,.08);border-radius:3px;position:relative;overflow:hidden}
.vu-fill{position:absolute;top:0;left:0;height:100%;border-radius:3px;transition:width .04s linear}
.vu-fill.in{background:var(--green)}
.vu-fill.out{background:var(--amber)}
.vu-lbl{font-size:6px;color:var(--text-dim);letter-spacing:.03em;line-height:1;width:10px;text-align:right}
.status-btn{width:40px;height:40px;border:none;background:transparent;color:var(--text-dim);font-size:16px;border-radius:6px;display:flex;align-items:center;justify-content:center;cursor:pointer;flex-shrink:0;transition:all .15s} .status-btn{width:40px;height:40px;border:none;background:transparent;color:var(--text-dim);font-size:16px;border-radius:6px;display:flex;align-items:center;justify-content:center;cursor:pointer;flex-shrink:0;transition:all .15s}
.status-btn:active{background:var(--border);color:var(--amber)} .status-btn:active{background:var(--border);color:var(--amber)}
@@ -166,6 +172,8 @@ body{display:flex;flex-direction:column;max-height:100vh;user-select:none;-webki
<div class="status-channel" id="statusChannel" onclick="cycleInstrument()" style="cursor:pointer">Guitar</div> <div class="status-channel" id="statusChannel" onclick="cycleInstrument()" style="cursor:pointer">Guitar</div>
<div class="status-preset" id="statusPreset"></div> <div class="status-preset" id="statusPreset"></div>
<div class="status-vol" id="statusVolPct">80%</div> <div class="status-vol" id="statusVolPct">80%</div>
<div class="vu-pair"><span class="vu-lbl">I</span><div class="vu-track"><div class="vu-fill in" id="vuIn" style="width:0%"></div></div></div>
<div class="vu-pair"><span class="vu-lbl">O</span><div class="vu-track"><div class="vu-fill out" id="vuOut" style="width:0%"></div></div></div>
<button class="status-btn" id="btnChannel" onclick="cycleInstrument()">🎛</button> <button class="status-btn" id="btnChannel" onclick="cycleInstrument()">🎛</button>
<button class="status-btn" id="btnSettings"></button> <button class="status-btn" id="btnSettings"></button>
</div> </div>
@@ -649,6 +657,10 @@ async function loadState(){
if(s.bypass){fs.className='footswitch off';fs.textContent='MUTE';} if(s.bypass){fs.className='footswitch off';fs.textContent='MUTE';}
else{fs.className='footswitch on';fs.textContent='BYpass';} else{fs.className='footswitch on';fs.textContent='BYpass';}
document.getElementById('cpuLabel').textContent=(s.cpu_percent!=null?s.cpu_percent:'—')+'%'; document.getElementById('cpuLabel').textContent=(s.cpu_percent!=null?s.cpu_percent:'—')+'%';
const vi=Math.min(100,Math.round((s.input_level||0)*300));
const vo=Math.min(100,Math.round((s.output_level||0)*300));
document.getElementById('vuIn').style.width=vi+'%';
document.getElementById('vuOut').style.width=vo+'%';
renderNamHero(); renderNamHero();
const tb=document.getElementById('tabTuner'); const tb=document.getElementById('tabTuner');
if(s.tuner_enabled){tb.querySelector('.tab-label').textContent='Tuning';tb.classList.add('active');} if(s.tuner_enabled){tb.querySelector('.tab-label').textContent='Tuning';tb.classList.add('active');}