NAM hero fills screen, pedal-style FX blocks below
This commit is contained in:
+227
-33
@@ -24,20 +24,39 @@ body{display:flex;flex-direction:column;max-height:100vh;user-select:none;-webki
|
||||
/* Main content */
|
||||
.main-content{flex:1;display:flex;flex-direction:column;overflow:hidden;padding:4px 6px;gap:4px}
|
||||
|
||||
/* FX Chain */
|
||||
.fx-strip{display:flex;gap:6px;overflow-x:auto;overflow-y:hidden;flex-shrink:0;padding:4px 0;min-height:72px;scrollbar-width:none;-ms-overflow-style:none}
|
||||
.fx-strip::-webkit-scrollbar{display:none}
|
||||
.fx-block{flex-shrink:0;width:100px;border-radius:var(--radius);background:var(--surface);border:1.5px solid var(--border);padding:4px 6px;display:flex;flex-direction:column;cursor:pointer;transition:all .15s;position:relative}
|
||||
.fx-block:active{transform:scale(.96)}
|
||||
.fx-block.on{border-color:var(--green);background:rgba(80,208,144,.06)}
|
||||
.fx-block.off{opacity:.4}
|
||||
.fx-block-add{flex-shrink:0;width:72px;border-radius:var(--radius);border:1.5px dashed var(--border);background:transparent;display:flex;align-items:center;justify-content:center;font-size:22px;color:var(--text-dim);cursor:pointer;transition:all .15s}
|
||||
.fx-block-add:active{background:var(--surface);color:var(--amber);border-color:var(--amber)}
|
||||
.fx-type{font-size:8px;font-weight:700;text-transform:uppercase;letter-spacing:.06em;color:var(--text-dim);margin-bottom:1px}
|
||||
.fx-name{font-size:10px;font-weight:600;overflow:hidden;text-overflow:ellipsis;white-space:nowrap;flex:1}
|
||||
.fx-indicator{display:flex;gap:2px;margin-top:auto;padding-top:2px}
|
||||
.fx-dot{width:6px;height:6px;border-radius:50%;background:var(--border)}
|
||||
.fx-dot.active{background:var(--amber)}
|
||||
.main-content{flex:1;display:flex;flex-direction:column;overflow:hidden;padding:3px 5px;gap:3px}
|
||||
|
||||
/* NAM Hero — fills available vertical space */
|
||||
.nam-hero{flex:1;display:flex;flex-direction:column;align-items:center;justify-content:center;background:var(--surface);border-radius:12px;border:2px solid var(--border);position:relative;overflow:hidden;min-height:100px;cursor:pointer;transition:all .15s}
|
||||
.nam-hero:active{transform:scale(.98)}
|
||||
.nam-hero .nam-badge{position:absolute;top:4px;left:6px;font-size:8px;font-weight:700;text-transform:uppercase;letter-spacing:.06em;padding:1px 6px;border-radius:3px;background:rgba(232,160,48,.15);color:var(--amber)}
|
||||
.nam-hero .nam-name{font-size:20px;font-weight:700;text-align:center;padding:0 12px;line-height:1.15;overflow:hidden;text-overflow:ellipsis;display:-webkit-box;-webkit-line-clamp:2;-webkit-box-orient:vertical;max-width:90%}
|
||||
.nam-hero .nam-detail{font-size:10px;color:var(--text2);margin-top:2px;font-variant-numeric:tabular-nums}
|
||||
.nam-hero .nam-led{position:absolute;top:4px;right:6px;width:10px;height:10px;border-radius:50%;box-shadow:0 0 6px}
|
||||
.nam-hero .nam-led.on{background:var(--green);box-shadow:0 0 8px var(--green)}
|
||||
.nam-hero .nam-led.off{background:var(--red);box-shadow:0 0 8px var(--red)}
|
||||
.nam-hero.empty{border-style:dashed;border-color:var(--border);opacity:.5}
|
||||
.nam-hero.empty .nam-name{font-size:14px;color:var(--text3)}
|
||||
|
||||
/* Pedal row — stompbox style FX blocks */
|
||||
.pedal-row{display:flex;gap:5px;overflow-x:auto;overflow-y:hidden;flex-shrink:0;padding:2px 0;min-height:80px;scrollbar-width:none;-ms-overflow-style:none}
|
||||
.pedal-row::-webkit-scrollbar{display:none}
|
||||
.pedal{flex-shrink:0;width:80px;border-radius:8px;background:var(--card);border:2px solid var(--border);display:flex;flex-direction:column;align-items:center;cursor:pointer;transition:all .12s;position:relative;overflow:hidden;min-height:76px}
|
||||
.pedal:active{transform:scale(.93)}
|
||||
.pedal.on{border-color:var(--green)}
|
||||
.pedal.off{opacity:.55;filter:saturate(.4)}
|
||||
.pedal .pd-led{width:8px;height:8px;border-radius:50%;margin-top:5px;flex-shrink:0}
|
||||
.pedal .pd-led.on{background:var(--green);box-shadow:0 0 5px var(--green)}
|
||||
.pedal .pd-led.off{background:var(--red)}
|
||||
.pedal .pd-icon{font-size:13px;margin-top:3px;line-height:1}
|
||||
.pedal .pd-label{font-size:8px;font-weight:600;text-align:center;padding:0 2px;overflow:hidden;text-overflow:ellipsis;white-space:nowrap;width:100%;flex:1;display:flex;align-items:center;justify-content:center}
|
||||
.pedal .pd-switch{width:28px;height:14px;border-radius:7px;margin-bottom:4px;flex-shrink:0;position:relative;transition:background .15s}
|
||||
.pedal .pd-switch.on{background:var(--green);box-shadow:0 0 4px var(--green)}
|
||||
.pedal .pd-switch.off{background:var(--border)}
|
||||
.pedal .pd-switch::after{content:'';position:absolute;top:2px;left:2px;width:10px;height:10px;border-radius:50%;background:var(--text);transition:transform .15s}
|
||||
.pedal .pd-switch.on::after{transform:translateX(14px)}
|
||||
.pedal .pd-add{display:flex;align-items:center;justify-content:center;width:100%;height:100%;font-size:20px;color:var(--text3);border:2px dashed var(--border);border-radius:8px}
|
||||
.pedal .pd-add:active{color:var(--amber);border-color:var(--amber)}
|
||||
|
||||
/* Volume */
|
||||
.vol-row{display:flex;align-items:center;gap:8px;flex-shrink:0;min-height:36px;padding:0 4px}
|
||||
@@ -113,11 +132,20 @@ body{display:flex;flex-direction:column;max-height:100vh;user-select:none;-webki
|
||||
.wifi-connect-pw input:focus{border-color:var(--amber)}
|
||||
.wifi-connect-pw button{min-width:52px;min-height:36px;border-radius:6px;border:1px solid var(--amber);background:var(--amber);color:#000;font-family:var(--font);font-size:11px;font-weight:700;cursor:pointer}
|
||||
.audio-profile-grid{display:grid;grid-template-columns:1fr 1fr;gap:4px}
|
||||
.ap-card{border-radius:6px;border:1px solid var(--border);background:var(--surface);padding:6px;cursor:pointer;text-align:center;transition:all .12s;min-height:40px}
|
||||
.ap-card:active{transform:scale(.97)}
|
||||
.ap-card.active{border-color:var(--amber);background:rgba(232,160,48,.1)}
|
||||
.ap-card .ap-key{font-size:10px;font-weight:700;text-transform:capitalize}
|
||||
.ap-card .ap-desc{font-size:8px;color:var(--text-dim);margin-top:1px}
|
||||
.ap-card{background:var(--surface);border:1px solid var(--border);border-radius:6px;padding:4px 6px;cursor:pointer;transition:all .12s;text-align:center}
|
||||
.ap-card:active{transform:scale(.95)}
|
||||
.ap-card.active{border-color:var(--amber);background:rgba(232,160,48,.08)}
|
||||
.ap-card .ap-key{font-size:9px;font-weight:600;text-transform:capitalize;white-space:nowrap;overflow:hidden;text-overflow:ellipsis}
|
||||
.ap-card .ap-desc{font-size:8px;color:var(--text2)}
|
||||
.ap-card.loading{opacity:.5;pointer-events:none}
|
||||
.ap-card.loading .ap-key::after{content:' ⟳';animation:spin .6s linear infinite;display:inline-block}
|
||||
|
||||
/* Loading overlay */
|
||||
.loading-overlay{position:fixed;inset:0;background:rgba(0,0,0,.5);z-index:300;display:flex;align-items:center;justify-content:center;flex-direction:column;gap:8px}
|
||||
.loading-overlay .spinner{width:28px;height:28px;border:3px solid var(--border);border-top-color:var(--amber);border-radius:50%;animation:spin .6s linear infinite}
|
||||
.loading-overlay .load-msg{font-size:12px;font-weight:600;color:var(--text2)}
|
||||
|
||||
@keyframes spin{to{transform:rotate(360deg)}}
|
||||
.snap-grid{display:grid;grid-template-columns:1fr 1fr 1fr 1fr;gap:4px}
|
||||
.snap-slot{border-radius:6px;border:1px solid var(--border);background:var(--surface);padding:6px 3px;cursor:pointer;text-align:center;transition:all .12s;min-height:40px;display:flex;flex-direction:column;align-items:center;justify-content:center}
|
||||
.snap-slot:active{transform:scale(.95)}
|
||||
@@ -146,8 +174,16 @@ body{display:flex;flex-direction:column;max-height:100vh;user-select:none;-webki
|
||||
|
||||
<!-- Main Content -->
|
||||
<div class="main-content">
|
||||
<div class="fx-strip" id="fxStrip">
|
||||
<div class="fx-block-add" id="btnAddBlock">+</div>
|
||||
<div class="nam-hero empty" id="namHero" onclick="switchTab('FX')">
|
||||
<div class="nam-badge" id="namBadge">NAM</div>
|
||||
<div class="nam-name" id="namName">No Model Loaded</div>
|
||||
<div class="nam-detail" id="namDetail">—</div>
|
||||
<div class="nam-led off" id="namLed"></div>
|
||||
</div>
|
||||
<div class="pedal-row" id="pedalRow">
|
||||
<div class="pedal" id="btnAddPedal" onclick="showAddBlock()">
|
||||
<div class="pd-add">+</div>
|
||||
</div>
|
||||
</div>
|
||||
<div class="vol-row">
|
||||
<span class="vol-label">Vol</span>
|
||||
@@ -224,6 +260,44 @@ body{display:flex;flex-direction:column;max-height:100vh;user-select:none;-webki
|
||||
<div class="setting-row"><span class="sr-label">🔄 Channel Mode</span><span class="sr-value" id="channelModeLabel">Dual Mono</span><button class="sr-action" id="channelModeToggle">Toggle</button></div>
|
||||
<div class="setting-row"><span class="sr-label">🔗 Routing</span><span class="sr-value" id="routingModeLabel">4CM</span><button class="sr-action" id="routingModeToggle">Toggle</button></div>
|
||||
</div>
|
||||
<div class="setting-group"><div class="setting-group-title">🎚 Audio Interface</div>
|
||||
<div class="setting-row"><span class="sr-label">🔌 Input Device</span><select id="audioInputDevice" style="flex:1;min-width:0;font-size:10px;padding:4px 6px;min-height:30px" onchange="setAudioDevice(this.value)"></select></div>
|
||||
<div class="ch-card" id="ch1Card" style="background:var(--card2);border-radius:8px;padding:6px 8px;margin:4px 0;border:2px solid rgba(232,160,48,.25)">
|
||||
<div style="display:flex;align-items:center;justify-content:space-between;margin-bottom:4px">
|
||||
<span style="font-size:11px;font-weight:700;color:var(--amber)">🔴 CH 1 — Input 1/L</span>
|
||||
<button id="ch1Power" onclick="toggleChPower(0)" style="width:32px;height:32px;border-radius:6px;display:flex;align-items:center;justify-content:center;font-size:12px;background:rgba(58,184,122,.2);border:1px solid rgba(58,184,122,.4);color:var(--green)">⚡</button>
|
||||
</div>
|
||||
<div style="display:flex;align-items:center;gap:6px">
|
||||
<span style="font-size:10px;color:var(--text2);flex-shrink:0">Instrument:</span>
|
||||
<select id="ch1Instrument" onchange="setChInstrument(0,this.value)" style="flex:1;min-width:0;font-size:10px;padding:3px 6px;min-height:28px">
|
||||
<option value="guitar">🎸 Guitar</option>
|
||||
<option value="bass">🎸 Bass</option>
|
||||
<option value="keys">🎹 Keys</option>
|
||||
<option value="vocals">🎤 Vocals</option>
|
||||
<option value="backing_tracks">📻 Backing</option>
|
||||
</select>
|
||||
</div>
|
||||
<div style="font-size:9px;color:var(--text2);margin-top:2px">Output: Main L/R (Scarlett 1+2)</div>
|
||||
</div>
|
||||
<div class="ch-card" id="ch2Card" style="background:var(--card2);border-radius:8px;padding:6px 8px;margin:4px 0;border:2px solid rgba(58,184,122,.25)">
|
||||
<div style="display:flex;align-items:center;justify-content:space-between;margin-bottom:4px">
|
||||
<span style="font-size:11px;font-weight:700;color:var(--green)">🟢 CH 2 — Input 2/R</span>
|
||||
<button id="ch2Power" onclick="toggleChPower(1)" style="width:32px;height:32px;border-radius:6px;display:flex;align-items:center;justify-content:center;font-size:12px;background:rgba(58,184,122,.2);border:1px solid rgba(58,184,122,.4);color:var(--green)">⚡</button>
|
||||
</div>
|
||||
<div style="display:flex;align-items:center;gap:6px">
|
||||
<span style="font-size:10px;color:var(--text2);flex-shrink:0">Instrument:</span>
|
||||
<select id="ch2Instrument" onchange="setChInstrument(1,this.value)" style="flex:1;min-width:0;font-size:10px;padding:3px 6px;min-height:28px">
|
||||
<option value="guitar">🎸 Guitar</option>
|
||||
<option value="bass">🎸 Bass</option>
|
||||
<option value="keys">🎹 Keys</option>
|
||||
<option value="vocals">🎤 Vocals</option>
|
||||
<option value="backing_tracks">📻 Backing</option>
|
||||
</select>
|
||||
</div>
|
||||
<div style="font-size:9px;color:var(--text2);margin-top:2px">Output: Main L/R (Scarlett 1+2)</div>
|
||||
</div>
|
||||
<div style="font-size:9px;color:var(--text3);padding:2px 0">Dual Mono: each channel has independent presets. Same instrument = shared presets.</div>
|
||||
</div>
|
||||
<div class="setting-group"><div class="setting-group-title">Bluetooth</div>
|
||||
<div class="setting-row"><span class="sr-label">🔵 Status</span><span class="sr-value" id="btStatus">—</span></div>
|
||||
<div class="setting-row"><span class="sr-label">🎛 MIDI</span><span class="sr-value" id="btMidiStatus">Off</span><button class="sr-action" id="btMidiToggle">Enable</button></div>
|
||||
@@ -332,6 +406,59 @@ function cycleInstrument(){
|
||||
localStorage.setItem('pedalCurrentChannel',currentChannel);
|
||||
toast(currentChannel);
|
||||
}
|
||||
|
||||
// Loading overlay
|
||||
function showLoading(msg){
|
||||
let el=document.getElementById('loadingOverlay');
|
||||
if(!el){
|
||||
el=document.createElement('div');el.id='loadingOverlay';el.className='loading-overlay';
|
||||
el.innerHTML='<div class=\"spinner\"></div><div class=\"load-msg\"></div>';
|
||||
document.body.appendChild(el);
|
||||
}
|
||||
el.querySelector('.load-msg').textContent=msg||'Loading...';
|
||||
el.style.display='flex';
|
||||
}
|
||||
function hideLoading(){
|
||||
const el=document.getElementById('loadingOverlay');
|
||||
if(el)el.style.display='none';
|
||||
}
|
||||
|
||||
// Audio Interface helpers
|
||||
async function loadAudioDevices(){
|
||||
const sel=document.getElementById('audioInputDevice');
|
||||
if(!sel)return;
|
||||
const devs=await API.listAudioDevices();
|
||||
sel.innerHTML=(devs||[]).map(d=>'<option value="'+d.id+'">'+d.name+'</option>').join('')||'<option value="">No devices</option>';
|
||||
}
|
||||
function setChInstrument(idx,val){
|
||||
const arr=JSON.parse(localStorage.getItem('chInstruments')||'["guitar","bass"]');
|
||||
arr[idx]=val;
|
||||
localStorage.setItem('chInstruments',JSON.stringify(arr));
|
||||
API.saveSettings({['ch'+(idx+1)+'_instrument']:val}).catch(()=>{});
|
||||
}
|
||||
function toggleChPower(idx){
|
||||
const btn=document.getElementById('ch'+(idx+1)+'Power');
|
||||
const on=btn.textContent==='⚡';
|
||||
btn.textContent=on?'⭕':'⚡';
|
||||
btn.style.background=on?'rgba(160,160,188,.1)':'rgba(58,184,122,.2)';
|
||||
btn.style.borderColor=on?'rgba(160,160,188,.2)':'rgba(58,184,122,.4)';
|
||||
btn.style.color=on?'var(--text3)':'var(--green)';
|
||||
API.bypassToggle(channels[idx]||'guitar').catch(()=>{});
|
||||
}
|
||||
async function setAudioDevice(devId){
|
||||
if(!devId)return;
|
||||
showLoading('Switching audio device...');
|
||||
await API.saveSettings({input_device:devId,output_device:devId}).catch(()=>{});
|
||||
hideLoading();
|
||||
toast('Device: '+devId);
|
||||
}
|
||||
function loadChInstrumentState(){
|
||||
const arr=JSON.parse(localStorage.getItem('chInstruments')||'["guitar","bass"]');
|
||||
['ch1Instrument','ch2Instrument'].forEach((id,i)=>{
|
||||
const el=document.getElementById(id);
|
||||
if(el)el.value=arr[i]||(i===0?'guitar':'bass');
|
||||
});
|
||||
}
|
||||
function toast(msg,t){const e=$('#toast');e.textContent=msg;e.classList.add('show');clearTimeout(e._h);e._h=setTimeout(()=>e.classList.remove('show'),t||2000);}
|
||||
|
||||
// ═══ Overlay Controller ═══
|
||||
@@ -372,7 +499,7 @@ async function loadState(){
|
||||
if(s.bypass){fs.className='footswitch off';fs.textContent='MUTE';}
|
||||
else{fs.className='footswitch on';fs.textContent='BYpass';}
|
||||
document.getElementById('cpuLabel').textContent=(s.cpu_percent!=null?s.cpu_percent:'—')+'%';
|
||||
renderFxBlocks();
|
||||
renderNamHero();
|
||||
const tb=document.getElementById('tabTuner');
|
||||
if(s.tuner_enabled){tb.querySelector('.tab-label').textContent='Tuning';tb.classList.add('active');}
|
||||
else{tb.querySelector('.tab-label').textContent='Tuner';tb.classList.remove('active');}
|
||||
@@ -381,20 +508,79 @@ async function loadState(){
|
||||
|
||||
// ═══ FX Blocks ═══
|
||||
const BLOCK_COLORS={nam_amp:'#E8A030',ir:'#4090E0',gate:'#50D090',compressor:'#E0E060',eq:'#A080E0',reverb:'#60C0E0',delay:'#80D0A0',distortion:'#E06060'};
|
||||
function renderFxBlocks(){
|
||||
const strip=document.getElementById('fxStrip');
|
||||
const FX_ICONS={nam_amp:'🎛',nam:'🎛',overdrive:'⚡',distortion:'💥',delay:'⏳',reverb:'🌊',chorus:'🌀',compressor:'📈',eq:'📊',cabinet:'📦',ir:'📦',volume:'🔊',gate:'🚪',tremolo:'〰️',phaser:'🌀',flanger:'🌊',boost:'🚀',pitch:'🎵',filter:'🔀'};
|
||||
const FX_COLORS={nam_amp:'#E0A040',nam:'#E0A040',overdrive:'#E8A030',distortion:'#E04040',delay:'#3090E0',reverb:'#40B0A0',chorus:'#50C080',compressor:'#D09040',eq:'#7080D0',cabinet:'#D07090',ir:'#D07090',volume:'#8888A0',gate:'#80A0C0',tremolo:'#C080D0',phaser:'#40B0D0',flanger:'#60A0E0',boost:'#E8A030',pitch:'#B080D0',filter:'#80C0A0'};
|
||||
|
||||
function renderNamHero(){
|
||||
const hero=document.getElementById('namHero');
|
||||
const nameEl=document.getElementById('namName');
|
||||
const detailEl=document.getElementById('namDetail');
|
||||
const ledEl=document.getElementById('namLed');
|
||||
const badgeEl=document.getElementById('namBadge');
|
||||
const blocks=state.blocks||[];
|
||||
const addBtn=document.getElementById('btnAddBlock');
|
||||
strip.innerHTML='';
|
||||
strip.appendChild(addBtn);
|
||||
blocks.forEach((b,i)=>{
|
||||
const div=document.createElement('div');
|
||||
const namBlock=blocks.find(b=>b.fx_type==='nam_amp'||b.fx_type==='nam');
|
||||
if(namBlock){
|
||||
hero.className='nam-hero';
|
||||
hero.style.borderColor=FX_COLORS[namBlock.fx_type]||'var(--border)';
|
||||
badgeEl.textContent=(namBlock.fx_type||'NAM').toUpperCase();
|
||||
badgeEl.style.background=(FX_COLORS[namBlock.fx_type]||'#E0A040')+'22';
|
||||
badgeEl.style.color=FX_COLORS[namBlock.fx_type]||'#E0A040';
|
||||
nameEl.textContent=namBlock.name||namBlock.nam_model_name||'NAM Model';
|
||||
const enabled=namBlock.enabled!==false;
|
||||
ledEl.className='nam-led '+(enabled?'on':'off');
|
||||
ledEl.style.background=enabled?'var(--green)':'var(--red)';
|
||||
const detailParts=[];
|
||||
if(namBlock.nam_model_name)detailParts.push(namBlock.nam_model_name);
|
||||
detailParts.push(enabled?'ON':'OFF');
|
||||
if(state.nam_loaded)detailParts.push('loaded');
|
||||
detailEl.textContent=detailParts.join(' · ')||'—';
|
||||
}else{
|
||||
hero.className='nam-hero empty';
|
||||
hero.style.borderColor='';
|
||||
nameEl.textContent='No NAM Model';
|
||||
detailEl.textContent='Tap to add amp block';
|
||||
ledEl.className='nam-led off';
|
||||
badgeEl.textContent='NAM';
|
||||
badgeEl.style='';
|
||||
}
|
||||
renderPedals();
|
||||
}
|
||||
function renderPedals(){
|
||||
const row=document.getElementById('pedalRow');
|
||||
const addBtn=document.getElementById('btnAddPedal');
|
||||
row.innerHTML='';
|
||||
row.appendChild(addBtn);
|
||||
const nonNam=(state.blocks||[]).filter(b=>b.fx_type!=='nam_amp'&&b.fx_type!=='nam');
|
||||
if(nonNam.length===0){
|
||||
const empty=document.createElement('div');
|
||||
empty.style.cssText='flex-shrink:0;display:flex;align-items:center;justify-content:center;font-size:9px;color:var(--text3);padding:0 8px;min-height:76px';
|
||||
empty.textContent='No FX pedals — tap + to add';
|
||||
row.insertBefore(empty,addBtn);
|
||||
}
|
||||
(state.blocks||[]).forEach((b,i)=>{
|
||||
if(b.fx_type==='nam_amp'||b.fx_type==='nam')return; // NAM shown in hero
|
||||
const en=b.enabled!==false&&!b.bypass;
|
||||
div.className='fx-block '+(en?'on':'off');
|
||||
div.innerHTML='<div class="fx-type">'+(b.fx_type||'fx')+'</div><div class="fx-name">'+(b.name||b.fx_type||'Block')+'</div><div class="fx-indicator">'+[...Array(4)].map((_,j)=>'<div class="fx-dot'+(j<3?' active':'')+'"></div>').join('')+'</div>';
|
||||
strip.insertBefore(div,addBtn);
|
||||
const type=b.fx_type||'fx';
|
||||
const name=b.name||b.fx_type||'Block';
|
||||
const color=FX_COLORS[type]||'#E8A030';
|
||||
const icon=FX_ICONS[type]||'🎛';
|
||||
const pedal=document.createElement('div');
|
||||
pedal.className='pedal '+(en?'on':'off');
|
||||
pedal.style.borderColor=en?color:'var(--border)';
|
||||
pedal.innerHTML='<div class="pd-led '+(en?'on':'off')+'" style="box-shadow:'+(en?'0 0 5px '+color:'none')+'"></div><div class="pd-icon">'+icon+'</div><div class="pd-label">'+name+'</div><div class="pd-switch '+(en?'on':'off')+'"></div>';
|
||||
pedal.onclick=()=>{toggleBlock(i);};
|
||||
row.insertBefore(pedal,addBtn);
|
||||
});
|
||||
}
|
||||
async function toggleBlock(idx){
|
||||
const blocks=state.blocks||[];
|
||||
const b=blocks[idx];
|
||||
if(!b)return;
|
||||
const newEn=!(b.enabled!==false);
|
||||
await API.toggleBlock(currentChannel,b.block_id||idx,newEn).catch(()=>{});
|
||||
loadState();
|
||||
}
|
||||
function showAddBlock(){openOverlay('Downloads');}
|
||||
|
||||
// ═══ Presets ═══
|
||||
async function loadPresets(){
|
||||
@@ -481,7 +667,14 @@ async function loadAudioProfiles(){
|
||||
const d=document.createElement('div');
|
||||
d.className='ap-card'+(p.key===cp?' active':'');
|
||||
d.innerHTML='<div class="ap-key">'+p.key.replace(/_/g,' ')+'</div><div class="ap-desc">'+p.period+'fr / '+p.rate+'Hz</div>'+((p.key===cp)?'<div style="font-size:8px;color:var(--amber)">● Active</div>':'');
|
||||
d.onclick=()=>{API.setAudioProfile(p.key).then(()=>{toast('Profile: '+p.key);loadAudioProfiles();}).catch(e=>toast(e.message));};
|
||||
d.onclick=async()=>{
|
||||
d.classList.add('loading');
|
||||
try{
|
||||
await API.setAudioProfile(p.key);
|
||||
toast('Profile: '+p.key);
|
||||
loadAudioProfiles();
|
||||
}catch(e){toast(e.message);d.classList.remove('loading');}
|
||||
};
|
||||
grid.appendChild(d);
|
||||
});
|
||||
document.getElementById('audioProfileLabel').textContent=cp;
|
||||
@@ -606,6 +799,7 @@ function init(){
|
||||
openOverlay('Settings');
|
||||
await loadAudioProfiles();await loadBtStatus();await loadBtMidi();await loadHotspotStatus();
|
||||
await loadNetworkInfo();await loadSystemInfo();await doWifiScan();
|
||||
loadAudioDevices();loadChInstrumentState();
|
||||
};
|
||||
|
||||
// Close all overlays
|
||||
|
||||
Reference in New Issue
Block a user