From 9e3ab9d53ba94ddd87236c4bd5fe90db33b02aea Mon Sep 17 00:00:00 2001 From: Shawn Date: Sun, 14 Jun 2026 00:01:46 +0000 Subject: [PATCH] fix: NAM amp level param now controls input drive, JACK period 256, toggle API matching --- main.py | 7 +-- src/dsp/nam_engine_host.py | 12 ++++- src/dsp/pipeline.py | 14 ++++++ src/system/audio.py | 50 ++++++++++++++----- src/web/server.py | 14 +++--- .../{index-B7j_x4LZ.js => index-CHS9Y8OM.js} | 2 +- src/web/ui-dist/index.html | 2 +- 7 files changed, 74 insertions(+), 27 deletions(-) rename src/web/ui-dist/assets/{index-B7j_x4LZ.js => index-CHS9Y8OM.js} (96%) diff --git a/main.py b/main.py index a923aeb..59b838d 100644 --- a/main.py +++ b/main.py @@ -25,7 +25,7 @@ import yaml from src.system.audio import AudioConfig, AudioSystem, JackAudioClient, _jack_is_running from src.system.defaults import ensure_defaults_exist from src.dsp.pipeline import AudioPipeline -from src.dsp.nam_host import NAMHost +from src.dsp.nam_engine_host import FastNAMHost from src.dsp.ir_loader import IRLoader from src.presets.manager import PresetManager from src.presets.types import Channel, MIDIMapping, Preset @@ -79,7 +79,7 @@ class PedalApp: self.audio_config: AudioConfig | None = None self.audio_system: AudioSystem | None = None self.jack_audio: JackAudioClient | None = None - self.nam_host: NAMHost | None = None + self.nam_host: FastNAMHost | None = None self.ir_loader: IRLoader | None = None self.pipeline: AudioPipeline | None = None self.presets: PresetManager | None = None @@ -133,9 +133,10 @@ class PedalApp: logger.info("JACK disabled in config — skipping audio server start") # ── 2. DSP pipeline (NAM + IR + FX chain) ──────────── - self.nam_host = NAMHost() + self.nam_host = FastNAMHost() self.ir_loader = IRLoader() self.pipeline = AudioPipeline(nam_host=self.nam_host, ir_loader=self.ir_loader) + self.nam_host.warm_up() # Set pipeline routing from config mode if audio_mode == "stereo_4cm": diff --git a/src/dsp/nam_engine_host.py b/src/dsp/nam_engine_host.py index 745fb83..e583c95 100644 --- a/src/dsp/nam_engine_host.py +++ b/src/dsp/nam_engine_host.py @@ -98,7 +98,17 @@ class FastNAMHost: def process(self, audio_block): if self._engine is None or not self._engine.is_loaded: return audio_block - return self._engine.process(audio_block) + in_peak = float(np.max(np.abs(audio_block))) + result = self._engine.process(audio_block) + out_peak = float(np.max(np.abs(result))) + has_nan = bool(np.any(np.isnan(result))) + has_inf = bool(np.any(np.isinf(result))) + import os as _os + _os.system('echo "NAM_PROC in_pk={:.6f} out_pk={:.6f} nan={} inf={} sz={}" >> /tmp/nam_debug.log'.format(in_peak, out_peak, has_nan, has_inf, len(audio_block))) + if has_nan or has_inf: + logger.error("NAM engine NaN/Inf block_size=%d", self._block_size) + return np.zeros_like(audio_block) + return result _crossfade_buf = None diff --git a/src/dsp/pipeline.py b/src/dsp/pipeline.py index 8118be2..8413ce6 100644 --- a/src/dsp/pipeline.py +++ b/src/dsp/pipeline.py @@ -698,10 +698,24 @@ class AudioPipeline: return self._apply_volume(buf, params, fx_state) case FXType.NAM_AMP: if self.nam.is_loaded: + import numpy as np + import os as _os + _os.system('echo "PIPELINE: NAM_AMP is_loaded=True buf_peak={:.6f}" >> /tmp/pipeline_debug.log'.format(float(np.max(np.abs(buf))))) + in_rms = 20 * np.log10(np.sqrt(np.mean(buf**2)) + 1e-10) processed = self.nam.process(buf) + _os.system('echo "PIPELINE: NAM_AMP processed peak={:.6f}" >> /tmp/pipeline_debug.log'.format(float(np.max(np.abs(processed))))) + out_rms = 20 * np.log10(np.sqrt(np.mean(processed**2)) + 1e-10) + logger.debug("NAM %s: in=%.1fdBFS out=%.1fdBFS gain=%.1fdB", + getattr(self.nam, '_loaded_path', '?'), + in_rms, out_rms, out_rms - in_rms) if self.nam._crossfade_buf is not None: processed = self.nam.apply_crossfade(processed) + level = params.get("level", 0.75) + attenuated = buf * (level * 2.0) + processed = self.nam.process(attenuated) return processed + _os.system('echo "PIPELINE: NAM_AMP is_loaded=False" >> /tmp/pipeline_debug.log') + logger.debug("NAM_AMP: engine not loaded, passing through") return buf case FXType.IR_CAB: if self.ir.is_loaded: diff --git a/src/system/audio.py b/src/system/audio.py index a0897d1..cd56d1e 100644 --- a/src/system/audio.py +++ b/src/system/audio.py @@ -79,7 +79,7 @@ FOCUSRITE_PROFILES: dict[str, tuple[str, int, int]] = { # 48 kHz / 64 frames = 1.33 ms buffer → aggressive, may xrun on RPi 4B LATENCY_PROFILES: dict[str, dict] = { "standard": { - "period": 128, + "period": 256, "nperiods": 2, "rate": 48000, "rt_priority": 70, @@ -415,9 +415,12 @@ class AudioSystem: This is a no-op if jack_connect is unavailable (not in PATH) or if any of the target ports don't exist yet. """ + # Mono: duplicate processed signal to both L/R channels + # so Scarlett 2i2 headphone jack (which mirrors stereo pair) works. mono_connections = [ ("system:capture_1", "fx_in:input_0"), ("fx_out:output_0", "system:playback_1"), + ("fx_out:output_0", "system:playback_2"), ] stereo_4cm_connections = [ ("system:capture_1", "fx_in:input_0"), @@ -934,18 +937,33 @@ class JackAudioClient: logger.warning("Failed to connect %s -> %s: %s", sys_port_name, port.name, exc) - # Connect each playback port to the corresponding system playback + # Connect each playback port to the corresponding system playback. + # In mono mode (single outport), duplicate to both L/R channels + # so Scarlett 2i2 headphone jack (which mirrors stereo pair) works. for i, port in enumerate(self._outports): - suffix = f"playback_{i + 1}" if self._output_channels > 1 else "playback_1" - sys_port_name = f"system:{suffix}" - try: - sys_ports = self._client.get_ports(sys_port_name) - if sys_ports: - self._client.connect(port, sys_ports[0]) - logger.debug("Connected %s -> %s", port.name, sys_port_name) - except Exception as exc: - logger.warning("Failed to connect %s -> %s: %s", - port.name, sys_port_name, exc) + if self._output_channels == 1: + # Mono: connect to both playback_1 and playback_2 + for ch in [1, 2]: + sys_port_name = f"system:playback_{ch}" + try: + sys_ports = self._client.get_ports(sys_port_name) + if sys_ports: + self._client.connect(port, sys_ports[0]) + logger.debug("Mono: connected %s -> %s", port.name, sys_port_name) + except Exception as exc: + logger.warning("Failed to connect %s -> %s: %s", + port.name, sys_port_name, exc) + else: + suffix = f"playback_{i + 1}" + sys_port_name = f"system:{suffix}" + try: + sys_ports = self._client.get_ports(sys_port_name) + if sys_ports: + self._client.connect(port, sys_ports[0]) + logger.debug("Connected %s -> %s", port.name, sys_port_name) + except Exception as exc: + logger.warning("Failed to connect %s -> %s: %s", + port.name, sys_port_name, exc) # ── Runtime reconfiguration ──────────────────────────────────── @@ -983,6 +1001,9 @@ class JackAudioClient: for ch in range(self._input_channels): port_buf = self._inports[ch].get_array() self._in_buf[ch, :frames] = port_buf[:frames] + # DEBUG: always log + import os + os.system('echo "CB frames=' + str(frames) + '" >> /tmp/debug_audio.log') # ── Run through pipeline ───────────────────────────────── if self._input_channels == 1: @@ -992,8 +1013,11 @@ class JackAudioClient: try: audio_out = self._pipeline.process(audio_in) - except Exception: + except Exception as exc: # Never let an exception escape the RT callback + logger.error("Pipeline error: %s", str(exc)) + import traceback + logger.error("Traceback:\n%s", traceback.format_exc()) audio_out = np.zeros(frames, dtype=np.float32) if self._output_channels == 1 else \ np.zeros((self._output_channels, frames), dtype=np.float32) diff --git a/src/web/server.py b/src/web/server.py index b1de27c..be84666 100644 --- a/src/web/server.py +++ b/src/web/server.py @@ -1748,16 +1748,14 @@ class WebServer: "channel_mode": self._channel_mgr.mode, # Audio levels from pipeline (RMS float32 normalized 0.0-1.0, scaled to 0-100) "input_level": ( - min(100, round((getattr(pl, '_input_level', 0.0) or 0.0) * 150)) - if pl and isinstance(getattr(pl, '_input_level', None), (int, float)) - and not math.isnan(getattr(pl, '_input_level', 0.0)) - else 0 + 0 + if pl is None or getattr(pl, '_input_level', None) is None + else max(0, min(100, round(0 if (v := float(getattr(pl, '_input_level', 0.0))) != v else v * 150))) ), "output_level": ( - min(100, round((getattr(pl, '_output_level', 0.0) or 0.0) * 150)) - if pl and isinstance(getattr(pl, '_output_level', None), (int, float)) - and not math.isnan(getattr(pl, '_output_level', 0.0)) - else 0 + 0 + if pl is None or getattr(pl, '_output_level', None) is None + else max(0, min(100, round(0 if (v := float(getattr(pl, '_output_level', 0.0))) != v else v * 150))) ), # System stats "cpu_percent": self._gather_system_stats().get("cpu_percent", 0), diff --git a/src/web/ui-dist/assets/index-B7j_x4LZ.js b/src/web/ui-dist/assets/index-CHS9Y8OM.js similarity index 96% rename from src/web/ui-dist/assets/index-B7j_x4LZ.js rename to src/web/ui-dist/assets/index-CHS9Y8OM.js index 2e86ead..04716e1 100644 --- a/src/web/ui-dist/assets/index-B7j_x4LZ.js +++ b/src/web/ui-dist/assets/index-CHS9Y8OM.js @@ -97,4 +97,4 @@ Error generating stack: `+e.message+` color: ${z.textDim}; font-weight: 600; margin-bottom: 8px; } .card { background: ${z.panel}; border: 1px solid ${z.border}; border-radius: 8px; padding: 14px; } .card-sm { background: ${z.surface}; border: 1px solid ${z.border}; border-radius: 6px; padding: 10px; } -`+x;function _t({label:e,value:t=50,onChange:n,size:r=48,color:i=z.amber,min:a=0,max:o=100}){let s=(0,l.useRef)(null),c=(t-a)/(o-a),u=-140+c*280,d=r*.38,p=r*.5-d,m=e=>{e.preventDefault(),s.current={y:e.touches?e.touches[0].clientY:e.clientY,val:t};let r=e=>{let t=e.touches?e.touches[0].clientY:e.clientY,r=(s.current.y-t)/120,i=Math.max(a,Math.min(o,s.current.val+r*(o-a)));n?.(Math.round(i))},i=()=>{window.removeEventListener(`mousemove`,r),window.removeEventListener(`mouseup`,i),window.removeEventListener(`touchmove`,r),window.removeEventListener(`touchend`,i)};window.addEventListener(`mousemove`,r),window.addEventListener(`mouseup`,i),window.addEventListener(`touchmove`,r,{passive:!1}),window.addEventListener(`touchend`,i)},h=r+12,g=h/2,_=h/2,v=r/2+3,y=g+v*Math.cos(Math.PI/180*-230),b=_+v*Math.sin(Math.PI/180*-230),x=g+v*Math.cos((u-90)*(Math.PI/180)),S=_+v*Math.sin((u-90)*(Math.PI/180)),C=+(c*280>180);return(0,f.jsxs)(`div`,{className:`knob-wrap`,style:{width:r+12},children:[(0,f.jsxs)(`div`,{className:`knob`,style:{width:r,height:r},onMouseDown:m,onTouchStart:m,children:[(0,f.jsxs)(`svg`,{style:{position:`absolute`,inset:-6,width:h,height:h,overflow:`visible`},children:[(0,f.jsx)(`circle`,{cx:g,cy:_,r:v,fill:`none`,stroke:z.border,strokeWidth:`2`,strokeLinecap:`round`,opacity:`.5`}),c>0&&(0,f.jsx)(`path`,{d:`M ${y} ${b} A ${v} ${v} 0 ${C} 1 ${x} ${S}`,fill:`none`,stroke:i,strokeWidth:`2`,strokeLinecap:`round`,style:{filter:`drop-shadow(0 0 3px ${i})`}})]}),(0,f.jsx)(`div`,{className:`knob-inner`,style:{"--amber":i},children:(0,f.jsx)(`div`,{className:`knob-dot`,style:{height:d,top:p,transform:`translateX(-50%) rotate(${u}deg)`,transformOrigin:`50% ${d}px`}})})]}),(0,f.jsx)(`div`,{className:`knob-value mono`,style:{color:i},children:t}),(0,f.jsx)(`div`,{className:`knob-label`,children:e})]})}function vt({label:e,value:t=50,onChange:n,onChangeEnd:r,min:i=0,max:a=100,color:o=z.amber}){let s=(0,l.useRef)(null),c=(0,l.useRef)(!1),u=(0,l.useRef)(t),d=(0,l.useRef)(n),p=(0,l.useRef)(r),m=(t-i)/(a-i)*100;(0,l.useEffect)(()=>{d.current=n},[n]),(0,l.useEffect)(()=>{p.current=r},[r]);let h=(0,l.useCallback)(e=>{let n=s.current;if(!n)return t;let r=n.getBoundingClientRect(),o=(e.touches?e.touches[0].clientX:e.clientX)-r.left;return Math.round(i+Math.max(0,Math.min(1,o/r.width))*(a-i))},[i,a,t]);(0,l.useEffect)(()=>{let e=e=>{if(!c.current)return;e.preventDefault();let t=h(e);u.current=t,d.current?.(t)},t=()=>{c.current&&=(p.current?.(u.current),!1)};return window.addEventListener(`mousemove`,e),window.addEventListener(`mouseup`,t),window.addEventListener(`touchmove`,e,{passive:!1}),window.addEventListener(`touchend`,t),()=>{window.removeEventListener(`mousemove`,e),window.removeEventListener(`mouseup`,t),window.removeEventListener(`touchmove`,e),window.removeEventListener(`touchend`,t)}},[h]);let g=e=>{e.preventDefault();let t=h(e);u.current=t,c.current=!0,d.current?.(t)},_=e=>n=>{n.preventDefault(),n.stopPropagation();let r=Math.max(i,Math.min(a,t+e));d.current?.(r),p.current?.(r)};return(0,f.jsxs)(`div`,{className:`param-row`,children:[(0,f.jsxs)(`div`,{className:`param-header`,children:[(0,f.jsx)(`span`,{className:`param-label`,style:{color:o},children:e}),(0,f.jsxs)(`div`,{style:{display:`flex`,gap:4,alignItems:`center`},children:[(0,f.jsx)(`button`,{className:`param-btn`,onMouseDown:_(-5),onTouchStart:_(-5),children:`−`}),(0,f.jsx)(`span`,{className:`param-val`,style:{color:o},children:t}),(0,f.jsx)(`button`,{className:`param-btn`,onMouseDown:_(5),onTouchStart:_(5),children:`+`})]})]}),(0,f.jsx)(`div`,{className:`param-track-wrap`,ref:s,onMouseDown:g,onTouchStart:g,children:(0,f.jsxs)(`div`,{className:`param-track`,children:[(0,f.jsx)(`div`,{className:`param-fill`,style:{width:`${m}%`,background:o}}),(0,f.jsx)(`div`,{className:`param-thumb`,style:{left:`${m}%`,background:o}})]})})]})}function yt({def:e,value:t,color:n,onChange:r}){return(0,f.jsxs)(`div`,{className:`param-row`,children:[(0,f.jsx)(`div`,{className:`param-header`,children:(0,f.jsx)(`span`,{className:`param-label`,style:{color:n},children:e.label})}),(0,f.jsx)(`div`,{style:{display:`flex`,gap:4,flexWrap:`wrap`,background:`#0A0A10`,borderRadius:6,padding:4},children:(e.options||[]).map(e=>(0,f.jsx)(`button`,{onClick:()=>r?.(e.value),style:{flex:1,padding:`5px 8px`,borderRadius:4,border:`none`,fontSize:10,fontWeight:600,cursor:`pointer`,background:t===e.value?n:`transparent`,color:t===e.value?`#000`:`#8888A0`,transition:`all .1s`,letterSpacing:`.03em`,whiteSpace:`nowrap`},children:e.label},e.value))})]})}function bt({block:e,params:t,onParamChange:n,onParamChangeEnd:r,viewMode:i,onToggleView:a,onFocus:o,onModelBrowser:s}){let c=ft(e.type),l=pt(e.type),u=c.filter(e=>!e.conditional||e.conditional(t));return(0,f.jsxs)(`div`,{className:`param-panel`,children:[(0,f.jsxs)(`div`,{className:`param-panel-header`,children:[(0,f.jsxs)(`span`,{className:`param-panel-title`,style:{cursor:s?`pointer`:`default`},onClick:()=>s?.(e.id),children:[(0,f.jsx)(`span`,{style:{color:l},children:`●`}),` `,e.name||e.type||``,s&&(0,f.jsx)(`span`,{style:{marginLeft:4,fontSize:8,color:z.textDim},children:`▼`})]}),(0,f.jsxs)(`div`,{style:{display:`flex`,gap:4,alignItems:`center`},children:[o&&(0,f.jsx)(`button`,{className:`btn`,style:{padding:`4px 8px`,fontSize:9,fontWeight:600,background:`${z.amber}22`,border:`1px solid ${z.amber}44`,color:z.amber,borderRadius:5,letterSpacing:`.04em`,cursor:`pointer`},onClick:o,children:`🔍 Focus`}),(0,f.jsx)(`button`,{className:`btn-icon`,style:{width:26,height:26,fontSize:11,flexShrink:0},onClick:a,children:i===`slider`?`◉`:`▦`})]})]}),i===`slider`?u.map(e=>e.type===`select`?(0,f.jsx)(yt,{def:e,value:t[e.key]==null?e.def:t[e.key],color:l,onChange:t=>{n?.(e.key,t),r?.(e.key,t)}},e.key):(0,f.jsx)(vt,{label:e.label,value:t[e.key]==null?e.def:t[e.key],min:e.min,max:e.max,color:l,onChange:t=>n?.(e.key,t),onChangeEnd:t=>r?.(e.key,t)},e.key)):u.filter(e=>e.type!==`select`).length>0&&(0,f.jsx)(`div`,{className:`param-knobs`,children:u.filter(e=>e.type!==`select`).map(e=>(0,f.jsx)(_t,{label:e.label,value:t[e.key]==null?e.def:t[e.key],min:e.min,max:e.max,size:48,color:l,onChange:t=>{n?.(e.key,t),r?.(e.key,t)}},e.key))})]})}function xt({level:e=0,height:t=24,vertical:n=!1,width:r}){let i=Math.round(e/100*12),a=e=>e>=10?z.red:e>=8?`#E8C030`:z.green;return n?(0,f.jsx)(`div`,{style:{display:`flex`,flexDirection:`column-reverse`,gap:1,height:t,alignItems:`center`},children:Array.from({length:12}).map((e,n)=>(0,f.jsx)(`div`,{style:{width:4,height:(t-12)/12,borderRadius:1,background:n(0,f.jsx)(`div`,{style:{flex:1,height:`100%`,borderRadius:1,background:t{R.listModels().then(e=>c(e.models||[])).catch(()=>{})},[]);let p=async()=>{if(r.trim()){d(!0);try{o((t===`models`?await R.searchModels(r):await R.searchIrs(r)).results||[])}catch{o([])}d(!1)}},m=async e=>{try{t===`models`?await R.installModel(e.download_url,e.name):await R.installIr(e.download_url,e.name),R.listModels().then(e=>c(e.models||[])).catch(()=>{})}catch(e){alert(`Install failed: `+e.message)}};return(0,f.jsxs)(`div`,{style:{display:`flex`,flexDirection:`column`,height:`100%`,background:z.bg},children:[(0,f.jsxs)(`div`,{className:`screen-header`,children:[(0,f.jsx)(`div`,{className:`screen-title`,children:`Downloads`}),(0,f.jsx)(`button`,{className:`btn-icon`,style:{width:28,height:28,fontSize:13},onClick:e,children:`✕`})]}),(0,f.jsx)(`div`,{style:{padding:`6px 12px`,borderBottom:`1px solid ${z.border}`},children:(0,f.jsxs)(`div`,{className:`toggle`,children:[(0,f.jsx)(`button`,{className:`toggle-btn ${t===`models`?`active`:``}`,onClick:()=>n(`models`),children:`NAM Models`}),(0,f.jsx)(`button`,{className:`toggle-btn ${t===`irs`?`active`:``}`,onClick:()=>n(`irs`),children:`IR Cabs`})]})}),(0,f.jsxs)(`div`,{style:{padding:`6px 12px`,display:`flex`,gap:6,borderBottom:`1px solid ${z.border}`},children:[(0,f.jsxs)(`div`,{style:{flex:1,display:`flex`,background:z.surface,borderRadius:6,border:`1px solid ${z.border}`,padding:`6px 10px`,gap:6,alignItems:`center`},children:[(0,f.jsx)(`span`,{style:{color:z.textDim,fontSize:11},children:`🔍`}),(0,f.jsx)(`input`,{placeholder:`Search ${t===`models`?`NAM models`:`IR cabs`}...`,value:r,onChange:e=>i(e.target.value),onKeyDown:e=>e.key===`Enter`&&p(),style:{flex:1,background:`none`,border:`none`,color:z.textPrimary,fontSize:12,outline:`none`}})]}),(0,f.jsx)(`button`,{className:`btn btn-primary`,style:{padding:`6px 11px`,fontSize:11},onClick:p,children:u?`...`:`Search`})]}),(0,f.jsxs)(`div`,{className:`screen-body`,children:[a===null&&s.length>0&&(0,f.jsxs)(f.Fragment,{children:[(0,f.jsx)(`div`,{className:`section-label`,children:`Installed Models`}),s.map((e,t)=>(0,f.jsxs)(`div`,{className:`preset-row`,style:{cursor:`default`},children:[(0,f.jsx)(`div`,{style:{width:26,height:26,borderRadius:5,display:`flex`,alignItems:`center`,justifyContent:`center`,fontSize:11,background:e.loaded?`${z.green}22`:z.surface,color:e.loaded?z.green:z.textDim},children:`🎛`}),(0,f.jsxs)(`div`,{style:{flex:1},children:[(0,f.jsx)(`div`,{style:{fontSize:12,fontWeight:500},children:e.name}),(0,f.jsxs)(`div`,{className:`mono`,style:{fontSize:10,color:z.textDim},children:[e.architecture,`·`,e.size_mb,`MB`,e.loaded?`· CURRENT`:``]})]})]},t))]}),a!==null&&a.length===0&&(0,f.jsx)(`div`,{className:`card-sm`,style:{textAlign:`center`,color:z.textDim,padding:20},children:`No results found`}),a!==null&&a.map((e,t)=>(0,f.jsxs)(`div`,{className:`preset-row`,style:{cursor:`default`},children:[(0,f.jsxs)(`div`,{style:{flex:1},children:[(0,f.jsx)(`div`,{style:{fontSize:12,fontWeight:500},children:e.name}),(0,f.jsxs)(`div`,{className:`mono`,style:{fontSize:10,color:z.textDim},children:[e.author,`·`,e.size_display]})]}),(0,f.jsx)(`button`,{className:`btn btn-ghost`,style:{padding:`4px 9px`,fontSize:11},onClick:()=>m(e),children:`Get`})]},t))]})]})}var Ct=[{id:`gate`,type:`gate`,name:`Noise Gate`,bypassed:!1,params:{threshold:40,release:25}},{id:`comp`,type:`compressor`,name:`Compressor`,bypassed:!1,params:{threshold:50,ratio:4,gain:50}},{id:`amp`,type:`nam`,name:`Twin Reverb`,bypassed:!1,params:{level:75}},{id:`cab`,type:`ir`,name:`2x12 American`,bypassed:!1,params:{level:75}},{id:`eq`,type:`eq`,name:`EQ`,bypassed:!1,params:{low:0,mid:0,high:0,level:70}},{id:`reverb`,type:`reverb`,name:`Hall Reverb`,bypassed:!1,params:{subtype:`hall`,decay:50,mix:30,tone:50}}],wt=[{num:1,bank:0,name:`Clean`}];function Tt(){let[e,t]=(0,l.useState)(`main`),[n,r]=(0,l.useState)({connected:!1,master_volume:.85,routing_mode:`mono`,cpu_percent:0}),[i,a]=(0,l.useState)(Ct),[o,s]=(0,l.useState)(!0),[c,u]=(0,l.useState)(`gate`),[d,p]=(0,l.useState)({}),[m,h]=(0,l.useState)(`slider`),[_,v]=(0,l.useState)(0),[y,x]=(0,l.useState)(0),[S,C]=(0,l.useState)(1),[w,ee]=(0,l.useState)(0),[T,te]=(0,l.useState)(wt),[ne,re]=(0,l.useState)(!1),[ie,ae]=(0,l.useState)(!1),[oe,se]=(0,l.useState)(null),[ce,le]=(0,l.useState)(null),[E,O]=(0,l.useState)(`stomp`),[ue,de]=(0,l.useState)(()=>{try{let e=localStorage.getItem(`pedal_channel`);return e===`guitar`||e===`bass`?e:`guitar`}catch{return`guitar`}}),k=g(),A=(0,l.useCallback)(async()=>{try{let e=await R.listPresets();if(e?.banks){let t=[];e.banks.forEach(e=>{(e.presets||[]).forEach((n,r)=>{n&&t.push({num:r+1,bank:e.number,name:n.name,chain:n.chain})})}),t.length>0?te(t):te([])}else te([])}catch{te(wt)}},[]),pe=(0,l.useCallback)(e=>{e!==ue&&(de(e),ee(0),C(1),u(null))},[ue]),[me,j]=(0,l.useState)([]),[ge,_e]=(0,l.useState)([]),ve=(0,l.useRef)({blocks:Ct,blockParams:{},selectedBlockId:null}),ye=(0,l.useRef)(null);(0,l.useEffect)(()=>{ve.current={blocks:i,blockParams:d,selectedBlockId:c}},[i,d,c]);let be=(0,l.useCallback)(()=>{let e=ve.current,t={blocks:e.blocks.map(e=>({...e,params:{...e.params}})),blockParams:Object.fromEntries(Object.entries(e.blockParams).map(([e,t])=>[e,{...t}])),selectedBlockId:e.selectedBlockId};j(e=>[...e.slice(-49),t]),_e([])},[]),xe=(0,l.useCallback)(()=>{ye.current||=(be(),setTimeout(()=>{ye.current=null},400))},[be]),Se=(0,l.useCallback)(()=>{j(e=>{if(e.length===0)return e;let t=ve.current,n={blocks:t.blocks.map(e=>({...e,params:{...e.params}})),blockParams:Object.fromEntries(Object.entries(t.blockParams).map(([e,t])=>[e,{...t}])),selectedBlockId:t.selectedBlockId};_e(e=>[...e,n]);let r=e[e.length-1];return a(r.blocks),p(r.blockParams),u(r.selectedBlockId),e.slice(0,-1)})},[]),Ce=(0,l.useCallback)(()=>{_e(e=>{if(e.length===0)return e;let t=ve.current,n={blocks:t.blocks.map(e=>({...e,params:{...e.params}})),blockParams:Object.fromEntries(Object.entries(t.blockParams).map(([e,t])=>[e,{...t}])),selectedBlockId:t.selectedBlockId};j(e=>[...e,n]);let r=e[e.length-1];return a(r.blocks),p(r.blockParams),u(r.selectedBlockId),e.slice(0,-1)})},[]),[we,M]=(0,l.useState)(!1),[Ee,De]=(0,l.useState)(null),Oe=(0,l.useCallback)(e=>{switch(M(!1),e){case`settings`:De(`settings`);break;case`eq`:De(`eq`);break;case`routing`:De(`routing`);break;case`wifi`:De(`wifi`);break;case`tuner`:r(e=>({...e,tuner_enabled:!0})),R.tunerToggle(!0).catch(()=>{});break;default:break}},[]),ke=(0,l.useRef)(!1),Ae=(0,l.useRef)(!1),je=(0,l.useCallback)(async()=>{try{let e=await R.getState();if(r(e),e.input_level!=null&&v(e.input_level),e.output_level!=null&&x(e.output_level),e.current_preset?.name&&!ke.current&&C(e.current_preset.program==null?S:e.current_preset.program+1),e.connected&&o){if(s(!1),e.current_preset?.bank!=null&&e.current_preset?.program!=null)try{let t=await R.getPreset(e.current_preset.bank,e.current_preset.program);if(t?.chain&&t.chain.length>0){a(t.chain.map((e,t)=>{let n={type:e.fx_type||`fx`,enabled:e.enabled!==!1,params:e.params||{},nam_model_path:e.nam_model_path,ir_file_path:e.ir_file_path};return{id:e.id||`block_${e.fx_type||`fx`}_${t}`,type:n.type,name:ht(n),bypassed:!e.enabled,enabled:n.enabled,params:n.params,nam_model_path:n.nam_model_path,ir_file_path:n.ir_file_path}}));return}}catch{}a(Ct)}}catch{}},[S,o]);(0,l.useEffect)(()=>{je();let e=setInterval(je,3e3);return()=>clearInterval(e)},[je]),(0,l.useEffect)(()=>{let e=setInterval(()=>{v(e=>n.input_level!=null&&n.input_level>0?n.input_level:Math.max(0,Math.min(100,e+(Math.random()-.45)*8))),x(e=>n.output_level!=null&&n.output_level>0?n.output_level:Math.max(0,Math.min(100,e+(Math.random()-.45)*8)))},150);return()=>clearInterval(e)},[n.input_level,n.output_level]),(0,l.useEffect)(()=>{try{localStorage.setItem(`pedal_channel`,ue)}catch{}ot(ue)},[ue]),(0,l.useEffect)(()=>{A()},[A,ue]);let Me=(0,l.useCallback)(async(e,t)=>{ke.current=!0,ae(!0);try{await R.activatePreset(e,t),await new Promise(e=>setTimeout(e,300));let n=await R.getState();r(n),n.current_preset?.name&&C(n.current_preset.program==null?t+1:n.current_preset.program+1);try{let n=await R.getPreset(e,t);n?.chain&&n.chain.length>0&&(a(n.chain.map((e,t)=>{let n={type:e.fx_type||`fx`,enabled:e.enabled!==!1,params:e.params||{},nam_model_path:e.nam_model_path,ir_file_path:e.ir_file_path};return{id:e.id||`block_${e.fx_type||`fx`}_${t}`,type:n.type,name:ht(n),bypassed:!e.enabled,enabled:n.enabled,params:n.params,nam_model_path:n.nam_model_path,ir_file_path:n.ir_file_path}})),Ae.current=!0)}catch(e){console.warn(`Could not fetch chain:`,e)}u(null)}catch(e){console.warn(`Preset activate failed:`,e)}ae(!1),setTimeout(()=>{ke.current=!1},5e3),setTimeout(()=>{Ae.current=!1},5e3)},[]),Ne=(0,l.useCallback)(async e=>{let t=T.find(t=>t.num===e);t&&(C(e),await Me(t.bank==null?1:t.bank,e-1))},[T,Me]),Pe=(0,l.useMemo)(()=>i.find(e=>e.id===c),[i,c]);(0,l.useEffect)(()=>{if(Pe&&d[c]==null){let e=ft(Pe.type),t={};e.forEach(e=>{t[e.key]=Pe.params?.[e.key]==null?e.def:Pe.params[e.key]}),p(e=>({...e,[c]:t}))}},[c,Pe,d]);let Fe=(0,l.useCallback)(e=>{u(t=>t===e?null:e)},[]),Le=(0,l.useCallback)((e,t,n)=>{xe(),p(r=>({...r,[e]:{...r[e]||{},[t]:n}}))},[xe]),Re=(0,l.useCallback)(e=>{be(),Ae.current=!0,a(t=>t.map(t=>t.id===e?{...t,bypassed:!t.bypassed}:t)),R.toggleBlock(e,!1).catch(()=>{}),setTimeout(()=>{Ae.current=!1},3e3)},[be]),ze=(0,l.useCallback)((e,t,n)=>{R.updateBlock(e,{[t]:n}).catch(()=>{})},[]),N=(0,l.useCallback)((e,t)=>{be(),a(n=>{let r=[...n],[i]=r.splice(e,1);return r.splice(t,0,i),r})},[be]),Ve=(0,l.useCallback)(()=>{be(),Ae.current=!0;let e=`block_`+Date.now(),t=`overdrive`,n=ft(t),r={};n.forEach(e=>{r[e.key]=e.def}),a(n=>[...n,{id:e,type:t,name:ht({type:t,params:r}),bypassed:!1,params:r}]),setTimeout(()=>{Ae.current=!1},3e3)},[be]),He=(0,l.useCallback)(e=>{be(),Ae.current=!0,a(t=>t.filter(t=>t.id!==e)),c===e&&u(null),setTimeout(()=>{Ae.current=!1},3e3)},[be,c]),Ue=(0,l.useMemo)(()=>T.filter(e=>e.bank===w),[T,w]),We=w+1,Ge=(0,l.useCallback)(()=>{let e=T.reduce((e,t)=>Math.max(e,t.bank),0),t=Math.min(w+1,e);ee(t);let n=T.filter(e=>e.bank===t);n.length>0&&Me(n[0].bank,n[0].num-1)},[w,T,Me]),P=(0,l.useCallback)(()=>{let e=Math.max(w-1,0);ee(e);let t=T.filter(t=>t.bank===e);t.length>0&&Me(t[0].bank,t[0].num-1)},[w,T,Me]),qe=(0,l.useCallback)(()=>{re(e=>!e),R.bypassToggle().catch(()=>{})},[]),Je=(0,l.useCallback)(()=>{let e=!n.tuner_enabled;r(t=>({...t,tuner_enabled:e})),R.tunerToggle(e).catch(()=>{}),e&&t(`main`)},[n.tuner_enabled]),Ye=(0,l.useCallback)(()=>{Ae.current=!0;let e=oe;e&&(a(t=>t.map(t=>t.id===e?{...t,bypassed:!t.bypassed}:t)),R.toggleBlock(e,!1).catch(()=>{}),setTimeout(()=>{Ae.current=!1},3e3))},[oe]),F=(0,l.useCallback)(e=>{le(e),oe&&se(null)},[oe]),Ze=(0,l.useCallback)(({type:e,name:t,modelPath:n,isBuiltin:r})=>{let i=ce;i&&(be(),Ae.current=!0,a(a=>a.map(a=>{if(a.id!==i)return a;let o=ft(e),s={};return o.forEach(e=>{s[e.key]=e.def}),{...a,type:e,name:t||ht({type:e}),params:s,nam_model_path:!r&&n&&e===`nam`?n:void 0,ir_file_path:!r&&n&&e===`ir`?n:void 0}})),le(null),p(e=>{let t={...e};return delete t[i],t}),setTimeout(()=>{Ae.current=!1},3e3))},[ce,be]),Qe=(0,l.useCallback)((e,t)=>{if(!i.find(e=>e.id===t))return;let n=d[t]?.maxTime??8;switch(e){case`record`:k.record(t,n);break;case`play`:k.play(t,n);break;case`overdub`:k.overdub(t,n);break;case`stop`:k.stop(t);break;case`clear`:k.clearLoop(t);break;case`undo`:k.undoLast(t);break}},[i,d,k]),$e=(0,l.useCallback)(e=>({onRecord:()=>Qe(`record`,e),onPlay:()=>Qe(`play`,e),onOverdub:()=>Qe(`overdub`,e),onStop:()=>Qe(`stop`,e),onClear:()=>Qe(`clear`,e),onUndo:()=>Qe(`undo`,e)}),[Qe]);return(0,f.jsxs)(f.Fragment,{children:[(0,f.jsx)(`style`,{children:gt}),(0,f.jsxs)(`div`,{style:{width:`100%`,height:`100vh`,display:`flex`,flexDirection:`column`,background:z.bg,overflow:`hidden`,fontFamily:`'Inter',sans-serif`,margin:`0 auto`,maxWidth:980},children:[e===`main`&&(0,f.jsxs)(f.Fragment,{children:[(0,f.jsxs)(`div`,{style:{display:`flex`,justifyContent:`space-between`,alignItems:`center`,padding:`4px 12px`,background:z.panel,borderBottom:`1px solid ${z.border}`,flexShrink:0,minHeight:32},children:[(0,f.jsxs)(`div`,{style:{display:`flex`,gap:8,alignItems:`center`},children:[(0,f.jsx)(`div`,{style:{width:6,height:6,borderRadius:`50%`,background:n.connected?z.green:z.red,boxShadow:`0 0 6px ${n.connected?z.green:z.red}`}}),(0,f.jsx)(`span`,{className:`mono`,style:{fontSize:9,color:z.textSec},children:n.connected?`CONNECTED`:`OFFLINE`}),(0,f.jsx)(`button`,{className:`btn-icon`,style:{width:20,height:20,fontSize:11,opacity:me.length>0?1:.3},onClick:Se,disabled:me.length===0,title:`Undo (Ctrl+Z)`,children:`↶`}),(0,f.jsx)(`button`,{className:`btn-icon`,style:{width:20,height:20,fontSize:11,opacity:ge.length>0?1:.3},onClick:Ce,disabled:ge.length===0,title:`Redo (Ctrl+Shift+Z)`,children:`↷`}),(0,f.jsx)(`div`,{style:{width:1,height:12,background:z.border}}),(0,f.jsx)(`span`,{className:`mono`,style:{fontSize:10,color:z.textPrimary,fontWeight:600},children:n.current_preset?.name||T.find(e=>e.num===S)?.name||`—`}),ie&&(0,f.jsx)(`span`,{className:`mono`,style:{fontSize:8,color:z.amber},children:`⟳`}),(0,f.jsx)(`div`,{style:{width:1,height:12,background:z.border}}),n.channel_mode===`stereo`?(0,f.jsx)(`span`,{className:`badge badge-amber`,style:{fontSize:9,padding:`2px 7px`,letterSpacing:`.08em`},children:`STEREO`}):(0,f.jsx)(`div`,{style:{display:`flex`,alignItems:`center`,gap:3,background:z.surface,borderRadius:5,border:`1px solid ${z.border}`,padding:2,height:22},children:[`guitar`,`bass`].map(e=>(0,f.jsxs)(`button`,{onClick:()=>pe(e),style:{padding:`1px 7px`,borderRadius:4,border:`none`,height:18,fontSize:8,fontWeight:700,letterSpacing:`.08em`,textTransform:`uppercase`,background:ue===e?z.amber:`transparent`,color:ue===e?`#000`:z.textSec,cursor:`pointer`,display:`flex`,alignItems:`center`,gap:3,lineHeight:1,transition:`all .1s`,whiteSpace:`nowrap`},children:[(0,f.jsx)(`span`,{style:{fontSize:10,opacity:ue===e?1:.5},children:`🎸`}),e===`guitar`?`GUITAR`:`BASS`]},e))})]}),(0,f.jsxs)(`div`,{style:{display:`flex`,gap:8,alignItems:`center`},children:[(0,f.jsx)(`div`,{style:{fontSize:9,color:z.textDim,fontWeight:600,letterSpacing:`.03em`},children:`IN`}),(0,f.jsx)(xt,{level:_,height:20,width:60}),(0,f.jsx)(`div`,{style:{fontSize:9,color:z.textDim,fontWeight:600,letterSpacing:`.03em`},children:`OUT`}),(0,f.jsx)(xt,{level:y,height:20,width:60}),(0,f.jsxs)(`span`,{className:`mono`,style:{fontSize:9,color:z.textDim},children:[`CPU `,n.cpu_percent==null?`—`:`${n.cpu_percent}%`]}),(0,f.jsx)(`div`,{style:{width:1,height:12,background:z.border}}),(0,f.jsx)(`button`,{className:`btn-icon`,style:{width:24,height:24,position:`relative`},onClick:()=>t(`captures`),title:`Downloads`,children:(0,f.jsx)(`img`,{src:`/ui/img/ic_bank.svg`,alt:`captures`,style:{width:14,height:14,filter:`invert(1) brightness(2)`}})}),(0,f.jsx)(`button`,{className:`btn-icon`,style:{width:24,height:24,position:`relative`},onClick:()=>t(`presets`),title:`Presets`,children:(0,f.jsx)(`img`,{src:`/ui/img/ic_presets.svg`,alt:`presets`,style:{width:14,height:14,filter:`invert(1) brightness(2)`}})}),(0,f.jsx)(`div`,{style:{display:`flex`,gap:2,alignItems:`center`,background:z.surface,borderRadius:4,border:`1px solid ${z.border}`,padding:2,height:24},children:[`stomp`,`preset`].map(e=>(0,f.jsx)(`button`,{onClick:()=>O(e),style:{padding:`1px 5px`,borderRadius:3,border:`none`,height:20,fontSize:8,fontWeight:700,letterSpacing:`.04em`,textTransform:`uppercase`,background:E===e?z.amber:`transparent`,color:E===e?`#000`:z.textDim,cursor:`pointer`,lineHeight:1.5,minWidth:34,display:`flex`,alignItems:`center`,justifyContent:`center`},children:e===`stomp`?`SM`:`PR`},e))}),(0,f.jsx)(`button`,{className:`btn-icon`,style:{width:24,height:24,position:`relative`,background:n.tuner_enabled?`rgba(128,208,160,.15)`:z.surface,borderColor:n.tuner_enabled?z.green:z.border},onClick:Je,title:n.tuner_enabled?`Exit Tuner`:`Tuner`,children:(0,f.jsx)(`img`,{src:`/ui/img/fx_analyzer.svg`,alt:`tuner`,style:{width:14,height:14,filter:n.tuner_enabled?`invert(0.9) brightness(2) sepia(0.4) hue-rotate(80deg)`:`invert(1) brightness(2)`}})}),(0,f.jsx)(`button`,{className:`btn-icon`,style:{width:24,height:24,position:`relative`,background:`rgba(232,160,48,.12)`,borderColor:z.amber},onClick:()=>{document.fullscreenElement?document.exitFullscreen():document.documentElement.requestFullscreen()},title:`Fullscreen`,children:(0,f.jsx)(`svg`,{viewBox:`0 0 24 24`,width:`14`,height:`14`,fill:`#E8A030`,children:(0,f.jsx)(`path`,{d:`M7 14H5v5h5v-2H7v-3zm-2-4h2V7h3V5H5v5zm12 7h-3v2h5v-5h-2v3zM14 5v2h3v3h2V5h-5z`})})}),(0,f.jsx)(`div`,{style:{width:1,height:16,background:z.border}}),(0,f.jsx)(`button`,{className:`btn-icon`,style:{width:24,height:24,position:`relative`},onClick:()=>M(!0),title:`Main Menu`,children:(0,f.jsx)(`span`,{style:{fontSize:14,color:z.textSec,lineHeight:1},children:`☰`})})]})]}),(0,f.jsxs)(`div`,{style:{flex:1,display:`flex`,overflow:`hidden`},children:[(0,f.jsxs)(`div`,{style:{width:90,flexShrink:0,background:z.surface,borderRight:`1px solid ${z.border}`,display:`flex`,flexDirection:`column`,overflow:`hidden`},children:[(0,f.jsxs)(`div`,{style:{padding:`6px 8px`,textAlign:`center`,borderBottom:`1px solid ${z.border}`,background:z.panel},children:[(0,f.jsxs)(`div`,{className:`mono`,style:{fontSize:14,fontWeight:700,color:z.amber},children:[`Bank `,We]}),(0,f.jsx)(`div`,{style:{fontSize:9,color:z.textDim,letterSpacing:`.06em`,textTransform:`uppercase`},children:`Programs`})]}),(0,f.jsx)(`div`,{style:{flex:1,overflowY:`auto`,padding:`4px 6px`,display:`flex`,flexDirection:`column`,gap:3},children:Ue.map(e=>(0,f.jsxs)(`div`,{className:`preset-chip-s ${e.num===S?`active`:``}`,onClick:()=>Ne(e.num),children:[(0,f.jsxs)(`div`,{className:`pcs-num`,children:[`0`,e.num]}),(0,f.jsx)(`div`,{className:`pcs-name`,children:e.name}),(0,f.jsx)(`div`,{className:`pcs-indicator`,style:{background:e.num===S?z.amber:z.border}})]},e.num))})]}),(0,f.jsxs)(`div`,{style:{flex:1,display:`flex`,flexDirection:`column`,overflow:`hidden`,minWidth:0},children:[(0,f.jsx)(`div`,{style:{display:`flex`,gap:4,padding:`3px 8px`,background:z.surface,borderBottom:`1px solid ${z.border}`,flexShrink:0},children:[{label:`Preset`,value:T.find(e=>e.num===S)?.name||`—`},{label:`Routing`,value:(n.routing_mode||`MONO`).toUpperCase()},{label:`Volume`,value:n.master_volume==null?`—`:`${Math.round(n.master_volume*100)}%`},{label:`Bank`,value:We}].map(e=>(0,f.jsxs)(`div`,{style:{flex:1,textAlign:`center`,padding:`2px 6px`,borderRadius:3,background:z.panel,border:`1px solid ${z.border}`},children:[(0,f.jsx)(`div`,{style:{fontSize:7,color:z.textDim,letterSpacing:`.06em`,textTransform:`uppercase`},children:e.label}),(0,f.jsx)(`div`,{className:`mono`,style:{fontSize:10,fontWeight:600,color:z.amber,marginTop:1},children:e.value})]},e.label))}),(0,f.jsx)(`div`,{style:{flex:Pe?`0 0 auto`:1,overflow:`hidden`},children:(0,f.jsx)(D,{blocks:i,selectedBlockId:c,onSelectBlock:Fe,onToggleBlock:Re,onReorder:N,onAddBlock:Ve,onRemoveBlock:He,onModelBrowser:F,looperStores:k.stores})}),Pe&&d[c]?(Pe.type||``).toLowerCase()===`looper`?(0,f.jsx)(b,{block:Pe,params:d[c]||{},looperStore:k.getStore(c),onParamChange:(e,t)=>Le(c,e,t),onParamChangeEnd:(e,t)=>ze(c,e,t),looperActions:$e(c)}):(0,f.jsx)(bt,{block:Pe,params:d[c]||{},viewMode:m,onToggleView:()=>h(e=>e===`slider`?`knob`:`slider`),onParamChange:(e,t)=>Le(c,e,t),onParamChangeEnd:(e,t)=>ze(c,e,t),onFocus:()=>se(c),onModelBrowser:F}):(0,f.jsx)(`div`,{style:{flex:1,display:`flex`,alignItems:`center`,justifyContent:`center`,background:z.bg,color:z.textDim,fontSize:11},children:`Tap a block to edit parameters`})]})]}),(()=>{let e=i.find(e=>e.id===c),t=e&&(e.type||``).toLowerCase()===`looper`;return(0,f.jsx)(fe,{mode:E,blocks:i,selectedBlockId:c,currentBank:We,currentPreset:S,presets:Ue,onSelectBlock:Fe,onToggleBlock:Re,onBankUp:Ge,onBankDown:P,onGlobalBypass:qe,globalBypass:ne,onTunerToggle:Je,tunerEnabled:n.tuner_enabled,onPresetClick:e=>{let t=Ue[parseInt(e)];t&&Me(t.bank,t.num-1)},looperActive:t,looperTransport:t?$e(c):void 0})})()]}),oe&&i.find(e=>e.id===oe)&&d[oe]&&(0,f.jsx)(Te,{block:i.find(e=>e.id===oe),params:d[oe]||{},viewMode:m,onClose:()=>se(null),onParamChange:(e,t)=>Le(oe,e,t),onParamChangeEnd:(e,t)=>ze(oe,e,t),onToggleView:()=>h(e=>e===`slider`?`knob`:`slider`),onToggleBypass:Ye,onOpenModelBrowser:()=>F(oe)}),e===`captures`&&(0,f.jsx)(St,{onClose:()=>t(`main`)}),e===`presets`&&(0,f.jsxs)(`div`,{style:{display:`flex`,flexDirection:`column`,height:`100%`,background:z.bg},children:[(0,f.jsxs)(`div`,{className:`screen-header`,children:[(0,f.jsx)(`div`,{className:`screen-title`,children:`Presets`}),(0,f.jsx)(`button`,{className:`btn-icon`,style:{width:28,height:28,fontSize:13},onClick:()=>t(`main`),children:`✕`})]}),(0,f.jsx)(`div`,{className:`screen-body`,children:T.map((e,n)=>(0,f.jsxs)(`div`,{className:`preset-row ${e.num===S?`active`:``}`,onClick:()=>{Ne(e.num),t(`main`)},children:[(0,f.jsx)(`div`,{className:`mono`,style:{width:26,height:26,borderRadius:5,display:`flex`,alignItems:`center`,justifyContent:`center`,fontSize:11,fontWeight:700,background:e.num===S?`${z.amber}22`:z.surface,color:e.num===S?z.amber:z.textDim,border:`1px solid ${e.num===S?z.amber+`60`:z.border}`},children:e.num}),(0,f.jsxs)(`div`,{style:{flex:1},children:[(0,f.jsx)(`div`,{style:{fontSize:12,fontWeight:600},children:e.name}),(0,f.jsxs)(`div`,{className:`mono`,style:{fontSize:9,color:z.textDim},children:[`Bank `,e.bank==null?1:e.bank,`·Program `,e.num-1]})]}),e.num===S&&(0,f.jsx)(`span`,{style:{color:z.amber,fontSize:16},children:`●`})]},n))})]}),n.tuner_enabled&&(0,f.jsx)(he,{tunerData:n,connected:n.connected,onExit:()=>{r(e=>({...e,tuner_enabled:!1})),R.tunerToggle(!1).catch(()=>{})}}),ce&&(()=>{let e=i.find(e=>e.id===ce);return e?(0,f.jsx)(Ie,{currentBlockType:e.type,currentBlockName:e.name,currentModelPath:e.nam_model_path||e.ir_file_path,onSelectModel:Ze,onClose:()=>le(null)}):null})(),we&&(0,f.jsx)(Be,{onClose:()=>M(!1),onItemSelect:Oe}),Ee===`settings`&&(0,f.jsx)(Ke,{onClose:()=>De(null),onMasterVolume:e=>R.masterVolume(e).catch(()=>{})}),Ee===`eq`&&(0,f.jsx)(Xe,{onClose:()=>De(null)}),Ee===`routing`&&(0,f.jsx)(et,{onClose:()=>De(null)}),Ee===`wifi`&&(0,f.jsx)(rt,{onClose:()=>De(null)})]})]})}(0,u.createRoot)(document.getElementById(`root`)).render((0,f.jsx)(l.StrictMode,{children:(0,f.jsx)(Tt,{})})); \ No newline at end of file +`+x;function _t({label:e,value:t=50,onChange:n,size:r=48,color:i=z.amber,min:a=0,max:o=100}){let s=(0,l.useRef)(null),c=(t-a)/(o-a),u=-140+c*280,d=r*.38,p=r*.5-d,m=e=>{e.preventDefault(),s.current={y:e.touches?e.touches[0].clientY:e.clientY,val:t};let r=e=>{let t=e.touches?e.touches[0].clientY:e.clientY,r=(s.current.y-t)/120,i=Math.max(a,Math.min(o,s.current.val+r*(o-a)));n?.(Math.round(i))},i=()=>{window.removeEventListener(`mousemove`,r),window.removeEventListener(`mouseup`,i),window.removeEventListener(`touchmove`,r),window.removeEventListener(`touchend`,i)};window.addEventListener(`mousemove`,r),window.addEventListener(`mouseup`,i),window.addEventListener(`touchmove`,r,{passive:!1}),window.addEventListener(`touchend`,i)},h=r+12,g=h/2,_=h/2,v=r/2+3,y=g+v*Math.cos(Math.PI/180*-230),b=_+v*Math.sin(Math.PI/180*-230),x=g+v*Math.cos((u-90)*(Math.PI/180)),S=_+v*Math.sin((u-90)*(Math.PI/180)),C=+(c*280>180);return(0,f.jsxs)(`div`,{className:`knob-wrap`,style:{width:r+12},children:[(0,f.jsxs)(`div`,{className:`knob`,style:{width:r,height:r},onMouseDown:m,onTouchStart:m,children:[(0,f.jsxs)(`svg`,{style:{position:`absolute`,inset:-6,width:h,height:h,overflow:`visible`},children:[(0,f.jsx)(`circle`,{cx:g,cy:_,r:v,fill:`none`,stroke:z.border,strokeWidth:`2`,strokeLinecap:`round`,opacity:`.5`}),c>0&&(0,f.jsx)(`path`,{d:`M ${y} ${b} A ${v} ${v} 0 ${C} 1 ${x} ${S}`,fill:`none`,stroke:i,strokeWidth:`2`,strokeLinecap:`round`,style:{filter:`drop-shadow(0 0 3px ${i})`}})]}),(0,f.jsx)(`div`,{className:`knob-inner`,style:{"--amber":i},children:(0,f.jsx)(`div`,{className:`knob-dot`,style:{height:d,top:p,transform:`translateX(-50%) rotate(${u}deg)`,transformOrigin:`50% ${d}px`}})})]}),(0,f.jsx)(`div`,{className:`knob-value mono`,style:{color:i},children:t}),(0,f.jsx)(`div`,{className:`knob-label`,children:e})]})}function vt({label:e,value:t=50,onChange:n,onChangeEnd:r,min:i=0,max:a=100,color:o=z.amber}){let s=(0,l.useRef)(null),c=(0,l.useRef)(!1),u=(0,l.useRef)(t),d=(0,l.useRef)(n),p=(0,l.useRef)(r),m=(t-i)/(a-i)*100;(0,l.useEffect)(()=>{d.current=n},[n]),(0,l.useEffect)(()=>{p.current=r},[r]);let h=(0,l.useCallback)(e=>{let n=s.current;if(!n)return t;let r=n.getBoundingClientRect(),o=(e.touches?e.touches[0].clientX:e.clientX)-r.left;return Math.round(i+Math.max(0,Math.min(1,o/r.width))*(a-i))},[i,a,t]);(0,l.useEffect)(()=>{let e=e=>{if(!c.current)return;e.preventDefault();let t=h(e);u.current=t,d.current?.(t)},t=()=>{c.current&&=(p.current?.(u.current),!1)};return window.addEventListener(`mousemove`,e),window.addEventListener(`mouseup`,t),window.addEventListener(`touchmove`,e,{passive:!1}),window.addEventListener(`touchend`,t),()=>{window.removeEventListener(`mousemove`,e),window.removeEventListener(`mouseup`,t),window.removeEventListener(`touchmove`,e),window.removeEventListener(`touchend`,t)}},[h]);let g=e=>{e.preventDefault();let t=h(e);u.current=t,c.current=!0,d.current?.(t)},_=e=>n=>{n.preventDefault(),n.stopPropagation();let r=Math.max(i,Math.min(a,t+e));d.current?.(r),p.current?.(r)};return(0,f.jsxs)(`div`,{className:`param-row`,children:[(0,f.jsxs)(`div`,{className:`param-header`,children:[(0,f.jsx)(`span`,{className:`param-label`,style:{color:o},children:e}),(0,f.jsxs)(`div`,{style:{display:`flex`,gap:4,alignItems:`center`},children:[(0,f.jsx)(`button`,{className:`param-btn`,onMouseDown:_(-5),onTouchStart:_(-5),children:`−`}),(0,f.jsx)(`span`,{className:`param-val`,style:{color:o},children:t}),(0,f.jsx)(`button`,{className:`param-btn`,onMouseDown:_(5),onTouchStart:_(5),children:`+`})]})]}),(0,f.jsx)(`div`,{className:`param-track-wrap`,ref:s,onMouseDown:g,onTouchStart:g,children:(0,f.jsxs)(`div`,{className:`param-track`,children:[(0,f.jsx)(`div`,{className:`param-fill`,style:{width:`${m}%`,background:o}}),(0,f.jsx)(`div`,{className:`param-thumb`,style:{left:`${m}%`,background:o}})]})})]})}function yt({def:e,value:t,color:n,onChange:r}){return(0,f.jsxs)(`div`,{className:`param-row`,children:[(0,f.jsx)(`div`,{className:`param-header`,children:(0,f.jsx)(`span`,{className:`param-label`,style:{color:n},children:e.label})}),(0,f.jsx)(`div`,{style:{display:`flex`,gap:4,flexWrap:`wrap`,background:`#0A0A10`,borderRadius:6,padding:4},children:(e.options||[]).map(e=>(0,f.jsx)(`button`,{onClick:()=>r?.(e.value),style:{flex:1,padding:`5px 8px`,borderRadius:4,border:`none`,fontSize:10,fontWeight:600,cursor:`pointer`,background:t===e.value?n:`transparent`,color:t===e.value?`#000`:`#8888A0`,transition:`all .1s`,letterSpacing:`.03em`,whiteSpace:`nowrap`},children:e.label},e.value))})]})}function bt({block:e,params:t,onParamChange:n,onParamChangeEnd:r,viewMode:i,onToggleView:a,onFocus:o,onModelBrowser:s}){let c=ft(e.type),l=pt(e.type),u=c.filter(e=>!e.conditional||e.conditional(t));return(0,f.jsxs)(`div`,{className:`param-panel`,children:[(0,f.jsxs)(`div`,{className:`param-panel-header`,children:[(0,f.jsxs)(`span`,{className:`param-panel-title`,style:{cursor:s?`pointer`:`default`},onClick:()=>s?.(e.id),children:[(0,f.jsx)(`span`,{style:{color:l},children:`●`}),` `,e.name||e.type||``,s&&(0,f.jsx)(`span`,{style:{marginLeft:4,fontSize:8,color:z.textDim},children:`▼`})]}),(0,f.jsxs)(`div`,{style:{display:`flex`,gap:4,alignItems:`center`},children:[o&&(0,f.jsx)(`button`,{className:`btn`,style:{padding:`4px 8px`,fontSize:9,fontWeight:600,background:`${z.amber}22`,border:`1px solid ${z.amber}44`,color:z.amber,borderRadius:5,letterSpacing:`.04em`,cursor:`pointer`},onClick:o,children:`🔍 Focus`}),(0,f.jsx)(`button`,{className:`btn-icon`,style:{width:26,height:26,fontSize:11,flexShrink:0},onClick:a,children:i===`slider`?`◉`:`▦`})]})]}),i===`slider`?u.map(e=>e.type===`select`?(0,f.jsx)(yt,{def:e,value:t[e.key]==null?e.def:t[e.key],color:l,onChange:t=>{n?.(e.key,t),r?.(e.key,t)}},e.key):(0,f.jsx)(vt,{label:e.label,value:t[e.key]==null?e.def:t[e.key],min:e.min,max:e.max,color:l,onChange:t=>n?.(e.key,t),onChangeEnd:t=>r?.(e.key,t)},e.key)):u.filter(e=>e.type!==`select`).length>0&&(0,f.jsx)(`div`,{className:`param-knobs`,children:u.filter(e=>e.type!==`select`).map(e=>(0,f.jsx)(_t,{label:e.label,value:t[e.key]==null?e.def:t[e.key],min:e.min,max:e.max,size:48,color:l,onChange:t=>{n?.(e.key,t),r?.(e.key,t)}},e.key))})]})}function xt({level:e=0,height:t=24,vertical:n=!1,width:r}){let i=Math.round(e/100*12),a=e=>e>=10?z.red:e>=8?`#E8C030`:z.green;return n?(0,f.jsx)(`div`,{style:{display:`flex`,flexDirection:`column-reverse`,gap:1,height:t,alignItems:`center`},children:Array.from({length:12}).map((e,n)=>(0,f.jsx)(`div`,{style:{width:4,height:(t-12)/12,borderRadius:1,background:n(0,f.jsx)(`div`,{style:{flex:1,height:`100%`,borderRadius:1,background:t{R.listModels().then(e=>c(e.models||[])).catch(()=>{})},[]);let p=async()=>{if(r.trim()){d(!0);try{o((t===`models`?await R.searchModels(r):await R.searchIrs(r)).results||[])}catch{o([])}d(!1)}},m=async e=>{try{t===`models`?await R.installModel(e.download_url,e.name):await R.installIr(e.download_url,e.name),R.listModels().then(e=>c(e.models||[])).catch(()=>{})}catch(e){alert(`Install failed: `+e.message)}};return(0,f.jsxs)(`div`,{style:{display:`flex`,flexDirection:`column`,height:`100%`,background:z.bg},children:[(0,f.jsxs)(`div`,{className:`screen-header`,children:[(0,f.jsx)(`div`,{className:`screen-title`,children:`Downloads`}),(0,f.jsx)(`button`,{className:`btn-icon`,style:{width:28,height:28,fontSize:13},onClick:e,children:`✕`})]}),(0,f.jsx)(`div`,{style:{padding:`6px 12px`,borderBottom:`1px solid ${z.border}`},children:(0,f.jsxs)(`div`,{className:`toggle`,children:[(0,f.jsx)(`button`,{className:`toggle-btn ${t===`models`?`active`:``}`,onClick:()=>n(`models`),children:`NAM Models`}),(0,f.jsx)(`button`,{className:`toggle-btn ${t===`irs`?`active`:``}`,onClick:()=>n(`irs`),children:`IR Cabs`})]})}),(0,f.jsxs)(`div`,{style:{padding:`6px 12px`,display:`flex`,gap:6,borderBottom:`1px solid ${z.border}`},children:[(0,f.jsxs)(`div`,{style:{flex:1,display:`flex`,background:z.surface,borderRadius:6,border:`1px solid ${z.border}`,padding:`6px 10px`,gap:6,alignItems:`center`},children:[(0,f.jsx)(`span`,{style:{color:z.textDim,fontSize:11},children:`🔍`}),(0,f.jsx)(`input`,{placeholder:`Search ${t===`models`?`NAM models`:`IR cabs`}...`,value:r,onChange:e=>i(e.target.value),onKeyDown:e=>e.key===`Enter`&&p(),style:{flex:1,background:`none`,border:`none`,color:z.textPrimary,fontSize:12,outline:`none`}})]}),(0,f.jsx)(`button`,{className:`btn btn-primary`,style:{padding:`6px 11px`,fontSize:11},onClick:p,children:u?`...`:`Search`})]}),(0,f.jsxs)(`div`,{className:`screen-body`,children:[a===null&&s.length>0&&(0,f.jsxs)(f.Fragment,{children:[(0,f.jsx)(`div`,{className:`section-label`,children:`Installed Models`}),s.map((e,t)=>(0,f.jsxs)(`div`,{className:`preset-row`,style:{cursor:`default`},children:[(0,f.jsx)(`div`,{style:{width:26,height:26,borderRadius:5,display:`flex`,alignItems:`center`,justifyContent:`center`,fontSize:11,background:e.loaded?`${z.green}22`:z.surface,color:e.loaded?z.green:z.textDim},children:`🎛`}),(0,f.jsxs)(`div`,{style:{flex:1},children:[(0,f.jsx)(`div`,{style:{fontSize:12,fontWeight:500},children:e.name}),(0,f.jsxs)(`div`,{className:`mono`,style:{fontSize:10,color:z.textDim},children:[e.architecture,`·`,e.size_mb,`MB`,e.loaded?`· CURRENT`:``]})]})]},t))]}),a!==null&&a.length===0&&(0,f.jsx)(`div`,{className:`card-sm`,style:{textAlign:`center`,color:z.textDim,padding:20},children:`No results found`}),a!==null&&a.map((e,t)=>(0,f.jsxs)(`div`,{className:`preset-row`,style:{cursor:`default`},children:[(0,f.jsxs)(`div`,{style:{flex:1},children:[(0,f.jsx)(`div`,{style:{fontSize:12,fontWeight:500},children:e.name}),(0,f.jsxs)(`div`,{className:`mono`,style:{fontSize:10,color:z.textDim},children:[e.author,`·`,e.size_display]})]}),(0,f.jsx)(`button`,{className:`btn btn-ghost`,style:{padding:`4px 9px`,fontSize:11},onClick:()=>m(e),children:`Get`})]},t))]})]})}var Ct=[{id:`gate`,type:`gate`,name:`Noise Gate`,bypassed:!1,params:{threshold:40,release:25}},{id:`comp`,type:`compressor`,name:`Compressor`,bypassed:!1,params:{threshold:50,ratio:4,gain:50}},{id:`amp`,type:`nam`,name:`Twin Reverb`,bypassed:!1,params:{level:75}},{id:`cab`,type:`ir`,name:`2x12 American`,bypassed:!1,params:{level:75}},{id:`eq`,type:`eq`,name:`EQ`,bypassed:!1,params:{low:0,mid:0,high:0,level:70}},{id:`reverb`,type:`reverb`,name:`Hall Reverb`,bypassed:!1,params:{subtype:`hall`,decay:50,mix:30,tone:50}}],wt=[{num:1,bank:0,name:`Clean`}];function Tt(){let[e,t]=(0,l.useState)(`main`),[n,r]=(0,l.useState)({connected:!1,master_volume:.85,routing_mode:`mono`,cpu_percent:0}),[i,a]=(0,l.useState)(Ct),[o,s]=(0,l.useState)(!0),[c,u]=(0,l.useState)(`gate`),[d,p]=(0,l.useState)({}),[m,h]=(0,l.useState)(`slider`),[_,v]=(0,l.useState)(0),[y,x]=(0,l.useState)(0),[S,C]=(0,l.useState)(1),[w,ee]=(0,l.useState)(0),[T,te]=(0,l.useState)(wt),[ne,re]=(0,l.useState)(!1),[ie,ae]=(0,l.useState)(!1),[oe,se]=(0,l.useState)(null),[ce,le]=(0,l.useState)(null),[E,O]=(0,l.useState)(`stomp`),[ue,de]=(0,l.useState)(()=>{try{let e=localStorage.getItem(`pedal_channel`);return e===`guitar`||e===`bass`?e:`guitar`}catch{return`guitar`}}),k=g(),A=(0,l.useCallback)(async()=>{try{let e=await R.listPresets();if(e?.banks){let t=[];e.banks.forEach(e=>{(e.presets||[]).forEach((n,r)=>{n&&t.push({num:r+1,bank:e.number,name:n.name,chain:n.chain})})}),t.length>0?te(t):te([])}else te([])}catch{te(wt)}},[]),pe=(0,l.useCallback)(e=>{e!==ue&&(de(e),ee(0),C(1),u(null))},[ue]),[me,j]=(0,l.useState)([]),[ge,_e]=(0,l.useState)([]),ve=(0,l.useRef)({blocks:Ct,blockParams:{},selectedBlockId:null}),ye=(0,l.useRef)(null);(0,l.useEffect)(()=>{ve.current={blocks:i,blockParams:d,selectedBlockId:c}},[i,d,c]);let be=(0,l.useCallback)(()=>{let e=ve.current,t={blocks:e.blocks.map(e=>({...e,params:{...e.params}})),blockParams:Object.fromEntries(Object.entries(e.blockParams).map(([e,t])=>[e,{...t}])),selectedBlockId:e.selectedBlockId};j(e=>[...e.slice(-49),t]),_e([])},[]),xe=(0,l.useCallback)(()=>{ye.current||=(be(),setTimeout(()=>{ye.current=null},400))},[be]),Se=(0,l.useCallback)(()=>{j(e=>{if(e.length===0)return e;let t=ve.current,n={blocks:t.blocks.map(e=>({...e,params:{...e.params}})),blockParams:Object.fromEntries(Object.entries(t.blockParams).map(([e,t])=>[e,{...t}])),selectedBlockId:t.selectedBlockId};_e(e=>[...e,n]);let r=e[e.length-1];return a(r.blocks),p(r.blockParams),u(r.selectedBlockId),e.slice(0,-1)})},[]),Ce=(0,l.useCallback)(()=>{_e(e=>{if(e.length===0)return e;let t=ve.current,n={blocks:t.blocks.map(e=>({...e,params:{...e.params}})),blockParams:Object.fromEntries(Object.entries(t.blockParams).map(([e,t])=>[e,{...t}])),selectedBlockId:t.selectedBlockId};j(e=>[...e,n]);let r=e[e.length-1];return a(r.blocks),p(r.blockParams),u(r.selectedBlockId),e.slice(0,-1)})},[]),[we,M]=(0,l.useState)(!1),[Ee,De]=(0,l.useState)(null),Oe=(0,l.useCallback)(e=>{switch(M(!1),e){case`settings`:De(`settings`);break;case`eq`:De(`eq`);break;case`routing`:De(`routing`);break;case`wifi`:De(`wifi`);break;case`tuner`:r(e=>({...e,tuner_enabled:!0})),R.tunerToggle(!0).catch(()=>{});break;default:break}},[]),ke=(0,l.useRef)(!1),Ae=(0,l.useRef)(!1),je=(0,l.useCallback)(async()=>{try{let e=await R.getState();if(r(e),e.input_level!=null&&v(e.input_level),e.output_level!=null&&x(e.output_level),e.current_preset?.name&&!ke.current&&C(e.current_preset.program==null?S:e.current_preset.program+1),e.connected&&o){if(s(!1),e.current_preset?.bank!=null&&e.current_preset?.program!=null)try{let t=await R.getPreset(e.current_preset.bank,e.current_preset.program);if(t?.chain&&t.chain.length>0){a(t.chain.map((e,t)=>{let n={type:e.fx_type||`fx`,enabled:e.enabled!==!1,params:e.params||{},nam_model_path:e.nam_model_path,ir_file_path:e.ir_file_path};return{id:e.id||`block_${e.fx_type||`fx`}_${t}`,type:n.type,name:ht(n),bypassed:!e.enabled,enabled:n.enabled,params:n.params,nam_model_path:n.nam_model_path,ir_file_path:n.ir_file_path}}));return}}catch{}a(Ct)}}catch{}},[S,o]);(0,l.useEffect)(()=>{je();let e=setInterval(je,3e3);return()=>clearInterval(e)},[je]),(0,l.useEffect)(()=>{let e=setInterval(()=>{v(e=>n.input_level!=null&&n.input_level>0?n.input_level:Math.max(0,Math.min(100,e+(Math.random()-.45)*8))),x(e=>n.output_level!=null&&n.output_level>0?n.output_level:Math.max(0,Math.min(100,e+(Math.random()-.45)*8)))},150);return()=>clearInterval(e)},[n.input_level,n.output_level]),(0,l.useEffect)(()=>{try{localStorage.setItem(`pedal_channel`,ue)}catch{}ot(ue)},[ue]),(0,l.useEffect)(()=>{A()},[A,ue]);let Me=(0,l.useCallback)(async(e,t)=>{ke.current=!0,ae(!0);try{await R.activatePreset(e,t),await new Promise(e=>setTimeout(e,300));let n=await R.getState();r(n),n.current_preset?.name&&C(n.current_preset.program==null?t+1:n.current_preset.program+1);try{let n=await R.getPreset(e,t);n?.chain&&n.chain.length>0&&(a(n.chain.map((e,t)=>{let n={type:e.fx_type||`fx`,enabled:e.enabled!==!1,params:e.params||{},nam_model_path:e.nam_model_path,ir_file_path:e.ir_file_path};return{id:e.id||`block_${e.fx_type||`fx`}_${t}`,type:n.type,name:ht(n),bypassed:!e.enabled,enabled:n.enabled,params:n.params,nam_model_path:n.nam_model_path,ir_file_path:n.ir_file_path}})),Ae.current=!0)}catch(e){console.warn(`Could not fetch chain:`,e)}u(null)}catch(e){console.warn(`Preset activate failed:`,e)}ae(!1),setTimeout(()=>{ke.current=!1},5e3),setTimeout(()=>{Ae.current=!1},5e3)},[]),Ne=(0,l.useCallback)(async e=>{let t=T.find(t=>t.num===e);t&&(C(e),await Me(t.bank==null?1:t.bank,e-1))},[T,Me]),Pe=(0,l.useMemo)(()=>i.find(e=>e.id===c),[i,c]);(0,l.useEffect)(()=>{if(Pe&&d[c]==null){let e=ft(Pe.type),t={};e.forEach(e=>{t[e.key]=Pe.params?.[e.key]==null?e.def:Pe.params[e.key]}),p(e=>({...e,[c]:t}))}},[c,Pe,d]);let Fe=(0,l.useCallback)(e=>{u(t=>t===e?null:e)},[]),Le=(0,l.useCallback)((e,t,n)=>{xe(),p(r=>({...r,[e]:{...r[e]||{},[t]:n}}))},[xe]),Re=(0,l.useCallback)(e=>{be(),Ae.current=!0;let t=i.find(t=>t.id===e),n=t?t.bypassed:!1;a(t=>t.map(t=>t.id===e?{...t,bypassed:!t.bypassed}:t)),R.toggleBlock(e,n).catch(()=>{}),setTimeout(()=>{Ae.current=!1},3e3)},[be,i]),ze=(0,l.useCallback)((e,t,n)=>{R.updateBlock(e,{[t]:n}).catch(()=>{})},[]),N=(0,l.useCallback)((e,t)=>{be(),a(n=>{let r=[...n],[i]=r.splice(e,1);return r.splice(t,0,i),r})},[be]),Ve=(0,l.useCallback)(()=>{be(),Ae.current=!0;let e=`block_`+Date.now(),t=`overdrive`,n=ft(t),r={};n.forEach(e=>{r[e.key]=e.def}),a(n=>[...n,{id:e,type:t,name:ht({type:t,params:r}),bypassed:!1,params:r}]),setTimeout(()=>{Ae.current=!1},3e3)},[be]),He=(0,l.useCallback)(e=>{be(),Ae.current=!0,a(t=>t.filter(t=>t.id!==e)),c===e&&u(null),setTimeout(()=>{Ae.current=!1},3e3)},[be,c]),Ue=(0,l.useMemo)(()=>T.filter(e=>e.bank===w),[T,w]),We=w+1,Ge=(0,l.useCallback)(()=>{let e=T.reduce((e,t)=>Math.max(e,t.bank),0),t=Math.min(w+1,e);ee(t);let n=T.filter(e=>e.bank===t);n.length>0&&Me(n[0].bank,n[0].num-1)},[w,T,Me]),P=(0,l.useCallback)(()=>{let e=Math.max(w-1,0);ee(e);let t=T.filter(t=>t.bank===e);t.length>0&&Me(t[0].bank,t[0].num-1)},[w,T,Me]),qe=(0,l.useCallback)(()=>{re(e=>!e),R.bypassToggle().catch(()=>{})},[]),Je=(0,l.useCallback)(()=>{let e=!n.tuner_enabled;r(t=>({...t,tuner_enabled:e})),R.tunerToggle(e).catch(()=>{}),e&&t(`main`)},[n.tuner_enabled]),Ye=(0,l.useCallback)(()=>{Ae.current=!0;let e=oe;e&&(a(t=>t.map(t=>t.id===e?{...t,bypassed:!t.bypassed}:t)),R.toggleBlock(e,!1).catch(()=>{}),setTimeout(()=>{Ae.current=!1},3e3))},[oe]),F=(0,l.useCallback)(e=>{le(e),oe&&se(null)},[oe]),Ze=(0,l.useCallback)(({type:e,name:t,modelPath:n,isBuiltin:r})=>{let i=ce;i&&(be(),Ae.current=!0,a(a=>a.map(a=>{if(a.id!==i)return a;let o=ft(e),s={};return o.forEach(e=>{s[e.key]=e.def}),{...a,type:e,name:t||ht({type:e}),params:s,nam_model_path:!r&&n&&e===`nam`?n:void 0,ir_file_path:!r&&n&&e===`ir`?n:void 0}})),le(null),p(e=>{let t={...e};return delete t[i],t}),setTimeout(()=>{Ae.current=!1},3e3))},[ce,be]),Qe=(0,l.useCallback)((e,t)=>{if(!i.find(e=>e.id===t))return;let n=d[t]?.maxTime??8;switch(e){case`record`:k.record(t,n);break;case`play`:k.play(t,n);break;case`overdub`:k.overdub(t,n);break;case`stop`:k.stop(t);break;case`clear`:k.clearLoop(t);break;case`undo`:k.undoLast(t);break}},[i,d,k]),$e=(0,l.useCallback)(e=>({onRecord:()=>Qe(`record`,e),onPlay:()=>Qe(`play`,e),onOverdub:()=>Qe(`overdub`,e),onStop:()=>Qe(`stop`,e),onClear:()=>Qe(`clear`,e),onUndo:()=>Qe(`undo`,e)}),[Qe]);return(0,f.jsxs)(f.Fragment,{children:[(0,f.jsx)(`style`,{children:gt}),(0,f.jsxs)(`div`,{style:{width:`100%`,height:`100vh`,display:`flex`,flexDirection:`column`,background:z.bg,overflow:`hidden`,fontFamily:`'Inter',sans-serif`,margin:`0 auto`,maxWidth:980},children:[e===`main`&&(0,f.jsxs)(f.Fragment,{children:[(0,f.jsxs)(`div`,{style:{display:`flex`,justifyContent:`space-between`,alignItems:`center`,padding:`4px 12px`,background:z.panel,borderBottom:`1px solid ${z.border}`,flexShrink:0,minHeight:32},children:[(0,f.jsxs)(`div`,{style:{display:`flex`,gap:8,alignItems:`center`},children:[(0,f.jsx)(`div`,{style:{width:6,height:6,borderRadius:`50%`,background:n.connected?z.green:z.red,boxShadow:`0 0 6px ${n.connected?z.green:z.red}`}}),(0,f.jsx)(`span`,{className:`mono`,style:{fontSize:9,color:z.textSec},children:n.connected?`CONNECTED`:`OFFLINE`}),(0,f.jsx)(`button`,{className:`btn-icon`,style:{width:20,height:20,fontSize:11,opacity:me.length>0?1:.3},onClick:Se,disabled:me.length===0,title:`Undo (Ctrl+Z)`,children:`↶`}),(0,f.jsx)(`button`,{className:`btn-icon`,style:{width:20,height:20,fontSize:11,opacity:ge.length>0?1:.3},onClick:Ce,disabled:ge.length===0,title:`Redo (Ctrl+Shift+Z)`,children:`↷`}),(0,f.jsx)(`div`,{style:{width:1,height:12,background:z.border}}),(0,f.jsx)(`span`,{className:`mono`,style:{fontSize:10,color:z.textPrimary,fontWeight:600},children:n.current_preset?.name||T.find(e=>e.num===S)?.name||`—`}),ie&&(0,f.jsx)(`span`,{className:`mono`,style:{fontSize:8,color:z.amber},children:`⟳`}),(0,f.jsx)(`div`,{style:{width:1,height:12,background:z.border}}),n.channel_mode===`stereo`?(0,f.jsx)(`span`,{className:`badge badge-amber`,style:{fontSize:9,padding:`2px 7px`,letterSpacing:`.08em`},children:`STEREO`}):(0,f.jsx)(`div`,{style:{display:`flex`,alignItems:`center`,gap:3,background:z.surface,borderRadius:5,border:`1px solid ${z.border}`,padding:2,height:22},children:[`guitar`,`bass`].map(e=>(0,f.jsxs)(`button`,{onClick:()=>pe(e),style:{padding:`1px 7px`,borderRadius:4,border:`none`,height:18,fontSize:8,fontWeight:700,letterSpacing:`.08em`,textTransform:`uppercase`,background:ue===e?z.amber:`transparent`,color:ue===e?`#000`:z.textSec,cursor:`pointer`,display:`flex`,alignItems:`center`,gap:3,lineHeight:1,transition:`all .1s`,whiteSpace:`nowrap`},children:[(0,f.jsx)(`span`,{style:{fontSize:10,opacity:ue===e?1:.5},children:`🎸`}),e===`guitar`?`GUITAR`:`BASS`]},e))})]}),(0,f.jsxs)(`div`,{style:{display:`flex`,gap:8,alignItems:`center`},children:[(0,f.jsx)(`div`,{style:{fontSize:9,color:z.textDim,fontWeight:600,letterSpacing:`.03em`},children:`IN`}),(0,f.jsx)(xt,{level:_,height:20,width:60}),(0,f.jsx)(`div`,{style:{fontSize:9,color:z.textDim,fontWeight:600,letterSpacing:`.03em`},children:`OUT`}),(0,f.jsx)(xt,{level:y,height:20,width:60}),(0,f.jsxs)(`span`,{className:`mono`,style:{fontSize:9,color:z.textDim},children:[`CPU `,n.cpu_percent==null?`—`:`${n.cpu_percent}%`]}),(0,f.jsx)(`div`,{style:{width:1,height:12,background:z.border}}),(0,f.jsx)(`button`,{className:`btn-icon`,style:{width:24,height:24,position:`relative`},onClick:()=>t(`captures`),title:`Downloads`,children:(0,f.jsx)(`img`,{src:`/ui/img/ic_bank.svg`,alt:`captures`,style:{width:14,height:14,filter:`invert(1) brightness(2)`}})}),(0,f.jsx)(`button`,{className:`btn-icon`,style:{width:24,height:24,position:`relative`},onClick:()=>t(`presets`),title:`Presets`,children:(0,f.jsx)(`img`,{src:`/ui/img/ic_presets.svg`,alt:`presets`,style:{width:14,height:14,filter:`invert(1) brightness(2)`}})}),(0,f.jsx)(`div`,{style:{display:`flex`,gap:2,alignItems:`center`,background:z.surface,borderRadius:4,border:`1px solid ${z.border}`,padding:2,height:24},children:[`stomp`,`preset`].map(e=>(0,f.jsx)(`button`,{onClick:()=>O(e),style:{padding:`1px 5px`,borderRadius:3,border:`none`,height:20,fontSize:8,fontWeight:700,letterSpacing:`.04em`,textTransform:`uppercase`,background:E===e?z.amber:`transparent`,color:E===e?`#000`:z.textDim,cursor:`pointer`,lineHeight:1.5,minWidth:34,display:`flex`,alignItems:`center`,justifyContent:`center`},children:e===`stomp`?`SM`:`PR`},e))}),(0,f.jsx)(`button`,{className:`btn-icon`,style:{width:24,height:24,position:`relative`,background:n.tuner_enabled?`rgba(128,208,160,.15)`:z.surface,borderColor:n.tuner_enabled?z.green:z.border},onClick:Je,title:n.tuner_enabled?`Exit Tuner`:`Tuner`,children:(0,f.jsx)(`img`,{src:`/ui/img/fx_analyzer.svg`,alt:`tuner`,style:{width:14,height:14,filter:n.tuner_enabled?`invert(0.9) brightness(2) sepia(0.4) hue-rotate(80deg)`:`invert(1) brightness(2)`}})}),(0,f.jsx)(`button`,{className:`btn-icon`,style:{width:24,height:24,position:`relative`,background:`rgba(232,160,48,.12)`,borderColor:z.amber},onClick:()=>{document.fullscreenElement?document.exitFullscreen():document.documentElement.requestFullscreen()},title:`Fullscreen`,children:(0,f.jsx)(`svg`,{viewBox:`0 0 24 24`,width:`14`,height:`14`,fill:`#E8A030`,children:(0,f.jsx)(`path`,{d:`M7 14H5v5h5v-2H7v-3zm-2-4h2V7h3V5H5v5zm12 7h-3v2h5v-5h-2v3zM14 5v2h3v3h2V5h-5z`})})}),(0,f.jsx)(`div`,{style:{width:1,height:16,background:z.border}}),(0,f.jsx)(`button`,{className:`btn-icon`,style:{width:24,height:24,position:`relative`},onClick:()=>M(!0),title:`Main Menu`,children:(0,f.jsx)(`span`,{style:{fontSize:14,color:z.textSec,lineHeight:1},children:`☰`})})]})]}),(0,f.jsxs)(`div`,{style:{flex:1,display:`flex`,overflow:`hidden`},children:[(0,f.jsxs)(`div`,{style:{width:90,flexShrink:0,background:z.surface,borderRight:`1px solid ${z.border}`,display:`flex`,flexDirection:`column`,overflow:`hidden`},children:[(0,f.jsxs)(`div`,{style:{padding:`6px 8px`,textAlign:`center`,borderBottom:`1px solid ${z.border}`,background:z.panel},children:[(0,f.jsxs)(`div`,{className:`mono`,style:{fontSize:14,fontWeight:700,color:z.amber},children:[`Bank `,We]}),(0,f.jsx)(`div`,{style:{fontSize:9,color:z.textDim,letterSpacing:`.06em`,textTransform:`uppercase`},children:`Programs`})]}),(0,f.jsx)(`div`,{style:{flex:1,overflowY:`auto`,padding:`4px 6px`,display:`flex`,flexDirection:`column`,gap:3},children:Ue.map(e=>(0,f.jsxs)(`div`,{className:`preset-chip-s ${e.num===S?`active`:``}`,onClick:()=>Ne(e.num),children:[(0,f.jsxs)(`div`,{className:`pcs-num`,children:[`0`,e.num]}),(0,f.jsx)(`div`,{className:`pcs-name`,children:e.name}),(0,f.jsx)(`div`,{className:`pcs-indicator`,style:{background:e.num===S?z.amber:z.border}})]},e.num))})]}),(0,f.jsxs)(`div`,{style:{flex:1,display:`flex`,flexDirection:`column`,overflow:`hidden`,minWidth:0},children:[(0,f.jsx)(`div`,{style:{display:`flex`,gap:4,padding:`3px 8px`,background:z.surface,borderBottom:`1px solid ${z.border}`,flexShrink:0},children:[{label:`Preset`,value:T.find(e=>e.num===S)?.name||`—`},{label:`Routing`,value:(n.routing_mode||`MONO`).toUpperCase()},{label:`Volume`,value:n.master_volume==null?`—`:`${Math.round(n.master_volume*100)}%`},{label:`Bank`,value:We}].map(e=>(0,f.jsxs)(`div`,{style:{flex:1,textAlign:`center`,padding:`2px 6px`,borderRadius:3,background:z.panel,border:`1px solid ${z.border}`},children:[(0,f.jsx)(`div`,{style:{fontSize:7,color:z.textDim,letterSpacing:`.06em`,textTransform:`uppercase`},children:e.label}),(0,f.jsx)(`div`,{className:`mono`,style:{fontSize:10,fontWeight:600,color:z.amber,marginTop:1},children:e.value})]},e.label))}),(0,f.jsx)(`div`,{style:{flex:Pe?`0 0 auto`:1,overflow:`hidden`},children:(0,f.jsx)(D,{blocks:i,selectedBlockId:c,onSelectBlock:Fe,onToggleBlock:Re,onReorder:N,onAddBlock:Ve,onRemoveBlock:He,onModelBrowser:F,looperStores:k.stores})}),Pe&&d[c]?(Pe.type||``).toLowerCase()===`looper`?(0,f.jsx)(b,{block:Pe,params:d[c]||{},looperStore:k.getStore(c),onParamChange:(e,t)=>Le(c,e,t),onParamChangeEnd:(e,t)=>ze(c,e,t),looperActions:$e(c)}):(0,f.jsx)(bt,{block:Pe,params:d[c]||{},viewMode:m,onToggleView:()=>h(e=>e===`slider`?`knob`:`slider`),onParamChange:(e,t)=>Le(c,e,t),onParamChangeEnd:(e,t)=>ze(c,e,t),onFocus:()=>se(c),onModelBrowser:F}):(0,f.jsx)(`div`,{style:{flex:1,display:`flex`,alignItems:`center`,justifyContent:`center`,background:z.bg,color:z.textDim,fontSize:11},children:`Tap a block to edit parameters`})]})]}),(()=>{let e=i.find(e=>e.id===c),t=e&&(e.type||``).toLowerCase()===`looper`;return(0,f.jsx)(fe,{mode:E,blocks:i,selectedBlockId:c,currentBank:We,currentPreset:S,presets:Ue,onSelectBlock:Fe,onToggleBlock:Re,onBankUp:Ge,onBankDown:P,onGlobalBypass:qe,globalBypass:ne,onTunerToggle:Je,tunerEnabled:n.tuner_enabled,onPresetClick:e=>{let t=Ue[parseInt(e)];t&&Me(t.bank,t.num-1)},looperActive:t,looperTransport:t?$e(c):void 0})})()]}),oe&&i.find(e=>e.id===oe)&&d[oe]&&(0,f.jsx)(Te,{block:i.find(e=>e.id===oe),params:d[oe]||{},viewMode:m,onClose:()=>se(null),onParamChange:(e,t)=>Le(oe,e,t),onParamChangeEnd:(e,t)=>ze(oe,e,t),onToggleView:()=>h(e=>e===`slider`?`knob`:`slider`),onToggleBypass:Ye,onOpenModelBrowser:()=>F(oe)}),e===`captures`&&(0,f.jsx)(St,{onClose:()=>t(`main`)}),e===`presets`&&(0,f.jsxs)(`div`,{style:{display:`flex`,flexDirection:`column`,height:`100%`,background:z.bg},children:[(0,f.jsxs)(`div`,{className:`screen-header`,children:[(0,f.jsx)(`div`,{className:`screen-title`,children:`Presets`}),(0,f.jsx)(`button`,{className:`btn-icon`,style:{width:28,height:28,fontSize:13},onClick:()=>t(`main`),children:`✕`})]}),(0,f.jsx)(`div`,{className:`screen-body`,children:T.map((e,n)=>(0,f.jsxs)(`div`,{className:`preset-row ${e.num===S?`active`:``}`,onClick:()=>{Ne(e.num),t(`main`)},children:[(0,f.jsx)(`div`,{className:`mono`,style:{width:26,height:26,borderRadius:5,display:`flex`,alignItems:`center`,justifyContent:`center`,fontSize:11,fontWeight:700,background:e.num===S?`${z.amber}22`:z.surface,color:e.num===S?z.amber:z.textDim,border:`1px solid ${e.num===S?z.amber+`60`:z.border}`},children:e.num}),(0,f.jsxs)(`div`,{style:{flex:1},children:[(0,f.jsx)(`div`,{style:{fontSize:12,fontWeight:600},children:e.name}),(0,f.jsxs)(`div`,{className:`mono`,style:{fontSize:9,color:z.textDim},children:[`Bank `,e.bank==null?1:e.bank,`·Program `,e.num-1]})]}),e.num===S&&(0,f.jsx)(`span`,{style:{color:z.amber,fontSize:16},children:`●`})]},n))})]}),n.tuner_enabled&&(0,f.jsx)(he,{tunerData:n,connected:n.connected,onExit:()=>{r(e=>({...e,tuner_enabled:!1})),R.tunerToggle(!1).catch(()=>{})}}),ce&&(()=>{let e=i.find(e=>e.id===ce);return e?(0,f.jsx)(Ie,{currentBlockType:e.type,currentBlockName:e.name,currentModelPath:e.nam_model_path||e.ir_file_path,onSelectModel:Ze,onClose:()=>le(null)}):null})(),we&&(0,f.jsx)(Be,{onClose:()=>M(!1),onItemSelect:Oe}),Ee===`settings`&&(0,f.jsx)(Ke,{onClose:()=>De(null),onMasterVolume:e=>R.masterVolume(e).catch(()=>{})}),Ee===`eq`&&(0,f.jsx)(Xe,{onClose:()=>De(null)}),Ee===`routing`&&(0,f.jsx)(et,{onClose:()=>De(null)}),Ee===`wifi`&&(0,f.jsx)(rt,{onClose:()=>De(null)})]})]})}(0,u.createRoot)(document.getElementById(`root`)).render((0,f.jsx)(l.StrictMode,{children:(0,f.jsx)(Tt,{})})); \ No newline at end of file diff --git a/src/web/ui-dist/index.html b/src/web/ui-dist/index.html index c509da5..44f8af6 100644 --- a/src/web/ui-dist/index.html +++ b/src/web/ui-dist/index.html @@ -6,7 +6,7 @@ NAM Pedal - +