@digitalmeadow/control-panel 1.0.4 → 1.0.6

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -83,7 +83,7 @@
83
83
  .cp-input-number {
84
84
  width: 50%;
85
85
  background: transparent;
86
- border: 1px solid #fff;
86
+ border: 1px solid rgba(255, 255, 255, 0.3);
87
87
  color: inherit;
88
88
  padding: 2px 4px;
89
89
  border-radius: 2px;
@@ -197,9 +197,6 @@
197
197
  .cp-number-settings {
198
198
  margin-top: 4px;
199
199
  background: transparent;
200
- border: 1px solid rgba(255, 255, 255, 0.2);
201
- padding: 4px;
202
- border-radius: 4px;
203
200
  display: flex;
204
201
  flex-direction: column;
205
202
  gap: 4px;
@@ -279,4 +276,4 @@
279
276
  flex-direction: column;
280
277
  gap: 4px;
281
278
  }
282
- `;let O=!1;function X(){if(O)return;const n=document.createElement("style");n.id="control-panel-styles",n.textContent=N,document.head.appendChild(n),O=!0}function r(n,t={},e=[]){const i=document.createElement(n);for(const[s,a]of Object.entries(t))s==="className"?i.className=String(a):s==="style"&&typeof a=="object"?Object.assign(i.style,a):s==="open"&&typeof a=="boolean"?a?i.setAttribute("open",""):i.removeAttribute("open"):typeof a!="object"&&i.setAttribute(s,String(a));for(const s of e)typeof s=="string"?i.appendChild(document.createTextNode(s)):i.appendChild(s);return i}function F(n){const t=r("button",{className:"cp-button cp-button-delete"},["×"]);return t.addEventListener("click",n),t}function tt(n){return n.replace(/([A-Z])/g," $1").replace(/^./,t=>t.toUpperCase()).trim()}function B(n,t,e){return Math.min(Math.max(n,t),e)}function et(n,t,e){if(t.length!==e.length)throw new Error("Input and output ranges must have the same length");if(t.length<2)throw new Error("Input and output ranges must have at least two values");let i=0;for(;i<t.length-1&&n>t[i+1];)i++;if(i===t.length-1)return e[e.length-1];if(i===0&&n<t[0])return e[0];const s=t[i],a=t[i+1],o=e[i],c=e[i+1];return(n-s)/(a-s)*(c-o)+o}class P{constructor(){this.source=null,this.stream=null,this.fftSize=2048,this.smoothingTimeConstant=.92,this.spectrumBoost=2,this.levels={bass:0,mids:0,highs:0,volume:0},this.peaks={bass:0,mids:0,highs:0,volume:0},this._isAnalyzing=!1,this.loop=()=>{this._isAnalyzing&&(requestAnimationFrame(this.loop),this.update())};const t=window.AudioContext||window.webkitAudioContext;this.ctx=new t,this.analyser=this.ctx.createAnalyser(),this.analyser.fftSize=this.fftSize,this.analyser.smoothingTimeConstant=this.smoothingTimeConstant,this.dataArray=new Uint8Array(this.analyser.frequencyBinCount),this.waveformArray=new Uint8Array(this.analyser.frequencyBinCount)}setFFTSize(t){this.fftSize=t,this.analyser.fftSize=t,this.dataArray=new Uint8Array(this.analyser.frequencyBinCount),this.waveformArray=new Uint8Array(this.analyser.frequencyBinCount)}async setInput(t){try{let e;t==="browser"?e=navigator.mediaDevices.getDisplayMedia({audio:!0,video:!0}):e=navigator.mediaDevices.getUserMedia({audio:!0});const i=await e;this.ctx.state==="suspended"&&this.ctx.resume(),this.source&&this.source.disconnect(),this.stream&&this.stream.getTracks().forEach(s=>s.stop()),this.stream=i,this.source=this.ctx.createMediaStreamSource(this.stream),this.source.connect(this.analyser),this._isAnalyzing=!0,this.loop()}catch(e){console.error("Error accessing audio input:",e),this._isAnalyzing=!1}}update(){if(this.analyser.getByteFrequencyData(this.dataArray),this.analyser.getByteTimeDomainData(this.waveformArray),this.spectrumBoost!==1){const h=this.dataArray.length;for(let p=0;p<h;p++){const f=1+p/h*(this.spectrumBoost-1);this.dataArray[p]=Math.min(255,this.dataArray[p]*f)}}const t=[2,10],e=[10,150],i=[150,600],s=this.getAverage(t[0],t[1]),a=this.getAverage(e[0],e[1]),o=this.getAverage(i[0],i[1]),c=this.getAverage(0,i[1]);this.processLevel("bass",s),this.processLevel("mids",a),this.processLevel("highs",o),this.processLevel("volume",c)}processLevel(t,e){this.peaks[t]-=5e-4,this.peaks[t]=B(this.peaks[t],.1,1),e>this.peaks[t]&&(this.peaks[t]=e),this.levels[t]=B(et(e,[0,this.peaks[t]],[0,1]),0,1)}getAverage(t,e){let i=0;const s=e-t;if(s<=0)return 0;for(let a=t;a<e;a++)i+=this.dataArray[a];return i/s/255}getSignal(t){return()=>this.levels[t]}}const w=new P;class z{constructor(){this.midiAccess=null,this.values=new Map,this.isListening=!1,this.resolveListen=null,this.listeningCallback=null,this.init()}async init(){if(typeof navigator<"u"&&navigator.requestMIDIAccess)try{this.midiAccess=await navigator.requestMIDIAccess(),this.setupInputs(),this.midiAccess.onstatechange=t=>{t.port.type==="input"&&t.port.state==="connected"&&this.setupInputs()}}catch(t){console.warn("MIDI Access failed",t)}}setupInputs(){if(!this.midiAccess)return;const t=this.midiAccess.inputs.values();for(const e of t)e.onmidimessage=this.handleMessage.bind(this)}handleMessage(t){const e=t.data,[i]=e;if((i&240)>=240)return;const a=this.getIdFromMessage(t),o=this.normalizeValue(e);this.values.set(a,o),this.isListening&&this.resolveListen&&o>0&&(this.resolveListen(a),this.isListening=!1,this.resolveListen=null,this.listeningCallback&&this.listeningCallback())}getIdFromMessage(t){const e=t.data,[i,s]=e,a=i&240,o=t.currentTarget.name||"unknown",c=a===144||a===128?"note":"ctrl",h=o.replace(/[^a-zA-Z0-9]/g,"");return`${s}_${c}_${h}`}normalizeValue(t){const[e,i,s]=t,a=e&240;return a===144?s>0?1:0:a===128?0:a===176?s/127:0}listen(){return this.isListening=!0,new Promise(t=>{this.resolveListen=t})}cancelListen(){this.isListening=!1,this.resolveListen=null}getSignal(t){return()=>this.values.get(t)??0}}const j=new z,k=class k{constructor(t,e,i={}){this.changeFns=new Set,this.object=t,this.property=e,this.key=i.id??e,this.initialValue=this.object[this.property],this.domElement=r("div",{className:"cp-controller"});const s=i.label??tt(e),a=r("label",{className:"cp-label"},[String(s)]);a.setAttribute("title",String(s)),this.domElement.appendChild(a),i.disabled&&this.domElement.setAttribute("data-disabled","true")}get value(){return this.object[this.property]}setValue(t,e=!0){this.object[this.property]=t,e&&this.emitChange(t),this.updateDisplay()}save(){return this.value}load(t){this.setValue(t)}reset(){this.setValue(this.initialValue)}onChange(t){return this.changeFns.add(t),this}emitChange(t){for(const e of this.changeFns)e(t)}appendWidget(t){this.domElement.appendChild(t)}};k.audio=w,k.midi=j;let m=k;const R={linear:n=>n,quadIn:n=>n*n,quadOut:n=>n*(2-n),quadInOut:n=>n<.5?2*n*n:-1+(4-2*n)*n,cubicIn:n=>n*n*n,cubicOut:n=>--n*n*n+1,cubicInOut:n=>n<.5?4*n*n*n:(n-1)*(2*n-2)*(2*n-2)+1,expoIn:n=>n===0?0:Math.pow(2,10*(n-1)),expoOut:n=>n===1?1:-Math.pow(2,-10*n)+1,expoInOut:n=>n===0||n===1?n:(n*=2)<1?.5*Math.pow(2,10*(n-1)):.5*(-Math.pow(2,-10*--n)+2),sineIn:n=>1-Math.cos(n*Math.PI/2),sineOut:n=>Math.sin(n*Math.PI/2),sineInOut:n=>-(Math.cos(Math.PI*n)-1)/2};class ${constructor(t){this.rafId=null,this.currentSignalType=null,this.currentMidiId=null,this.currentEase="linear",this.currentBehaviour="forward",this.loop=()=>{if(this.currentSignalType){let e=0;this.currentSignalType==="midi"?this.currentMidiId&&(e=m.midi.getSignal(this.currentMidiId)()):e=m.audio.getSignal(this.currentSignalType)();const i=R[this.currentEase](e);this.onChange(i,this.currentBehaviour),this.rafId=requestAnimationFrame(this.loop)}},this.onChange=t.onChange,this.setupControllers(t.container)}setupControllers(t){const e=this.createSettingSelect("signal",["none","bass","mids","highs","volume","midi"],o=>this.setSignalType(o));this.signalSelect=e.select,t.appendChild(e.row),this.midiRow=r("div",{className:"cp-setting-row",style:"display: none;"});const i=r("label",{className:"cp-setting-label"},["Midi"]);this.midiBtn=r("button",{className:"cp-button cp-input-small"},["Learn"]),this.midiBtn.addEventListener("click",async()=>{if(this.midiBtn.textContent==="Listening..."){m.midi.cancelListen(),this.setMidiId(null);return}this.midiBtn.textContent="Listening...";const o=await m.midi.listen();this.setMidiId(o)}),this.midiRow.appendChild(i),this.midiRow.appendChild(this.midiBtn),t.appendChild(this.midiRow);const s=this.createSettingSelect("behaviour",["forward","backward","loopForward","loopBackward","pingpong"],o=>this.setBehaviour(o));this.behaviourRow=s.row,this.behaviourSelect=s.select,this.behaviourRow.style.display="none",this.behaviourSelect.value=this.currentBehaviour,t.appendChild(this.behaviourRow);const a=this.createSettingSelect("ease",Object.keys(R),o=>this.setEase(o));this.easeRow=a.row,this.easeSelect=a.select,this.easeRow.style.display="none",this.easeSelect.value=this.currentEase,t.appendChild(this.easeRow)}createSettingSelect(t,e,i){const s=r("div",{className:"cp-setting-row"}),a=r("label",{className:"cp-setting-label"},[t]),o=r("select",{className:"cp-select cp-input-small"});return e.forEach(c=>{const h=r("option",{value:c},[c]);o.appendChild(h)}),o.addEventListener("change",()=>i(o.value)),s.appendChild(a),s.appendChild(o),{row:s,select:o}}setSignalType(t){if(!t||t==="none")this.currentSignalType=null,this.currentMidiId=null,this.midiRow.style.display="none",this.easeRow.style.display="none",this.behaviourRow.style.display="none",this.stop(),this.signalSelect.value!=="none"&&(this.signalSelect.value="none");else{this.currentSignalType=t;const e=t==="midi";this.midiRow.style.display=e?"flex":"none",this.easeRow.style.display="flex",this.behaviourRow.style.display="flex",e||(this.currentMidiId=null,m.audio.ctx.state==="suspended"&&m.audio.setInput("microphone")),this.start(),this.signalSelect.value!==t&&(this.signalSelect.value=t)}}setMidiId(t){this.currentMidiId=t,this.midiBtn.textContent=t??"Learn"}setEase(t){this.currentEase=t,this.easeSelect.value!==t&&(this.easeSelect.value=t)}setBehaviour(t){this.currentBehaviour=t,this.behaviourSelect.value!==t&&(this.behaviourSelect.value=t)}start(){!this.rafId&&this.currentSignalType&&this.loop()}stop(){this.rafId&&(cancelAnimationFrame(this.rafId),this.rafId=null)}save(){return{type:this.currentSignalType,midiId:this.currentMidiId,ease:this.currentEase,behaviour:this.currentBehaviour}}load(t){t&&(this.setSignalType(t.type),this.setMidiId(t.midiId||null),this.setEase(t.ease||"linear"),this.setBehaviour(t.behaviour||"forward"))}reset(){this.setSignalType("none"),this.setEase("linear"),this.setBehaviour("forward"),this.setMidiId(null)}}class q extends m{constructor(t,e,i={}){super(t,e,i),this.pingPongDirection=1,this.min=0,this.max=100,this.initialOptions=i,this.min=i.min??0,this.max=i.max??100;const s=r("details",{className:"cp-controller-details"}),a=r("summary",{className:"cp-controller-summary"});this.input=r("input",{type:"range",className:"cp-input-range",step:i.step??"any"}),i.min!==void 0&&(this.input.min=String(i.min)),i.max!==void 0&&(this.input.max=String(i.max)),this.input.value=String(this.value),this.display=r("span",{className:"cp-value-display"},[String(this.value.toFixed(1))]),this.input.addEventListener("input",()=>{const l=parseFloat(this.input.value);isNaN(l)||(this.setValue(l),this.display.textContent=String(l.toFixed(1)))}),this.input.addEventListener("click",l=>{l.stopPropagation()});const o=r("div",{className:"cp-controller-summary-content"});o.appendChild(this.input),o.appendChild(this.display),a.appendChild(o),s.appendChild(a);const c=r("div",{className:"cp-number-settings"}),h=this.createSetting("min",i.min,l=>this.setMin(l));this.minInput=h.input,c.appendChild(h.row);const p=this.createSetting("max",i.max,l=>this.setMax(l));this.maxInput=p.input,c.appendChild(p.row);const b=this.createSetting("step",i.step,l=>this.setStep(l));this.stepInput=b.input,c.appendChild(b.row);const f=r("hr",{className:"cp-separator"});c.appendChild(f),this.signalHandler=new $({container:c,onChange:(l,u)=>this.applySignal(l,u)}),s.appendChild(c),this.appendWidget(s)}setMin(t){typeof t=="number"&&(t=String(t)),t===""||isNaN(parseFloat(t))?this.input.removeAttribute("min"):(this.input.min=t,this.min=parseFloat(t)),this.minInput&&this.minInput.value!==t&&(this.minInput.value=t)}setMax(t){typeof t=="number"&&(t=String(t)),t===""||isNaN(parseFloat(t))?this.input.removeAttribute("max"):(this.input.max=t,this.max=parseFloat(t)),this.maxInput&&this.maxInput.value!==t&&(this.maxInput.value=t)}setStep(t){t===void 0&&(t=""),typeof t=="number"&&(t=String(t)),t===""||t==="any"||isNaN(parseFloat(t))?this.input.step="any":this.input.step=t,this.stepInput&&(t==="any"||t===""?this.stepInput.value="":this.stepInput.value!==t&&(this.stepInput.value=t))}applySignal(t,e){const i=this.max-this.min;let s;if(e==="forward")s=this.min+t*i;else if(e==="backward")s=this.max-t*i;else{const a=t*(i*.01);s=this.value,e==="loopForward"?(s+=a,s>this.max&&(s=this.min+(s-this.min)%i)):e==="loopBackward"?(s-=a,s<this.min&&(s=this.max-(this.max-s)%i)):e==="pingpong"&&(s+=a*this.pingPongDirection,s>=this.max?(s=this.max,this.pingPongDirection=-1):s<=this.min&&(s=this.min,this.pingPongDirection=1))}s=this.roundToStep(s),this.setValue(s),this.input.value=String(s),this.display.textContent=String(s.toFixed(1))}roundToStep(t){const e=this.input.step;if(e==="any"||e===""||isNaN(parseFloat(e)))return t;const i=parseFloat(e),s=this.min;return s+Math.round((t-s)/i)*i}createSetting(t,e,i){const s=r("div",{className:"cp-setting-row"}),a=r("label",{className:"cp-setting-label"},[t]),o=r("input",{type:"number",className:"cp-input-number cp-input-small",step:"any"});return e!==void 0&&(o.value=String(e)),o.addEventListener("input",()=>i(o.value)),s.appendChild(a),s.appendChild(o),{row:s,input:o}}updateDisplay(){this.input.value=String(this.value),this.display.textContent=String(this.value.toFixed(1))}save(){return{value:this.value,settings:{min:this.min,max:this.max,step:this.input.step,signal:this.signalHandler.save()}}}load(t){if(typeof t=="number")this.setValue(t),this.resetSettings();else if(typeof t=="object"&&t!==null&&"value"in t){const e=t.settings||{};e.min!==void 0?this.setMin(e.min):this.setMin(this.initialOptions.min!==void 0?this.initialOptions.min:""),e.max!==void 0?this.setMax(e.max):this.setMax(this.initialOptions.max!==void 0?this.initialOptions.max:""),e.step!==void 0?this.setStep(e.step):this.setStep(this.initialOptions.step);let i=t.value;!isNaN(this.min)&&i<this.min&&(i=this.min),!isNaN(this.max)&&i>this.max&&(i=this.max),this.setValue(i),this.signalHandler?.load(e.signal)}}reset(){this.setValue(this.initialValue),this.resetSettings()}resetSettings(){this.setMin(this.initialOptions.min!==void 0?this.initialOptions.min:""),this.setMax(this.initialOptions.max!==void 0?this.initialOptions.max:""),this.setStep(this.initialOptions.step),this.signalHandler?.reset()}}class H extends m{constructor(t,e,i){super(t,e,i),this.optionValues=[],this.select=r("select",{className:"cp-select"}),this.optionValues=i.options||[],this.optionValues.forEach((s,a)=>{const o=r("option",{value:String(a)},[String(s)]);this.select.appendChild(o)}),this.updateDisplay(),this.select.addEventListener("change",()=>{const s=parseInt(this.select.value),a=this.optionValues[s];this.setValue(a)}),this.appendWidget(this.select)}setOptions(t){this.select.innerHTML="",this.optionValues=t,this.optionValues.forEach((e,i)=>{const s=r("option",{value:String(i)},[String(e)]);this.select.appendChild(s)}),this.updateDisplay(),this.select.value===""&&this.optionValues.length>0&&this.setValue(this.optionValues[0])}updateDisplay(){const t=this.optionValues.indexOf(this.value);t!==-1&&(this.select.value=String(t))}}class U extends m{constructor(t,e,i={}){const s={action:e};super(s,"action",i);const a=i.label??t;this.button=r("button",{className:"cp-button"},[String(a)]),this.button.addEventListener("click",()=>{const o=this.value;typeof o=="function"&&o(),this.emitChange(o)}),this.appendWidget(this.button)}updateDisplay(){}}class _ extends m{constructor(t,e,i={}){super(t,e,i),this.input=r("input",{type:"checkbox",className:"cp-checkbox"}),this.input.checked=this.value,this.input.addEventListener("change",()=>{this.setValue(this.input.checked)}),this.appendWidget(this.input)}updateDisplay(){this.input.checked=this.value}}class W extends m{constructor(t,e,i){super(t,e,i),this.buttons=[],this.optionValues=[],this.container=r("div",{className:"cp-radios"}),this.optionValues=i.options||[],this.optionValues.forEach(s=>{const a=r("button",{className:"cp-button cp-radio"},[String(s)]);a.addEventListener("click",()=>{this.setValue(s)}),this.container.appendChild(a),this.buttons.push(a)}),this.updateDisplay(),this.appendWidget(this.container)}updateDisplay(){const t=this.value;this.buttons.forEach((e,i)=>{this.optionValues[i]===t?e.setAttribute("data-active","true"):e.removeAttribute("data-active")})}}class J extends m{constructor(t,e,i={}){super(t,e,i),this.input=r("input",{type:"color",className:"cp-input-color",value:this.value||"#000000"}),this.appendWidget(this.input),this.input.addEventListener("input",s=>{const a=s.target;this.setValue(a.value)}),this.updateDisplay()}updateDisplay(){this.input.value=this.value}}function G(n){const t=/^#?([a-f\d])([a-f\d])([a-f\d])$/i;n=n.replace(t,(i,s,a,o)=>s+s+a+a+o+o);const e=/^#?([a-f\d]{2})([a-f\d]{2})([a-f\d]{2})$/i.exec(n);return e?[parseInt(e[1],16),parseInt(e[2],16),parseInt(e[3],16)]:[0,0,0]}function st(n,t,e){return"#"+((1<<24)+(Math.round(n)<<16)+(Math.round(t)<<8)+Math.round(e)).toString(16).slice(1)}function I(n){const t=n/255;return t<=.04045?t/12.92:Math.pow((t+.055)/1.055,2.4)}function A(n){return n<=.0031308?n*12.92*255:(1.055*Math.pow(n,1/2.4)-.055)*255}function it(n,t,e){const[i,s,a]=G(n),[o,c,h]=G(t),p=I(i),b=I(s),f=I(a),l=I(o),u=I(c),g=I(h),v=p+e*(l-p),E=b+e*(u-b),S=f+e*(g-f),x=A(v),y=A(E),C=A(S);return st(x,y,C)}class Z extends m{constructor(t,e,i={}){super(t,e,i),this.stops=[],this.pingPongDirection=1,this.animationT=0,this.manualPosition=0,this.initialOptions=i,this.stops=i.stops||[{color:"#000000",position:0},{color:"#ffffff",position:1}],this.sortStops();const s=r("details",{className:"cp-controller-details"}),a=r("summary",{className:"cp-controller-summary"}),o=r("div",{className:"cp-controller-summary-content"});this.displayColor=r("div",{className:"cp-color-swatch",style:`background-color: ${this.value}`}),this.displayText=r("span",{className:"cp-value-display"},[String(this.value)]),o.appendChild(this.displayColor),o.appendChild(this.displayText),a.appendChild(o),s.appendChild(a);const c=r("div",{className:"cp-number-settings"});this.stopsContainer=r("div",{className:"cp-stops-container"}),this.renderStops(),c.appendChild(this.stopsContainer);const h=r("button",{className:"cp-button"},["+ Add Stop"]);h.addEventListener("click",()=>{this.stops.push({color:"#ffffff",position:.5}),this.sortStops(),this.renderStops(),this.updateOutput()}),c.appendChild(h);const p=r("hr",{className:"cp-separator"});c.appendChild(p),this.signalHandler=new $({container:c,onChange:(b,f)=>this.applySignal(b,f)}),s.appendChild(c),this.appendWidget(s),this.updateOutput(0)}sortStops(){this.stops.sort((t,e)=>t.position-e.position)}renderStops(){this.stopsContainer.innerHTML="",this.stops.forEach((t,e)=>{const i=r("div",{className:"cp-setting-row"}),s=r("input",{type:"color",className:"cp-input-color",value:t.color});s.addEventListener("input",c=>{t.color=c.target.value,this.updateOutput()});const a=r("input",{type:"number",className:"cp-input-number cp-input-small",min:"0",max:"1",step:"0.01",value:String(t.position)});a.addEventListener("change",c=>{let h=parseFloat(c.target.value);isNaN(h)&&(h=0),t.position=Math.max(0,Math.min(1,h)),this.sortStops(),this.renderStops(),this.updateOutput()});const o=F(()=>{this.stops.splice(e,1),this.renderStops(),this.updateOutput()});i.appendChild(s),i.appendChild(a),i.appendChild(o),this.stopsContainer.appendChild(i)})}updateOutput(t=this.manualPosition){if(this.stops.length===0)return;if(this.stops.length===1){this.setValue(this.stops[0].color),this.updateDisplay();return}let e="#000000";if(t=Math.max(0,Math.min(1,t)),t<=this.stops[0].position)e=this.stops[0].color;else if(t>=this.stops[this.stops.length-1].position)e=this.stops[this.stops.length-1].color;else for(let i=0;i<this.stops.length-1;i++){const s=this.stops[i],a=this.stops[i+1];if(t>=s.position&&t<=a.position){const o=a.position-s.position,c=o===0?0:(t-s.position)/o;e=it(s.color,a.color,c);break}}this.setValue(e),this.updateDisplay()}updateDisplay(){this.displayColor&&(this.displayColor.style.backgroundColor=this.value),this.displayText&&(this.displayText.textContent=this.value)}applySignal(t,e){let i=t;if(e==="forward")i=t;else if(e==="backward")i=1-t;else{const s=t*.05;e==="loopForward"?(this.animationT=(this.animationT+s)%1,i=this.animationT):e==="loopBackward"?(this.animationT=(this.animationT-s+1)%1,i=this.animationT):e==="pingpong"&&(this.animationT+=s*this.pingPongDirection,this.animationT>=1?(this.animationT=1,this.pingPongDirection=-1):this.animationT<=0&&(this.animationT=0,this.pingPongDirection=1),i=this.animationT)}this.updateOutput(i),this.manualPosition=i}save(){return{stops:this.stops,settings:{signal:this.signalHandler.save()}}}load(t){t&&t.stops&&(this.stops=t.stops,this.sortStops(),this.renderStops()),t&&t.settings&&this.signalHandler?.load(t.settings.signal)}reset(){this.stops=this.initialOptions.stops||[{color:"#000000",position:0},{color:"#ffffff",position:1}],this.sortStops(),this.renderStops(),this.signalHandler?.reset(),this.updateOutput(0)}}class K extends m{constructor(t,e,i={}){super(t,e,i),this.items=[],this.initialOptions=i,this.itemType=i.itemType||"string",this.items=this.parseValue(this.value);const s=r("details",{className:"cp-controller-details"}),a=r("summary",{className:"cp-controller-summary"}),o=r("div",{className:"cp-controller-summary-content"}),c=r("span",{className:"cp-value-display"},[`${this.items.length} items`]);o.appendChild(c),a.appendChild(o),s.appendChild(a);const h=r("div",{className:"cp-number-settings"});this.itemsContainer=r("div",{className:"cp-stops-container"}),this.renderItems(),h.appendChild(this.itemsContainer);const p=r("button",{className:"cp-button cp-input-small",style:"margin-top: 8px; width: 100%;"},["+ Add Item"]);p.addEventListener("click",()=>{this.addItem()}),h.appendChild(p),s.appendChild(h),this.appendWidget(s)}parseValue(t){return!t||t.trim()===""?[]:t.split(",").map(e=>e.trim())}serializeValue(){return this.items.join(",")}getDefaultItemValue(){switch(this.itemType){case"color":return"#ffffff";case"number":return"0";default:return""}}addItem(t){const e=t!==void 0?t:this.getDefaultItemValue();this.items.push(e),this.renderItems(),this.updateValue()}updateValue(){const t=this.serializeValue();this.setValue(t),this.updateSummary()}updateSummary(){const t=this.domElement.querySelector(".cp-value-display");t&&(t.textContent=`${this.items.length} items`)}renderItems(){this.itemsContainer.innerHTML="",this.items.forEach((t,e)=>{const i=r("div",{className:"cp-setting-row"});let s;this.itemType==="color"?s=r("input",{type:"color",className:"cp-input-color",value:t}):this.itemType==="number"?s=r("input",{type:"number",className:"cp-input-number cp-input-small",step:"any",value:t}):s=r("input",{type:"text",className:"cp-input-number cp-input-small",value:t}),s.addEventListener("input",o=>{this.items[e]=o.target.value,this.updateValue()});const a=F(()=>{this.items.splice(e,1),this.renderItems(),this.updateValue()});i.appendChild(s),i.appendChild(a),this.itemsContainer.appendChild(i)})}updateDisplay(){}save(){return[...this.items]}load(t){Array.isArray(t)?this.items=[...t]:typeof t=="string"&&(this.items=this.parseValue(t)),this.renderItems(),this.updateValue()}reset(){const t=this.initialValue||"";this.items=this.parseValue(t),this.renderItems(),this.updateValue()}}class nt{constructor(){this.frames=0,this.pollingInterval=1e3,this.prevTime=performance.now(),this.render=()=>{this.frames++;const t=performance.now();if(t>=this.prevTime+this.pollingInterval){const e=Math.round(this.frames*1e3/(t-this.prevTime));let i="";const s=performance.memory;s&&(i=` / ${Math.round(s.usedJSHeapSize/1048576)}MB`),this.domElement.textContent=`${e} FPS${i}`,this.prevTime=t,this.frames=0}this.rafId=requestAnimationFrame(this.render)},this.domElement=r("span",{className:"cp-stats"}),this.rafId=requestAnimationFrame(this.render)}destroy(){cancelAnimationFrame(this.rafId)}}class V{constructor(){this.controllers=[],this.folders=[]}addNumber(t,e,i={}){const s=new q(t,e,i);return this.contentElement.appendChild(s.domElement),this.controllers.push(s),s}addSelect(t,e,i={}){const s=new H(t,e,i);return this.contentElement.appendChild(s.domElement),this.controllers.push(s),s}addBoolean(t,e,i={}){const s=new _(t,e,i);return this.contentElement.appendChild(s.domElement),this.controllers.push(s),s}addButton(t,e,i={}){const s=new U(t,e,i);return this.contentElement.appendChild(s.domElement),this.controllers.push(s),s}addRadio(t,e,i={}){const s=new W(t,e,i);return this.contentElement.appendChild(s.domElement),this.controllers.push(s),s}addColor(t,e,i={}){const s=new J(t,e,i);return this.contentElement.appendChild(s.domElement),this.controllers.push(s),s}addGradient(t,e,i={}){const s=new Z(t,e,i);return this.contentElement.appendChild(s.domElement),this.controllers.push(s),s}addArray(t,e,i={}){const s=new K(t,e,i);return this.contentElement.appendChild(s.domElement),this.controllers.push(s),s}addFolder(t){const e=new Y(t);return this.contentElement.appendChild(e.domElement),this.folders.push(e),e}save(){const t={controllers:{},folders:{}};for(const e of this.controllers)typeof e.value!="function"&&(t.controllers[e.key]=e.save());for(const e of this.folders)t.folders[e.title]=e.save();return t}load(t){if(!t){this.reset();return}for(const e of this.controllers)if(typeof e.value!="function")if(t.controllers&&e.key in t.controllers){const i=t.controllers[e.key];i!==void 0&&e.load(i)}else e.reset();for(const e of this.folders){const i=t.folders?t.folders[e.title]:void 0;e.load(i)}}reset(){for(const t of this.controllers)typeof t.value!="function"&&t.reset();for(const t of this.folders)t.reset()}}class Y extends V{constructor(t){super(),this.title=t,this.domElement=r("details",{className:"cp-folder",open:!0}),this.summaryElement=r("summary",{className:"cp-summary"},[t]),this.domElement.appendChild(this.summaryElement),this.contentElement=r("div",{className:"cp-content cp-folder-content"}),this.domElement.appendChild(this.contentElement),this.domElement.appendChild(r("hr",{className:"cp-separator"}))}}class at extends V{constructor(t,e={}){super(),X(),this.domElement=r("details",{className:"cp-root",open:!0}),this.summaryElement=r("summary",{className:"cp-summary cp-summary-root"}),this.domElement.appendChild(this.summaryElement);const i=r("span",{},[e.title||"ControlPanel"]);this.summaryElement.appendChild(i),this.stats=new nt,this.summaryElement.appendChild(this.stats.domElement),this.contentElement=r("div",{className:"cp-content"}),this.domElement.appendChild(this.contentElement);const s=this.addFolder("_Signals"),a={audioInput:null,fftSize:2048};s.addRadio(a,"audioInput",{label:"Audio Signal",options:["microphone","browser"]}).onChange(l=>{w.setInput(l)}),s.addSelect(a,"fftSize",{label:"FFT Size",options:[256,512,1024,2048]}).onChange(l=>{w.setFFTSize(l)}),s.addNumber(w,"smoothingTimeConstant",{min:0,max:.99,step:.01,label:"Smoothing"}).onChange(l=>{w.analyser.smoothingTimeConstant=l}),s.addNumber(w,"spectrumBoost",{min:1,max:5,step:.1,label:"Compression"}),t?t.appendChild(this.domElement):document.body.appendChild(this.domElement);const o=e.title||"ControlPanel";this.presetStoragePrefix=`cp-presets-${o}-`;const c=this.addFolder("_User Presets"),h=()=>{const l=["Default"];if(typeof localStorage>"u")return l;for(let u=0;u<localStorage.length;u++){const g=localStorage.key(u);if(g&&g.startsWith(this.presetStoragePrefix)){const v=g.substring(this.presetStoragePrefix.length);v!=="Default"&&!l.includes(v)&&l.push(v)}}return l.sort()},p={selected:"Default",save:()=>{const l=prompt("Preset Name:",p.selected);if(l){if(l==="Default"){alert("Cannot overwrite Default preset");return}const u=this.presetStoragePrefix+l;this.saveToLocalStorage(u);const g=h();f.setOptions(g),p.selected=l,f.setValue(l)}},load:()=>{const l=p.selected,u=this.presetStoragePrefix+l;this.loadFromLocalStorage(u),p.selected=l,f.setValue(l)},delete:()=>{if(p.selected==="Default"){alert("Cannot delete Default preset");return}if(confirm(`Delete preset "${p.selected}"?`)){const l=this.presetStoragePrefix+p.selected;localStorage.removeItem(l);const u=h();f.setOptions(u),p.selected="Default",f.setValue("Default"),this.reset()}},export:()=>{const l=this.save(),u=L=>{const T={controllers:{},folders:{}};for(const[M,D]of Object.entries(L.controllers))M.startsWith("_")||(T.controllers[M]=D);for(const[M,D]of Object.entries(L.folders))M.startsWith("_")||(T.folders[M]=u(D));return T},g=u(l),v={_presetName:p.selected||"CustomPreset",_exportDate:new Date().toISOString(),_instructions:"To add as factory preset: Copy 'controllers' and 'folders' fields into the presets.json file",...g},E=JSON.stringify(v,null,2),S=new Blob([E],{type:"application/json"}),x=URL.createObjectURL(S),y=document.createElement("a");y.href=x;const C=new Date().toISOString().split("T")[0],Q=p.selected.replace(/[^a-z0-9]/gi,"-").toLowerCase();y.download=`${o.toLowerCase()}-preset-${Q}-${C}.json`,document.body.appendChild(y),y.click(),document.body.removeChild(y),URL.revokeObjectURL(x)},import:()=>{const l=document.createElement("input");l.type="file",l.accept=".json",l.onchange=u=>{const g=u.target.files?.[0];if(!g)return;const v=new FileReader;v.onload=E=>{try{const S=E.target?.result,x=JSON.parse(S),y={controllers:x.controllers||{},folders:x.folders||{}};if(!y.controllers||!y.folders){alert("Invalid preset file: missing 'controllers' or 'folders'");return}this.load(y);const C=x._presetName||"ImportedPreset";if(confirm(`Preset loaded! Save as "${C}" to User Presets?`)){const L=this.presetStoragePrefix+C;this.saveToLocalStorage(L);const T=h();f.setOptions(T),p.selected=C,f.setValue(C)}}catch(S){alert(`Failed to import preset: ${S instanceof Error?S.message:"Invalid JSON"}`),console.error("Import error:",S)}},v.readAsText(g)},l.click()}},b=h(),f=c.addSelect(p,"selected",{label:"Preset",options:b});c.addButton("Load",()=>p.load()),c.addButton("Save / New",()=>p.save()),c.addButton("Delete",()=>p.delete()),c.addButton("Export JSON",()=>p.export()),c.addButton("Import JSON",()=>p.import())}saveToLocalStorage(t){const e=this.save();try{localStorage.setItem(t,JSON.stringify(e))}catch(i){console.warn("ControlPanel: Failed to save to localStorage",i)}}loadFromLocalStorage(t){try{const e=localStorage.getItem(t);if(e){const i=JSON.parse(e);this.load(i)}}catch(e){console.warn("ControlPanel: Failed to load from localStorage",e)}}saveDefaultPreset(){const t=this.presetStoragePrefix+"Default";this.save(),this.saveToLocalStorage(t)}destroy(){this.stats.destroy(),this.domElement.remove(),this.controllers=[],this.folders=[]}}d.ArrayController=K,d.AudioSignals=P,d.BooleanController=_,d.ButtonController=U,d.ColorController=J,d.ControlPanel=at,d.ControlPanelContainer=V,d.Controller=m,d.Folder=Y,d.GradientController=Z,d.MidiSignals=z,d.NumberController=q,d.RadioController=W,d.SelectController=H,d.audioSignals=w,d.midiSignals=j,Object.defineProperty(d,Symbol.toStringTag,{value:"Module"})}));
279
+ `;let O=!1;function et(){if(O)return;const n=document.createElement("style");n.id="control-panel-styles",n.textContent=N,document.head.appendChild(n),O=!0}function r(n,t={},e=[]){const s=document.createElement(n);for(const[i,a]of Object.entries(t))i==="className"?s.className=String(a):i==="style"&&typeof a=="object"?Object.assign(s.style,a):i==="open"&&typeof a=="boolean"?a?s.setAttribute("open",""):s.removeAttribute("open"):typeof a!="object"&&s.setAttribute(i,String(a));for(const i of e)typeof i=="string"?s.appendChild(document.createTextNode(i)):s.appendChild(i);return s}function F(n){const t=r("button",{className:"cp-button cp-button-delete"},["×"]);return t.addEventListener("click",n),t}function st(n){return n.replace(/([A-Z])/g," $1").replace(/^./,t=>t.toUpperCase()).trim()}function D(n,t,e){return Math.min(Math.max(n,t),e)}function it(n,t,e){if(t.length!==e.length)throw new Error("Input and output ranges must have the same length");if(t.length<2)throw new Error("Input and output ranges must have at least two values");let s=0;for(;s<t.length-1&&n>t[s+1];)s++;if(s===t.length-1)return e[e.length-1];if(s===0&&n<t[0])return e[0];const i=t[s],a=t[s+1],o=e[s],c=e[s+1];return(n-i)/(a-i)*(c-o)+o}class B{constructor(){this.source=null,this.stream=null,this.fftSize=2048,this.smoothingTimeConstant=.82,this.spectrumBoost=3,this.levels={volume:0,bass:0,mids:0,highs:0},this.peaks={volume:0,bass:0,mids:0,highs:0},this._isAnalyzing=!1,this.loop=()=>{this._isAnalyzing&&(requestAnimationFrame(this.loop),this.update())};const t=window.AudioContext||window.webkitAudioContext;this.ctx=new t,this.analyser=this.ctx.createAnalyser(),this.analyser.fftSize=this.fftSize,this.analyser.smoothingTimeConstant=this.smoothingTimeConstant,this.dataArray=new Uint8Array(this.analyser.frequencyBinCount),this.waveformArray=new Uint8Array(this.analyser.frequencyBinCount)}setFFTSize(t){this.fftSize=t,this.analyser.fftSize=t,this.dataArray=new Uint8Array(this.analyser.frequencyBinCount),this.waveformArray=new Uint8Array(this.analyser.frequencyBinCount)}async setInput(t){try{let e;t==="browser"?e=navigator.mediaDevices.getDisplayMedia({audio:!0,video:!0}):e=navigator.mediaDevices.getUserMedia({audio:!0});const s=await e;this.ctx.state==="suspended"&&this.ctx.resume(),this.source&&this.source.disconnect(),this.stream&&this.stream.getTracks().forEach(i=>i.stop()),this.stream=s,this.source=this.ctx.createMediaStreamSource(this.stream),this.source.connect(this.analyser),this._isAnalyzing=!0,this.loop()}catch(e){console.error("Error accessing audio input:",e),this._isAnalyzing=!1}}update(){if(this.analyser.getByteFrequencyData(this.dataArray),this.analyser.getByteTimeDomainData(this.waveformArray),this.spectrumBoost!==1){const p=this.dataArray.length;for(let h=0;h<p;h++){const f=1+h/p*(this.spectrumBoost-1);this.dataArray[h]=Math.min(255,this.dataArray[h]*f)}}const t=[2,10],e=[10,150],s=[150,600],i=this.getAverage(t[0],t[1]),a=this.getAverage(e[0],e[1]),o=this.getAverage(s[0],s[1]),c=this.getAverage(0,s[1]);this.processLevel("bass",i),this.processLevel("mids",a),this.processLevel("highs",o),this.processLevel("volume",c)}processLevel(t,e){this.peaks[t]-=5e-4,this.peaks[t]=D(this.peaks[t],.1,1),e>this.peaks[t]&&(this.peaks[t]=e),this.levels[t]=D(it(e,[0,this.peaks[t]],[0,1]),0,1)}getAverage(t,e){let s=0;const i=e-t;if(i<=0)return 0;for(let a=t;a<e;a++)s+=this.dataArray[a];return s/i/255}getSignal(t){return()=>this.levels[t]}}const x=new B;class q{constructor(){this.midiAccess=null,this.values=new Map,this.isListening=!1,this.resolveListen=null,this.listeningCallback=null,this.init()}async init(){if(typeof navigator<"u"&&navigator.requestMIDIAccess)try{this.midiAccess=await navigator.requestMIDIAccess(),this.setupInputs(),this.midiAccess.onstatechange=t=>{t.port.type==="input"&&t.port.state==="connected"&&this.setupInputs()}}catch(t){console.warn("MIDI Access failed",t)}}setupInputs(){if(!this.midiAccess)return;const t=this.midiAccess.inputs.values();for(const e of t)e.onmidimessage=this.handleMessage.bind(this)}handleMessage(t){const e=t.data,[s]=e;if((s&240)>=240)return;const a=this.getIdFromMessage(t),o=this.normalizeValue(e);this.values.set(a,o),this.isListening&&this.resolveListen&&o>0&&(this.resolveListen(a),this.isListening=!1,this.resolveListen=null,this.listeningCallback&&this.listeningCallback())}getIdFromMessage(t){const e=t.data,[s,i]=e,a=s&240,o=t.currentTarget.name||"unknown",c=a===144||a===128?"note":"ctrl",p=o.replace(/[^a-zA-Z0-9]/g,"");return`${i}_${c}_${p}`}normalizeValue(t){const[e,s,i]=t,a=e&240;return a===144?i>0?1:0:a===128?0:a===176?i/127:0}listen(){return this.isListening=!0,new Promise(t=>{this.resolveListen=t})}cancelListen(){this.isListening=!1,this.resolveListen=null}getSignal(t){return()=>this.values.get(t)??0}}const z=new q;class R{constructor(){this.configs=new Map,this.lastRandomUpdateTime=0,this.currentRandomValue=0,this.startTime=performance.now(),this.configs.set("constant",{frequency:.1,amplitude:1,offset:0,phase:0}),this.configs.set("sine",{frequency:.1,amplitude:1,offset:0,phase:0}),this.configs.set("sawtooth",{frequency:.1,amplitude:1,offset:0,phase:0}),this.configs.set("triangle",{frequency:.1,amplitude:1,offset:0,phase:0}),this.configs.set("square",{frequency:.1,amplitude:1,offset:0,phase:0}),this.configs.set("random",{frequency:10,amplitude:1,offset:0,phase:0})}setConfig(t,e){const s=this.configs.get(t);this.configs.set(t,{...s,...e})}getConfig(t){return{...this.configs.get(t)}}getTime(){return(performance.now()-this.startTime)/1e3}normalizeOutput(t,e){const s=t*e.amplitude+e.offset;return Math.max(0,Math.min(1,s))}constant(t){const e=t?{...this.configs.get("constant"),...t}:this.configs.get("constant"),i=this.getTime()*e.frequency%1;return this.normalizeOutput(i,e)}sine(t){const e=t?{...this.configs.get("sine"),...t}:this.configs.get("sine"),s=this.getTime(),i=e.phase*Math.PI*2,o=(Math.sin(s*e.frequency*Math.PI*2+i)+1)/2;return this.normalizeOutput(o,e)}sawtooth(t){const e=t?{...this.configs.get("sawtooth"),...t}:this.configs.get("sawtooth"),s=this.getTime(),i=e.phase,a=(s*e.frequency+i)%1+i%1;return this.normalizeOutput(a%1,e)}triangle(t){const e=t?{...this.configs.get("triangle"),...t}:this.configs.get("triangle"),s=this.getTime(),i=e.phase,a=(s*e.frequency+i)%1,o=a<.5?a*2:2-a*2;return this.normalizeOutput(o,e)}square(t){const e=t?{...this.configs.get("square"),...t}:this.configs.get("square"),s=this.getTime(),i=e.phase,o=(s*e.frequency+i)%1<.5?0:1;return this.normalizeOutput(o,e)}random(t){const e=t?{...this.configs.get("random"),...t}:this.configs.get("random"),s=performance.now(),i=1e3/e.frequency;return s-this.lastRandomUpdateTime>i&&(this.currentRandomValue=Math.random(),this.lastRandomUpdateTime=s),this.normalizeOutput(this.currentRandomValue,e)}getSignal(t){switch(t){case"constant":return()=>this.constant();case"sine":return()=>this.sine();case"sawtooth":return()=>this.sawtooth();case"triangle":return()=>this.triangle();case"square":return()=>this.square();case"random":return()=>this.random()}}reset(){this.startTime=performance.now(),this.lastRandomUpdateTime=0,this.currentRandomValue=0}}const j=new R,E=class E{constructor(t,e,s={}){this.changeFns=new Set,this.object=t,this.property=e,this.key=s.id??e,this.initialValue=this.object[this.property],this.domElement=r("div",{className:"cp-controller"});const i=s.label??st(e),a=r("label",{className:"cp-label"},[String(i)]);a.setAttribute("title",String(i)),this.domElement.appendChild(a),s.disabled&&this.domElement.setAttribute("data-disabled","true")}get value(){return this.object[this.property]}setValue(t,e=!0){this.object[this.property]=t,e&&this.emitChange(t),this.updateDisplay()}save(){return this.value}load(t){this.setValue(t)}reset(){this.setValue(this.initialValue)}onChange(t){return this.changeFns.add(t),this}emitChange(t){for(const e of this.changeFns)e(t)}appendWidget(t){this.domElement.appendChild(t)}};E.audio=x,E.midi=z,E.math=j;let u=E;const $={linear:n=>n,quadIn:n=>n*n,quadOut:n=>n*(2-n),quadInOut:n=>n<.5?2*n*n:-1+(4-2*n)*n,cubicIn:n=>n*n*n,cubicOut:n=>--n*n*n+1,cubicInOut:n=>n<.5?4*n*n*n:(n-1)*(2*n-2)*(2*n-2)+1,expoIn:n=>n===0?0:Math.pow(2,10*(n-1)),expoOut:n=>n===1?1:-Math.pow(2,-10*n)+1,expoInOut:n=>n===0||n===1?n:(n*=2)<1?.5*Math.pow(2,10*(n-1)):.5*(-Math.pow(2,-10*--n)+2),sineIn:n=>1-Math.cos(n*Math.PI/2),sineOut:n=>Math.sin(n*Math.PI/2),sineInOut:n=>-(Math.cos(Math.PI*n)-1)/2};class U{constructor(t){this.rafId=null,this.currentSignalType=null,this.currentMidiId=null,this.currentEase="linear",this.currentBehaviour="forward",this.loop=()=>{if(this.currentSignalType){let e=0;this.currentSignalType==="midi"?this.currentMidiId&&(e=u.midi.getSignal(this.currentMidiId)()):["constant","sine","sawtooth","triangle","square","random"].includes(this.currentSignalType)?e=u.math.getSignal(this.currentSignalType)():["volume","bass","mids","highs"].includes(this.currentSignalType)&&(e=u.audio.getSignal(this.currentSignalType)());const s=$[this.currentEase](e);this.onChange(s,this.currentBehaviour),this.rafId=requestAnimationFrame(this.loop)}},this.onChange=t.onChange,this.setupControllers(t.container)}setupControllers(t){const e=this.createSettingSelect("signal",["none","volume","bass","mids","highs","constant","sine","sawtooth","triangle","square","random","midi"],o=>this.setSignalType(o));this.signalSelect=e.select,t.appendChild(e.row),this.midiRow=r("div",{className:"cp-setting-row",style:"display: none;"});const s=r("label",{className:"cp-setting-label"},["Midi"]);this.midiBtn=r("button",{className:"cp-button cp-input-small"},["Learn"]),this.midiBtn.addEventListener("click",async()=>{if(this.midiBtn.textContent==="Listening..."){u.midi.cancelListen(),this.setMidiId(null);return}this.midiBtn.textContent="Listening...";const o=await u.midi.listen();this.setMidiId(o)}),this.midiRow.appendChild(s),this.midiRow.appendChild(this.midiBtn),t.appendChild(this.midiRow),this.mathParamsContainer=r("div",{style:"display: none; flex-direction: column; gap: 4px;"}),t.appendChild(this.mathParamsContainer);const i=this.createSettingSelect("behaviour",["forward","backward","loopForward","loopBackward","pingpong"],o=>this.setBehaviour(o));this.behaviourRow=i.row,this.behaviourSelect=i.select,this.behaviourRow.style.display="none",this.behaviourSelect.value=this.currentBehaviour,t.appendChild(this.behaviourRow);const a=this.createSettingSelect("ease",Object.keys($),o=>this.setEase(o));this.easeRow=a.row,this.easeSelect=a.select,this.easeRow.style.display="none",this.easeSelect.value=this.currentEase,t.appendChild(this.easeRow)}createSettingSelect(t,e,s){const i=r("div",{className:"cp-setting-row"}),a=r("label",{className:"cp-setting-label"},[t]),o=r("select",{className:"cp-select cp-input-small"});return e.forEach(c=>{const p=r("option",{value:c},[c]);o.appendChild(p)}),o.addEventListener("change",()=>s(o.value)),i.appendChild(a),i.appendChild(o),{row:i,select:o}}createNumberInput(t,e,s,i,a,o){const c=r("div",{className:"cp-setting-row"}),p=r("label",{className:"cp-setting-label"},[t]),h=r("input",{className:"cp-input-number cp-input-small",type:"number",value:String(e),min:String(s),max:String(i),step:String(a)});return h.addEventListener("input",()=>{const m=parseFloat(h.value);isNaN(m)||o(m)}),c.appendChild(p),c.appendChild(h),{row:c,input:h}}updateMathParams(t){this.mathParamsContainer.innerHTML="";const e=u.math.getConfig(t);let s="Frequency",i=.1,a=10,o=.1;t==="random"?a=30:t==="constant"&&(s="Speed",i=.01,a=2,o=.01);const c=this.createNumberInput(s,e.frequency,i,a,o,m=>u.math.setConfig(t,{frequency:m}));this.mathParamsContainer.appendChild(c.row);const p=this.createNumberInput("Amplitude",e.amplitude,0,2,.1,m=>u.math.setConfig(t,{amplitude:m}));this.mathParamsContainer.appendChild(p.row);const h=this.createNumberInput("Offset",e.offset,0,1,.1,m=>u.math.setConfig(t,{offset:m}));if(this.mathParamsContainer.appendChild(h.row),["sine","sawtooth","triangle","square"].includes(t)){const m=this.createNumberInput("Phase",e.phase,0,1,.01,f=>u.math.setConfig(t,{phase:f}));this.mathParamsContainer.appendChild(m.row)}}setSignalType(t){if(!t||t==="none")this.currentSignalType=null,this.currentMidiId=null,this.midiRow.style.display="none",this.mathParamsContainer.style.display="none",this.easeRow.style.display="none",this.behaviourRow.style.display="none",this.stop(),this.signalSelect.value!=="none"&&(this.signalSelect.value="none");else{this.currentSignalType=t;const e=t==="midi",s=["constant","sine","sawtooth","triangle","square","random"].includes(t),i=["volume","bass","mids","highs"].includes(t);this.midiRow.style.display=e?"flex":"none",this.mathParamsContainer.style.display=s?"flex":"none",s&&this.updateMathParams(t),this.easeRow.style.display="flex",this.behaviourRow.style.display="flex",i?(this.currentMidiId=null,u.audio.ctx.state==="suspended"&&u.audio.setInput("microphone")):e||(this.currentMidiId=null),this.start(),this.signalSelect.value!==t&&(this.signalSelect.value=t)}}setMidiId(t){this.currentMidiId=t,this.midiBtn.textContent=t??"Learn"}setEase(t){this.currentEase=t,this.easeSelect.value!==t&&(this.easeSelect.value=t)}setBehaviour(t){this.currentBehaviour=t,this.behaviourSelect.value!==t&&(this.behaviourSelect.value=t)}start(){!this.rafId&&this.currentSignalType&&this.loop()}stop(){this.rafId&&(cancelAnimationFrame(this.rafId),this.rafId=null)}save(){return{type:this.currentSignalType,midiId:this.currentMidiId,ease:this.currentEase,behaviour:this.currentBehaviour}}load(t){t&&(this.setSignalType(t.type),this.setMidiId(t.midiId||null),this.setEase(t.ease||"linear"),this.setBehaviour(t.behaviour||"forward"))}reset(){this.setSignalType("none"),this.setEase("linear"),this.setBehaviour("forward"),this.setMidiId(null)}}class H extends u{constructor(t,e,s={}){super(t,e,s),this.pingPongDirection=1,this.min=0,this.max=100,this.initialOptions=s,this.min=s.min??0,this.max=s.max??100;const i=r("details",{className:"cp-controller-details"}),a=r("summary",{className:"cp-controller-summary"});this.input=r("input",{type:"range",className:"cp-input-range",step:s.step??"any"}),s.min!==void 0&&(this.input.min=String(s.min)),s.max!==void 0&&(this.input.max=String(s.max)),this.input.value=String(this.value),this.display=r("span",{className:"cp-value-display"},[String(this.value.toFixed(1))]),this.input.addEventListener("input",()=>{const l=parseFloat(this.input.value);isNaN(l)||(this.setValue(l),this.display.textContent=String(l.toFixed(1)))}),this.input.addEventListener("click",l=>{l.stopPropagation()});const o=r("div",{className:"cp-controller-summary-content"});o.appendChild(this.input),o.appendChild(this.display),a.appendChild(o),i.appendChild(a);const c=r("div",{className:"cp-number-settings"}),p=this.createSetting("min",s.min,l=>this.setMin(l));this.minInput=p.input,c.appendChild(p.row);const h=this.createSetting("max",s.max,l=>this.setMax(l));this.maxInput=h.input,c.appendChild(h.row);const m=this.createSetting("step",s.step,l=>this.setStep(l));this.stepInput=m.input,c.appendChild(m.row);const f=r("hr",{className:"cp-separator"});c.appendChild(f),this.signalHandler=new U({container:c,onChange:(l,g)=>this.applySignal(l,g)}),i.appendChild(c),this.appendWidget(i)}setMin(t){typeof t=="number"&&(t=String(t)),t===""||isNaN(parseFloat(t))?this.input.removeAttribute("min"):(this.input.min=t,this.min=parseFloat(t)),this.minInput&&this.minInput.value!==t&&(this.minInput.value=t)}setMax(t){typeof t=="number"&&(t=String(t)),t===""||isNaN(parseFloat(t))?this.input.removeAttribute("max"):(this.input.max=t,this.max=parseFloat(t)),this.maxInput&&this.maxInput.value!==t&&(this.maxInput.value=t)}setStep(t){t===void 0&&(t=""),typeof t=="number"&&(t=String(t)),t===""||t==="any"||isNaN(parseFloat(t))?this.input.step="any":this.input.step=t,this.stepInput&&(t==="any"||t===""?this.stepInput.value="":this.stepInput.value!==t&&(this.stepInput.value=t))}applySignal(t,e){const s=this.max-this.min;let i;if(e==="forward")i=this.min+t*s;else if(e==="backward")i=this.max-t*s;else{const a=t*(s*.01);i=this.value,e==="loopForward"?(i+=a,i>this.max&&(i=this.min+(i-this.min)%s)):e==="loopBackward"?(i-=a,i<this.min&&(i=this.max-(this.max-i)%s)):e==="pingpong"&&(i+=a*this.pingPongDirection,i>=this.max?(i=this.max,this.pingPongDirection=-1):i<=this.min&&(i=this.min,this.pingPongDirection=1))}i=this.roundToStep(i),this.setValue(i),this.input.value=String(i),this.display.textContent=String(i.toFixed(1))}roundToStep(t){const e=this.input.step;if(e==="any"||e===""||isNaN(parseFloat(e)))return t;const s=parseFloat(e),i=this.min;return i+Math.round((t-i)/s)*s}createSetting(t,e,s){const i=r("div",{className:"cp-setting-row"}),a=r("label",{className:"cp-setting-label"},[t]),o=r("input",{type:"number",className:"cp-input-number cp-input-small",step:"any"});return e!==void 0&&(o.value=String(e)),o.addEventListener("input",()=>s(o.value)),i.appendChild(a),i.appendChild(o),{row:i,input:o}}updateDisplay(){this.input.value=String(this.value),this.display.textContent=String(this.value.toFixed(1))}save(){return{value:this.value,settings:{min:this.min,max:this.max,step:this.input.step,signal:this.signalHandler.save()}}}load(t){if(typeof t=="number")this.setValue(t),this.resetSettings();else if(typeof t=="object"&&t!==null&&"value"in t){const e=t.settings||{};e.min!==void 0?this.setMin(e.min):this.setMin(this.initialOptions.min!==void 0?this.initialOptions.min:""),e.max!==void 0?this.setMax(e.max):this.setMax(this.initialOptions.max!==void 0?this.initialOptions.max:""),e.step!==void 0?this.setStep(e.step):this.setStep(this.initialOptions.step);let s=t.value;!isNaN(this.min)&&s<this.min&&(s=this.min),!isNaN(this.max)&&s>this.max&&(s=this.max),this.setValue(s),this.signalHandler?.load(e.signal)}}reset(){this.setValue(this.initialValue),this.resetSettings()}resetSettings(){this.setMin(this.initialOptions.min!==void 0?this.initialOptions.min:""),this.setMax(this.initialOptions.max!==void 0?this.initialOptions.max:""),this.setStep(this.initialOptions.step),this.signalHandler?.reset()}}class W extends u{constructor(t,e,s){super(t,e,s),this.optionValues=[],this.select=r("select",{className:"cp-select"}),this.optionValues=s.options||[],this.optionValues.forEach((i,a)=>{const o=r("option",{value:String(a)},[String(i)]);this.select.appendChild(o)}),this.updateDisplay(),this.select.addEventListener("change",()=>{const i=parseInt(this.select.value),a=this.optionValues[i];this.setValue(a)}),this.appendWidget(this.select)}setOptions(t){this.select.innerHTML="",this.optionValues=t,this.optionValues.forEach((e,s)=>{const i=r("option",{value:String(s)},[String(e)]);this.select.appendChild(i)}),this.updateDisplay(),this.select.value===""&&this.optionValues.length>0&&this.setValue(this.optionValues[0])}updateDisplay(){const t=this.optionValues.indexOf(this.value);t!==-1&&(this.select.value=String(t))}}class _ extends u{constructor(t,e,s={}){const i={action:e};super(i,"action",s);const a=s.label??t;this.button=r("button",{className:"cp-button"},[String(a)]),this.button.addEventListener("click",()=>{const o=this.value;typeof o=="function"&&o(),this.emitChange(o)}),this.appendWidget(this.button)}updateDisplay(){}}class J extends u{constructor(t,e,s={}){super(t,e,s),this.input=r("input",{type:"checkbox",className:"cp-checkbox"}),this.input.checked=this.value,this.input.addEventListener("change",()=>{this.setValue(this.input.checked)}),this.appendWidget(this.input)}updateDisplay(){this.input.checked=this.value}}class G extends u{constructor(t,e,s){super(t,e,s),this.buttons=[],this.optionValues=[],this.container=r("div",{className:"cp-radios"}),this.optionValues=s.options||[],this.optionValues.forEach(i=>{const a=r("button",{className:"cp-button cp-radio"},[String(i)]);a.addEventListener("click",()=>{this.setValue(i)}),this.container.appendChild(a),this.buttons.push(a)}),this.updateDisplay(),this.appendWidget(this.container)}updateDisplay(){const t=this.value;this.buttons.forEach((e,s)=>{this.optionValues[s]===t?e.setAttribute("data-active","true"):e.removeAttribute("data-active")})}}class Z extends u{constructor(t,e,s={}){super(t,e,s),this.input=r("input",{type:"color",className:"cp-input-color",value:this.value||"#000000"}),this.appendWidget(this.input),this.input.addEventListener("input",i=>{const a=i.target;this.setValue(a.value)}),this.updateDisplay()}updateDisplay(){this.input.value=this.value}}function K(n){const t=/^#?([a-f\d])([a-f\d])([a-f\d])$/i;n=n.replace(t,(s,i,a,o)=>i+i+a+a+o+o);const e=/^#?([a-f\d]{2})([a-f\d]{2})([a-f\d]{2})$/i.exec(n);return e?[parseInt(e[1],16),parseInt(e[2],16),parseInt(e[3],16)]:[0,0,0]}function nt(n,t,e){return"#"+((1<<24)+(Math.round(n)<<16)+(Math.round(t)<<8)+Math.round(e)).toString(16).slice(1)}function I(n){const t=n/255;return t<=.04045?t/12.92:Math.pow((t+.055)/1.055,2.4)}function A(n){return n<=.0031308?n*12.92*255:(1.055*Math.pow(n,1/2.4)-.055)*255}function at(n,t,e){const[s,i,a]=K(n),[o,c,p]=K(t),h=I(s),m=I(i),f=I(a),l=I(o),g=I(c),y=I(p),v=h+e*(l-h),T=m+e*(g-m),S=f+e*(y-f),C=A(v),b=A(T),w=A(S);return nt(C,b,w)}class Y extends u{constructor(t,e,s={}){super(t,e,s),this.stops=[],this.pingPongDirection=1,this.animationT=0,this.manualPosition=0,this.initialOptions=s,this.stops=s.stops||[{color:"#000000",position:0},{color:"#ffffff",position:1}],this.sortStops();const i=r("details",{className:"cp-controller-details"}),a=r("summary",{className:"cp-controller-summary"}),o=r("div",{className:"cp-controller-summary-content"});this.displayColor=r("div",{className:"cp-color-swatch",style:`background-color: ${this.value}`}),this.displayText=r("span",{className:"cp-value-display"},[String(this.value)]),o.appendChild(this.displayColor),o.appendChild(this.displayText),a.appendChild(o),i.appendChild(a);const c=r("div",{className:"cp-number-settings"});this.stopsContainer=r("div",{className:"cp-stops-container"}),this.renderStops(),c.appendChild(this.stopsContainer);const p=r("button",{className:"cp-button"},["+ Add Stop"]);p.addEventListener("click",()=>{this.stops.push({color:"#ffffff",position:.5}),this.sortStops(),this.renderStops(),this.updateOutput()}),c.appendChild(p);const h=r("hr",{className:"cp-separator"});c.appendChild(h),this.signalHandler=new U({container:c,onChange:(m,f)=>this.applySignal(m,f)}),i.appendChild(c),this.appendWidget(i),this.updateOutput(0)}sortStops(){this.stops.sort((t,e)=>t.position-e.position)}renderStops(){this.stopsContainer.innerHTML="",this.stops.forEach((t,e)=>{const s=r("div",{className:"cp-setting-row"}),i=r("input",{type:"color",className:"cp-input-color",value:t.color});i.addEventListener("input",c=>{t.color=c.target.value,this.updateOutput()});const a=r("input",{type:"number",className:"cp-input-number cp-input-small",min:"0",max:"1",step:"0.01",value:String(t.position)});a.addEventListener("change",c=>{let p=parseFloat(c.target.value);isNaN(p)&&(p=0),t.position=Math.max(0,Math.min(1,p)),this.sortStops(),this.renderStops(),this.updateOutput()});const o=F(()=>{this.stops.splice(e,1),this.renderStops(),this.updateOutput()});s.appendChild(i),s.appendChild(a),s.appendChild(o),this.stopsContainer.appendChild(s)})}updateOutput(t=this.manualPosition){if(this.stops.length===0)return;if(this.stops.length===1){this.setValue(this.stops[0].color),this.updateDisplay();return}let e="#000000";if(t=Math.max(0,Math.min(1,t)),t<=this.stops[0].position)e=this.stops[0].color;else if(t>=this.stops[this.stops.length-1].position)e=this.stops[this.stops.length-1].color;else for(let s=0;s<this.stops.length-1;s++){const i=this.stops[s],a=this.stops[s+1];if(t>=i.position&&t<=a.position){const o=a.position-i.position,c=o===0?0:(t-i.position)/o;e=at(i.color,a.color,c);break}}this.setValue(e),this.updateDisplay()}updateDisplay(){this.displayColor&&(this.displayColor.style.backgroundColor=this.value),this.displayText&&(this.displayText.textContent=this.value)}applySignal(t,e){let s=t;if(e==="forward")s=t;else if(e==="backward")s=1-t;else{const i=t*.05;e==="loopForward"?(this.animationT=(this.animationT+i)%1,s=this.animationT):e==="loopBackward"?(this.animationT=(this.animationT-i+1)%1,s=this.animationT):e==="pingpong"&&(this.animationT+=i*this.pingPongDirection,this.animationT>=1?(this.animationT=1,this.pingPongDirection=-1):this.animationT<=0&&(this.animationT=0,this.pingPongDirection=1),s=this.animationT)}this.updateOutput(s),this.manualPosition=s}save(){return{stops:this.stops,settings:{signal:this.signalHandler.save()}}}load(t){t&&t.stops&&(this.stops=t.stops,this.sortStops(),this.renderStops()),t&&t.settings&&this.signalHandler?.load(t.settings.signal)}reset(){this.stops=this.initialOptions.stops||[{color:"#000000",position:0},{color:"#ffffff",position:1}],this.sortStops(),this.renderStops(),this.signalHandler?.reset(),this.updateOutput(0)}}class Q extends u{constructor(t,e,s={}){super(t,e,s),this.items=[],this.initialOptions=s,this.itemType=s.itemType||"string",this.items=this.parseValue(this.value);const i=r("details",{className:"cp-controller-details"}),a=r("summary",{className:"cp-controller-summary"}),o=r("div",{className:"cp-controller-summary-content"}),c=r("span",{className:"cp-value-display"},[`${this.items.length} items`]);o.appendChild(c),a.appendChild(o),i.appendChild(a);const p=r("div",{className:"cp-number-settings"});this.itemsContainer=r("div",{className:"cp-stops-container"}),this.renderItems(),p.appendChild(this.itemsContainer);const h=r("button",{className:"cp-button cp-input-small",style:"margin-top: 8px; width: 100%;"},["+ Add Item"]);h.addEventListener("click",()=>{this.addItem()}),p.appendChild(h),i.appendChild(p),this.appendWidget(i)}parseValue(t){return!t||t.trim()===""?[]:t.split(",").map(e=>e.trim())}serializeValue(){return this.items.join(",")}getDefaultItemValue(){switch(this.itemType){case"color":return"#ffffff";case"number":return"0";default:return""}}addItem(t){const e=t!==void 0?t:this.getDefaultItemValue();this.items.push(e),this.renderItems(),this.updateValue()}updateValue(){const t=this.serializeValue();this.setValue(t),this.updateSummary()}updateSummary(){const t=this.domElement.querySelector(".cp-value-display");t&&(t.textContent=`${this.items.length} items`)}renderItems(){this.itemsContainer.innerHTML="",this.items.forEach((t,e)=>{const s=r("div",{className:"cp-setting-row"});let i;this.itemType==="color"?i=r("input",{type:"color",className:"cp-input-color",value:t}):this.itemType==="number"?i=r("input",{type:"number",className:"cp-input-number cp-input-small",step:"any",value:t}):i=r("input",{type:"text",className:"cp-input-number cp-input-small",value:t}),i.addEventListener("input",o=>{this.items[e]=o.target.value,this.updateValue()});const a=F(()=>{this.items.splice(e,1),this.renderItems(),this.updateValue()});s.appendChild(i),s.appendChild(a),this.itemsContainer.appendChild(s)})}updateDisplay(){}save(){return[...this.items]}load(t){Array.isArray(t)?this.items=[...t]:typeof t=="string"&&(this.items=this.parseValue(t)),this.renderItems(),this.updateValue()}reset(){const t=this.initialValue||"";this.items=this.parseValue(t),this.renderItems(),this.updateValue()}}class ot{constructor(){this.frames=0,this.pollingInterval=1e3,this.prevTime=performance.now(),this.render=()=>{this.frames++;const t=performance.now();if(t>=this.prevTime+this.pollingInterval){const e=Math.round(this.frames*1e3/(t-this.prevTime));let s="";const i=performance.memory;i&&(s=` / ${Math.round(i.usedJSHeapSize/1048576)}MB`),this.domElement.textContent=`${e} FPS${s}`,this.prevTime=t,this.frames=0}this.rafId=requestAnimationFrame(this.render)},this.domElement=r("span",{className:"cp-stats"}),this.rafId=requestAnimationFrame(this.render)}destroy(){cancelAnimationFrame(this.rafId)}}class P{constructor(){this.controllers=[],this.folders=[]}addNumber(t,e,s={}){const i=new H(t,e,s);return this.contentElement.appendChild(i.domElement),this.controllers.push(i),i}addSelect(t,e,s={}){const i=new W(t,e,s);return this.contentElement.appendChild(i.domElement),this.controllers.push(i),i}addBoolean(t,e,s={}){const i=new J(t,e,s);return this.contentElement.appendChild(i.domElement),this.controllers.push(i),i}addButton(t,e,s={}){const i=new _(t,e,s);return this.contentElement.appendChild(i.domElement),this.controllers.push(i),i}addRadio(t,e,s={}){const i=new G(t,e,s);return this.contentElement.appendChild(i.domElement),this.controllers.push(i),i}addColor(t,e,s={}){const i=new Z(t,e,s);return this.contentElement.appendChild(i.domElement),this.controllers.push(i),i}addGradient(t,e,s={}){const i=new Y(t,e,s);return this.contentElement.appendChild(i.domElement),this.controllers.push(i),i}addArray(t,e,s={}){const i=new Q(t,e,s);return this.contentElement.appendChild(i.domElement),this.controllers.push(i),i}addFolder(t){const e=new X(t);return this.contentElement.appendChild(e.domElement),this.folders.push(e),e}save(){const t={controllers:{},folders:{}};for(const e of this.controllers)typeof e.value!="function"&&(t.controllers[e.key]=e.save());for(const e of this.folders)t.folders[e.title]=e.save();return t}load(t){if(!t){this.reset();return}for(const e of this.controllers)if(typeof e.value!="function")if(t.controllers&&e.key in t.controllers){const s=t.controllers[e.key];s!==void 0&&e.load(s)}else e.reset();for(const e of this.folders){const s=t.folders?t.folders[e.title]:void 0;e.load(s)}}reset(){for(const t of this.controllers)typeof t.value!="function"&&t.reset();for(const t of this.folders)t.reset()}}class X extends P{constructor(t){super(),this.title=t,this.domElement=r("details",{className:"cp-folder",open:!0}),this.summaryElement=r("summary",{className:"cp-summary"},[t]),this.domElement.appendChild(this.summaryElement),this.contentElement=r("div",{className:"cp-content cp-folder-content"}),this.domElement.appendChild(this.contentElement),this.domElement.appendChild(r("hr",{className:"cp-separator"}))}}class rt extends P{constructor(t,e={}){super(),et(),this.domElement=r("details",{className:"cp-root",open:!0}),this.summaryElement=r("summary",{className:"cp-summary cp-summary-root"}),this.domElement.appendChild(this.summaryElement);const s=r("span",{},[e.title||"ControlPanel"]);this.summaryElement.appendChild(s),this.stats=new ot,this.summaryElement.appendChild(this.stats.domElement),this.contentElement=r("div",{className:"cp-content"}),this.domElement.appendChild(this.contentElement);const i=this.addFolder("_Signals"),a={audioInput:null,fftSize:2048};i.addRadio(a,"audioInput",{label:"Audio Signal",options:["microphone","browser"]}).onChange(l=>{x.setInput(l)}),i.addSelect(a,"fftSize",{label:"FFT Size",options:[256,512,1024,2048]}).onChange(l=>{x.setFFTSize(l)}),i.addNumber(x,"smoothingTimeConstant",{min:0,max:.99,step:.01,label:"Smoothing"}).onChange(l=>{x.analyser.smoothingTimeConstant=l}),i.addNumber(x,"spectrumBoost",{min:1,max:5,step:.1,label:"Compression"}),t?t.appendChild(this.domElement):document.body.appendChild(this.domElement);const o=e.title||"ControlPanel";this.presetStoragePrefix=`cp-presets-${o}-`;const c=this.addFolder("_User Presets"),p=()=>{const l=["Default"];if(typeof localStorage>"u")return l;for(let g=0;g<localStorage.length;g++){const y=localStorage.key(g);if(y&&y.startsWith(this.presetStoragePrefix)){const v=y.substring(this.presetStoragePrefix.length);v!=="Default"&&!l.includes(v)&&l.push(v)}}return l.sort()},h={selected:"Default",save:()=>{const l=prompt("Preset Name:",h.selected);if(l){if(l==="Default"){alert("Cannot overwrite Default preset");return}const g=this.presetStoragePrefix+l;this.saveToLocalStorage(g);const y=p();f.setOptions(y),h.selected=l,f.setValue(l)}},load:()=>{const l=h.selected,g=this.presetStoragePrefix+l;this.loadFromLocalStorage(g),h.selected=l,f.setValue(l)},delete:()=>{if(h.selected==="Default"){alert("Cannot delete Default preset");return}if(confirm(`Delete preset "${h.selected}"?`)){const l=this.presetStoragePrefix+h.selected;localStorage.removeItem(l);const g=p();f.setOptions(g),h.selected="Default",f.setValue("Default"),this.reset()}},export:()=>{const l=this.save(),g=L=>{const M={controllers:{},folders:{}};for(const[k,V]of Object.entries(L.controllers))k.startsWith("_")||(M.controllers[k]=V);for(const[k,V]of Object.entries(L.folders))k.startsWith("_")||(M.folders[k]=g(V));return M},y=g(l),v={_presetName:h.selected||"CustomPreset",_exportDate:new Date().toISOString(),_instructions:"To add as factory preset: Copy 'controllers' and 'folders' fields into the presets.json file",...y},T=JSON.stringify(v,null,2),S=new Blob([T],{type:"application/json"}),C=URL.createObjectURL(S),b=document.createElement("a");b.href=C;const w=new Date().toISOString().split("T")[0],tt=h.selected.replace(/[^a-z0-9]/gi,"-").toLowerCase();b.download=`${o.toLowerCase()}-preset-${tt}-${w}.json`,document.body.appendChild(b),b.click(),document.body.removeChild(b),URL.revokeObjectURL(C)},import:()=>{const l=document.createElement("input");l.type="file",l.accept=".json",l.onchange=g=>{const y=g.target.files?.[0];if(!y)return;const v=new FileReader;v.onload=T=>{try{const S=T.target?.result,C=JSON.parse(S),b={controllers:C.controllers||{},folders:C.folders||{}};if(!b.controllers||!b.folders){alert("Invalid preset file: missing 'controllers' or 'folders'");return}this.load(b);const w=C._presetName||"ImportedPreset";if(confirm(`Preset loaded! Save as "${w}" to User Presets?`)){const L=this.presetStoragePrefix+w;this.saveToLocalStorage(L);const M=p();f.setOptions(M),h.selected=w,f.setValue(w)}}catch(S){alert(`Failed to import preset: ${S instanceof Error?S.message:"Invalid JSON"}`),console.error("Import error:",S)}},v.readAsText(y)},l.click()}},m=p(),f=c.addSelect(h,"selected",{label:"Preset",options:m});c.addButton("Load",()=>h.load()),c.addButton("Save / New",()=>h.save()),c.addButton("Delete",()=>h.delete()),c.addButton("Export JSON",()=>h.export()),c.addButton("Import JSON",()=>h.import())}saveToLocalStorage(t){const e=this.save();try{localStorage.setItem(t,JSON.stringify(e))}catch(s){console.warn("ControlPanel: Failed to save to localStorage",s)}}loadFromLocalStorage(t){try{const e=localStorage.getItem(t);if(e){const s=JSON.parse(e);this.load(s)}}catch(e){console.warn("ControlPanel: Failed to load from localStorage",e)}}saveDefaultPreset(){const t=this.presetStoragePrefix+"Default";this.save(),this.saveToLocalStorage(t)}destroy(){this.stats.destroy(),this.domElement.remove(),this.controllers=[],this.folders=[]}}d.ArrayController=Q,d.AudioSignals=B,d.BooleanController=J,d.ButtonController=_,d.ColorController=Z,d.ControlPanel=rt,d.ControlPanelContainer=P,d.Controller=u,d.Folder=X,d.GradientController=Y,d.MathSignals=R,d.MidiSignals=q,d.NumberController=H,d.RadioController=G,d.SelectController=W,d.audioSignals=x,d.mathSignals=j,d.midiSignals=z,Object.defineProperty(d,Symbol.toStringTag,{value:"Module"})}));
@@ -10,16 +10,16 @@ export declare class AudioSignals {
10
10
  smoothingTimeConstant: number;
11
11
  spectrumBoost: number;
12
12
  levels: {
13
+ volume: number;
13
14
  bass: number;
14
15
  mids: number;
15
16
  highs: number;
16
- volume: number;
17
17
  };
18
18
  peaks: {
19
+ volume: number;
19
20
  bass: number;
20
21
  mids: number;
21
22
  highs: number;
22
- volume: number;
23
23
  };
24
24
  private _isAnalyzing;
25
25
  constructor();
@@ -0,0 +1,28 @@
1
+ export type MathSignalType = "constant" | "sine" | "sawtooth" | "triangle" | "square" | "random";
2
+ export interface MathSignalConfig {
3
+ frequency: number;
4
+ amplitude: number;
5
+ offset: number;
6
+ phase: number;
7
+ }
8
+ export declare class MathSignals {
9
+ private startTime;
10
+ private configs;
11
+ private lastRandomUpdateTime;
12
+ private currentRandomValue;
13
+ constructor();
14
+ setConfig(type: MathSignalType, config: Partial<MathSignalConfig>): void;
15
+ getConfig(type: MathSignalType): MathSignalConfig;
16
+ private getTime;
17
+ private normalizeOutput;
18
+ constant(config?: Partial<MathSignalConfig>): number;
19
+ sine(config?: Partial<MathSignalConfig>): number;
20
+ sawtooth(config?: Partial<MathSignalConfig>): number;
21
+ triangle(config?: Partial<MathSignalConfig>): number;
22
+ square(config?: Partial<MathSignalConfig>): number;
23
+ random(config?: Partial<MathSignalConfig>): number;
24
+ getSignal(type: MathSignalType): () => number;
25
+ reset(): void;
26
+ }
27
+ export declare const mathSignals: MathSignals;
28
+ //# sourceMappingURL=MathSignals.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"MathSignals.d.ts","sourceRoot":"","sources":["../../src/signals/MathSignals.ts"],"names":[],"mappings":"AAAA,MAAM,MAAM,cAAc,GACtB,UAAU,GACV,MAAM,GACN,UAAU,GACV,UAAU,GACV,QAAQ,GACR,QAAQ,CAAC;AAEb,MAAM,WAAW,gBAAgB;IAC/B,SAAS,EAAE,MAAM,CAAC;IAClB,SAAS,EAAE,MAAM,CAAC;IAClB,MAAM,EAAE,MAAM,CAAC;IACf,KAAK,EAAE,MAAM,CAAC;CACf;AAED,qBAAa,WAAW;IACtB,OAAO,CAAC,SAAS,CAAS;IAC1B,OAAO,CAAC,OAAO,CAAoD;IACnE,OAAO,CAAC,oBAAoB,CAAK;IACjC,OAAO,CAAC,kBAAkB,CAAK;;IA4C/B,SAAS,CAAC,IAAI,EAAE,cAAc,EAAE,MAAM,EAAE,OAAO,CAAC,gBAAgB,CAAC;IAKjE,SAAS,CAAC,IAAI,EAAE,cAAc,GAAG,gBAAgB;IAIjD,OAAO,CAAC,OAAO;IAIf,OAAO,CAAC,eAAe;IAKvB,QAAQ,CAAC,MAAM,CAAC,EAAE,OAAO,CAAC,gBAAgB,CAAC,GAAG,MAAM;IASpD,IAAI,CAAC,MAAM,CAAC,EAAE,OAAO,CAAC,gBAAgB,CAAC,GAAG,MAAM;IAYhD,QAAQ,CAAC,MAAM,CAAC,EAAE,OAAO,CAAC,gBAAgB,CAAC,GAAG,MAAM;IAUpD,QAAQ,CAAC,MAAM,CAAC,EAAE,OAAO,CAAC,gBAAgB,CAAC,GAAG,MAAM;IAYpD,MAAM,CAAC,MAAM,CAAC,EAAE,OAAO,CAAC,gBAAgB,CAAC,GAAG,MAAM;IAWlD,MAAM,CAAC,MAAM,CAAC,EAAE,OAAO,CAAC,gBAAgB,CAAC,GAAG,MAAM;IAelD,SAAS,CAAC,IAAI,EAAE,cAAc;IAiB9B,KAAK;CAKN;AAED,eAAO,MAAM,WAAW,aAAoB,CAAC"}
@@ -19,6 +19,7 @@ export declare class SignalHandler {
19
19
  private signalSelect;
20
20
  private midiRow;
21
21
  private midiBtn;
22
+ private mathParamsContainer;
22
23
  private easeRow;
23
24
  private easeSelect;
24
25
  private behaviourRow;
@@ -27,6 +28,8 @@ export declare class SignalHandler {
27
28
  constructor(options: SignalHandlerOptions);
28
29
  private setupControllers;
29
30
  private createSettingSelect;
31
+ private createNumberInput;
32
+ private updateMathParams;
30
33
  setSignalType(val: string | null): void;
31
34
  setMidiId(id: string | null): void;
32
35
  setEase(val: Ease): void;
@@ -1 +1 @@
1
- {"version":3,"file":"SignalHandler.d.ts","sourceRoot":"","sources":["../../src/signals/SignalHandler.ts"],"names":[],"mappings":"AAEA,OAAO,EAAW,KAAK,IAAI,EAAE,MAAM,kBAAkB,CAAC;AAEtD,MAAM,MAAM,eAAe,GACvB,SAAS,GACT,UAAU,GACV,aAAa,GACb,cAAc,GACd,UAAU,CAAC;AAEf,MAAM,WAAW,oBAAoB;IACnC,SAAS,EAAE,WAAW,CAAC;IACvB,QAAQ,EAAE,CAAC,UAAU,EAAE,MAAM,EAAE,SAAS,EAAE,eAAe,KAAK,IAAI,CAAC;CACpE;AAED,MAAM,WAAW,kBAAkB;IACjC,IAAI,EAAE,MAAM,GAAG,IAAI,CAAC;IACpB,MAAM,EAAE,MAAM,GAAG,IAAI,CAAC;IACtB,IAAI,EAAE,IAAI,CAAC;IACX,SAAS,EAAE,eAAe,CAAC;CAC5B;AAED,qBAAa,aAAa;IAExB,OAAO,CAAC,KAAK,CAAuB;IACpC,OAAO,CAAC,iBAAiB,CAMT;IAChB,OAAO,CAAC,aAAa,CAAuB;IAC5C,OAAO,CAAC,WAAW,CAAkB;IACrC,OAAO,CAAC,gBAAgB,CAA8B;IAGtD,OAAO,CAAC,YAAY,CAAqB;IACzC,OAAO,CAAC,OAAO,CAAe;IAC9B,OAAO,CAAC,OAAO,CAAqB;IACpC,OAAO,CAAC,OAAO,CAAe;IAC9B,OAAO,CAAC,UAAU,CAAqB;IACvC,OAAO,CAAC,YAAY,CAAe;IACnC,OAAO,CAAC,eAAe,CAAqB;IAG5C,OAAO,CAAC,QAAQ,CAA2D;gBAE/D,OAAO,EAAE,oBAAoB;IAKzC,OAAO,CAAC,gBAAgB;IAgExB,OAAO,CAAC,mBAAmB;IAyB3B,aAAa,CAAC,GAAG,EAAE,MAAM,GAAG,IAAI;IA4BhC,SAAS,CAAC,EAAE,EAAE,MAAM,GAAG,IAAI;IAK3B,OAAO,CAAC,GAAG,EAAE,IAAI;IAKjB,YAAY,CAAC,GAAG,EAAE,eAAe;IAKjC,OAAO,CAAC,IAAI,CAkBV;IAEF,KAAK;IAML,IAAI;IAOJ,IAAI,IAAI,kBAAkB;IAS1B,IAAI,CAAC,IAAI,EAAE,kBAAkB,GAAG,IAAI;IASpC,KAAK;CAMN"}
1
+ {"version":3,"file":"SignalHandler.d.ts","sourceRoot":"","sources":["../../src/signals/SignalHandler.ts"],"names":[],"mappings":"AAEA,OAAO,EAAW,KAAK,IAAI,EAAE,MAAM,kBAAkB,CAAC;AAGtD,MAAM,MAAM,eAAe,GACvB,SAAS,GACT,UAAU,GACV,aAAa,GACb,cAAc,GACd,UAAU,CAAC;AAEf,MAAM,WAAW,oBAAoB;IACnC,SAAS,EAAE,WAAW,CAAC;IACvB,QAAQ,EAAE,CAAC,UAAU,EAAE,MAAM,EAAE,SAAS,EAAE,eAAe,KAAK,IAAI,CAAC;CACpE;AAED,MAAM,WAAW,kBAAkB;IACjC,IAAI,EAAE,MAAM,GAAG,IAAI,CAAC;IACpB,MAAM,EAAE,MAAM,GAAG,IAAI,CAAC;IACtB,IAAI,EAAE,IAAI,CAAC;IACX,SAAS,EAAE,eAAe,CAAC;CAC5B;AAED,qBAAa,aAAa;IAExB,OAAO,CAAC,KAAK,CAAuB;IACpC,OAAO,CAAC,iBAAiB,CAYT;IAChB,OAAO,CAAC,aAAa,CAAuB;IAC5C,OAAO,CAAC,WAAW,CAAkB;IACrC,OAAO,CAAC,gBAAgB,CAA8B;IAGtD,OAAO,CAAC,YAAY,CAAqB;IACzC,OAAO,CAAC,OAAO,CAAe;IAC9B,OAAO,CAAC,OAAO,CAAqB;IACpC,OAAO,CAAC,mBAAmB,CAAe;IAC1C,OAAO,CAAC,OAAO,CAAe;IAC9B,OAAO,CAAC,UAAU,CAAqB;IACvC,OAAO,CAAC,YAAY,CAAe;IACnC,OAAO,CAAC,eAAe,CAAqB;IAG5C,OAAO,CAAC,QAAQ,CAA2D;gBAE/D,OAAO,EAAE,oBAAoB;IAKzC,OAAO,CAAC,gBAAgB;IAmFxB,OAAO,CAAC,mBAAmB;IAyB3B,OAAO,CAAC,iBAAiB;IAiCzB,OAAO,CAAC,gBAAgB;IAmExB,aAAa,CAAC,GAAG,EAAE,MAAM,GAAG,IAAI;IA6ChC,SAAS,CAAC,EAAE,EAAE,MAAM,GAAG,IAAI;IAK3B,OAAO,CAAC,GAAG,EAAE,IAAI;IAKjB,YAAY,CAAC,GAAG,EAAE,eAAe;IAKjC,OAAO,CAAC,IAAI,CAoCV;IAEF,KAAK;IAML,IAAI;IAOJ,IAAI,IAAI,kBAAkB;IAS1B,IAAI,CAAC,IAAI,EAAE,kBAAkB,GAAG,IAAI;IASpC,KAAK;CAMN"}
@@ -1 +1 @@
1
- {"version":3,"file":"styles.d.ts","sourceRoot":"","sources":["../src/styles.ts"],"names":[],"mappings":"AA6RA,wBAAgB,YAAY,SAS3B"}
1
+ {"version":3,"file":"styles.d.ts","sourceRoot":"","sources":["../src/styles.ts"],"names":[],"mappings":"AA0RA,wBAAgB,YAAY,SAS3B"}
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@digitalmeadow/control-panel",
3
- "version": "1.0.4",
3
+ "version": "1.0.6",
4
4
  "type": "module",
5
5
  "description": "A minimalist, framework-agnostic control panel GUI",
6
6
  "author": "Digital Meadow <inbox@digitalmeadow.studio> (https://digitalmeadow.studio)",