@digitalmeadow/control-panel 1.0.0 → 1.0.2

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.
Files changed (46) hide show
  1. package/README.md +30 -24
  2. package/dist/ControlPanel.d.ts +56 -0
  3. package/dist/ControlPanel.d.ts.map +1 -0
  4. package/dist/controllers/ArrayController.d.ts +24 -0
  5. package/dist/controllers/ArrayController.d.ts.map +1 -0
  6. package/dist/controllers/BooleanController.d.ts +9 -0
  7. package/dist/controllers/BooleanController.d.ts.map +1 -0
  8. package/dist/controllers/ButtonController.d.ts +9 -0
  9. package/dist/controllers/ButtonController.d.ts.map +1 -0
  10. package/dist/controllers/ColorController.d.ts +9 -0
  11. package/dist/controllers/ColorController.d.ts.map +1 -0
  12. package/dist/controllers/Controller.d.ts +27 -0
  13. package/dist/controllers/Controller.d.ts.map +1 -0
  14. package/dist/controllers/GradientController.d.ts +37 -0
  15. package/dist/controllers/GradientController.d.ts.map +1 -0
  16. package/dist/controllers/NumberController.d.ts +42 -0
  17. package/dist/controllers/NumberController.d.ts.map +1 -0
  18. package/dist/controllers/RadioController.d.ts +12 -0
  19. package/dist/controllers/RadioController.d.ts.map +1 -0
  20. package/dist/controllers/SelectController.d.ts +12 -0
  21. package/dist/controllers/SelectController.d.ts.map +1 -0
  22. package/dist/index.d.ts +13 -0
  23. package/dist/index.d.ts.map +1 -0
  24. package/dist/{control-panel.js → index.js} +123 -119
  25. package/dist/index.umd.cjs +1 -0
  26. package/dist/signals/AudioSignals.d.ts +35 -0
  27. package/dist/signals/AudioSignals.d.ts.map +1 -0
  28. package/dist/signals/MidiSignals.d.ts +18 -0
  29. package/dist/signals/MidiSignals.d.ts.map +1 -0
  30. package/dist/signals/SignalHandler.d.ts +41 -0
  31. package/dist/signals/SignalHandler.d.ts.map +1 -0
  32. package/dist/stats.d.ts +11 -0
  33. package/dist/stats.d.ts.map +1 -0
  34. package/dist/utils/color.d.ts +2 -0
  35. package/dist/utils/color.d.ts.map +1 -0
  36. package/dist/utils/dom.d.ts +3 -0
  37. package/dist/utils/dom.d.ts.map +1 -0
  38. package/dist/utils/easings.d.ts +3 -0
  39. package/dist/utils/easings.d.ts.map +1 -0
  40. package/dist/utils/math.d.ts +3 -0
  41. package/dist/utils/math.d.ts.map +1 -0
  42. package/dist/utils/strings.d.ts +2 -0
  43. package/dist/utils/strings.d.ts.map +1 -0
  44. package/package.json +8 -6
  45. package/dist/control-panel.umd.cjs +0 -1
  46. /package/dist/{control-panel.css → index.css} +0 -0
@@ -34,8 +34,8 @@ function j(n, t, e) {
34
34
  return e[e.length - 1];
35
35
  if (i === 0 && n < t[0])
36
36
  return e[0];
37
- const s = t[i], a = t[i + 1], o = e[i], l = e[i + 1];
38
- return (n - s) / (a - s) * (l - o) + o;
37
+ const s = t[i], a = t[i + 1], o = e[i], h = e[i + 1];
38
+ return (n - s) / (a - s) * (h - o) + o;
39
39
  }
40
40
  class z {
41
41
  constructor() {
@@ -75,14 +75,14 @@ class z {
75
75
  }
76
76
  update() {
77
77
  if (this.analyser.getByteFrequencyData(this.dataArray), this.analyser.getByteTimeDomainData(this.waveformArray), this.spectrumBoost !== 1) {
78
- const d = this.dataArray.length;
79
- for (let h = 0; h < d; h++) {
80
- const p = 1 + h / d * (this.spectrumBoost - 1);
81
- this.dataArray[h] = Math.min(255, this.dataArray[h] * p);
78
+ const p = this.dataArray.length;
79
+ for (let c = 0; c < p; c++) {
80
+ const u = 1 + c / p * (this.spectrumBoost - 1);
81
+ this.dataArray[c] = Math.min(255, this.dataArray[c] * u);
82
82
  }
83
83
  }
84
- 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]), l = this.getAverage(0, i[1]);
85
- this.processLevel("bass", s), this.processLevel("mids", a), this.processLevel("highs", o), this.processLevel("volume", l);
84
+ 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]), h = this.getAverage(0, i[1]);
85
+ this.processLevel("bass", s), this.processLevel("mids", a), this.processLevel("highs", o), this.processLevel("volume", h);
86
86
  }
87
87
  processLevel(t, e) {
88
88
  this.peaks[t] -= 5e-4, this.peaks[t] = D(this.peaks[t], 0.1, 1), e > this.peaks[t] && (this.peaks[t] = e), this.levels[t] = D(
@@ -131,8 +131,8 @@ class $ {
131
131
  this.values.set(a, o), this.isListening && this.resolveListen && o > 0 && (this.resolveListen(a), this.isListening = !1, this.resolveListen = null, this.listeningCallback && this.listeningCallback());
132
132
  }
133
133
  getIdFromMessage(t) {
134
- const e = t.data, [i, s] = e, a = i & 240, o = t.currentTarget.name || "unknown", l = a === 144 || a === 128 ? "note" : "ctrl", d = o.replace(/[^a-zA-Z0-9]/g, "");
135
- return `${s}_${l}_${d}`;
134
+ const e = t.data, [i, s] = e, a = i & 240, o = t.currentTarget.name || "unknown", h = a === 144 || a === 128 ? "note" : "ctrl", p = o.replace(/[^a-zA-Z0-9]/g, "");
135
+ return `${s}_${h}_${p}`;
136
136
  }
137
137
  normalizeValue(t) {
138
138
  const [e, i, s] = t, a = e & 240;
@@ -150,7 +150,7 @@ class $ {
150
150
  return () => this.values.get(t) ?? 0;
151
151
  }
152
152
  }
153
- const U = new $(), M = class M {
153
+ const q = new $(), M = class M {
154
154
  constructor(t, e, i = {}) {
155
155
  this.changeFns = /* @__PURE__ */ 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" });
156
156
  const s = i.label ?? R(e), a = r("label", { className: "cp-label" }, [
@@ -184,7 +184,7 @@ const U = new $(), M = class M {
184
184
  this.domElement.appendChild(t);
185
185
  }
186
186
  };
187
- M.audio = x, M.midi = U;
187
+ M.audio = x, M.midi = q;
188
188
  let m = M;
189
189
  const O = {
190
190
  linear: (n) => n,
@@ -201,7 +201,7 @@ const O = {
201
201
  sineOut: (n) => Math.sin(n * Math.PI / 2),
202
202
  sineInOut: (n) => -(Math.cos(Math.PI * n) - 1) / 2
203
203
  };
204
- class k {
204
+ class P {
205
205
  constructor(t) {
206
206
  this.rafId = null, this.currentSignalType = null, this.currentMidiId = null, this.currentEase = "linear", this.currentBehaviour = "forward", this.loop = () => {
207
207
  if (this.currentSignalType) {
@@ -259,9 +259,9 @@ class k {
259
259
  ]), o = r("select", {
260
260
  className: "cp-select cp-input-small"
261
261
  });
262
- return e.forEach((l) => {
263
- const d = r("option", { value: l }, [l]);
264
- o.appendChild(d);
262
+ return e.forEach((h) => {
263
+ const p = r("option", { value: h }, [h]);
264
+ o.appendChild(p);
265
265
  }), o.addEventListener("change", () => i(o.value)), s.appendChild(a), s.appendChild(o), { row: s, select: o };
266
266
  }
267
267
  setSignalType(t) {
@@ -303,7 +303,7 @@ class k {
303
303
  this.setSignalType("none"), this.setEase("linear"), this.setBehaviour("forward"), this.setMidiId(null);
304
304
  }
305
305
  }
306
- class q extends m {
306
+ class H extends m {
307
307
  constructor(t, e, i = {}) {
308
308
  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;
309
309
  const s = r("details", {
@@ -322,36 +322,38 @@ class q extends m {
322
322
  },
323
323
  [String(this.value.toFixed(1))]
324
324
  ), this.input.addEventListener("input", () => {
325
- const p = parseFloat(this.input.value);
326
- isNaN(p) || (this.setValue(p), this.display.textContent = String(p.toFixed(1)));
327
- }), this.input.addEventListener("click", (p) => {
328
- p.stopPropagation();
325
+ const l = parseFloat(this.input.value);
326
+ isNaN(l) || (this.setValue(l), this.display.textContent = String(l.toFixed(1)));
327
+ }), this.input.addEventListener("click", (l) => {
328
+ l.stopPropagation();
329
329
  });
330
330
  const o = r("div", {
331
331
  className: "cp-controller-summary-content"
332
332
  });
333
333
  o.appendChild(this.input), o.appendChild(this.display), a.appendChild(o), s.appendChild(a);
334
- const l = r("div", { className: "cp-number-settings" }), d = this.createSetting(
334
+ const h = r("div", { className: "cp-number-settings" }), p = this.createSetting(
335
335
  "min",
336
336
  i.min,
337
- (p) => this.setMin(p)
337
+ (l) => this.setMin(l)
338
338
  );
339
- this.minInput = d.input, l.appendChild(d.row);
340
- const h = this.createSetting(
339
+ this.minInput = p.input, h.appendChild(p.row);
340
+ const c = this.createSetting(
341
341
  "max",
342
342
  i.max,
343
- (p) => this.setMax(p)
343
+ (l) => this.setMax(l)
344
344
  );
345
- this.maxInput = h.input, l.appendChild(h.row);
345
+ this.maxInput = c.input, h.appendChild(c.row);
346
346
  const y = this.createSetting(
347
347
  "step",
348
348
  i.step,
349
- (p) => this.setStep(p)
349
+ (l) => this.setStep(l)
350
350
  );
351
- this.stepInput = y.input, l.appendChild(y.row), this.signalHandler = new k({
352
- container: l,
353
- onChange: (p, c) => this.applySignal(p, c)
354
- }), s.appendChild(l), this.appendWidget(s);
351
+ this.stepInput = y.input, h.appendChild(y.row);
352
+ const u = r("hr", { className: "cp-separator" });
353
+ h.appendChild(u), this.signalHandler = new P({
354
+ container: h,
355
+ onChange: (l, d) => this.applySignal(l, d)
356
+ }), s.appendChild(h), this.appendWidget(s);
355
357
  }
356
358
  // Setters
357
359
  setMin(t) {
@@ -432,7 +434,7 @@ class q extends m {
432
434
  ), this.setStep(this.initialOptions.step), this.signalHandler?.reset();
433
435
  }
434
436
  }
435
- class H extends m {
437
+ class U extends m {
436
438
  constructor(t, e, i) {
437
439
  super(t, e, i), this.optionValues = [], this.select = r("select", { className: "cp-select" }), this.optionValues = i.options || [], this.optionValues.forEach((s, a) => {
438
440
  const o = r("option", { value: String(a) }, [
@@ -485,7 +487,7 @@ class W extends m {
485
487
  this.input.checked = this.value;
486
488
  }
487
489
  }
488
- class G extends m {
490
+ class J extends m {
489
491
  constructor(t, e, i) {
490
492
  super(t, e, i), this.buttons = [], this.optionValues = [], this.container = r("div", { className: "cp-radios" }), this.optionValues = i.options || [], this.optionValues.forEach((s) => {
491
493
  const a = r("button", { className: "cp-button cp-radio" }, [
@@ -503,7 +505,7 @@ class G extends m {
503
505
  });
504
506
  }
505
507
  }
506
- class J extends m {
508
+ class G extends m {
507
509
  constructor(t, e, i = {}) {
508
510
  super(t, e, i), this.input = r("input", {
509
511
  type: "color",
@@ -539,7 +541,7 @@ function A(n) {
539
541
  return n <= 31308e-7 ? n * 12.92 * 255 : (1.055 * Math.pow(n, 1 / 2.4) - 0.055) * 255;
540
542
  }
541
543
  function K(n, t, e) {
542
- const [i, s, a] = F(n), [o, l, d] = F(t), h = w(i), y = w(s), p = w(a), c = w(o), u = w(l), f = w(d), v = h + e * (c - h), I = y + e * (u - y), S = p + e * (f - p), C = A(v), g = A(I), b = A(S);
544
+ const [i, s, a] = F(n), [o, h, p] = F(t), c = w(i), y = w(s), u = w(a), l = w(o), d = w(h), f = w(p), v = c + e * (l - c), I = y + e * (d - y), S = u + e * (f - u), C = A(v), g = A(I), b = A(S);
543
545
  return Z(C, g, b);
544
546
  }
545
547
  class Q extends m {
@@ -563,25 +565,25 @@ class Q extends m {
563
565
  { className: "cp-value-display" },
564
566
  [String(this.value)]
565
567
  ), o.appendChild(this.displayColor), o.appendChild(this.displayText), a.appendChild(o), s.appendChild(a);
566
- const l = r("div", { className: "cp-number-settings" });
568
+ const h = r("div", { className: "cp-number-settings" });
567
569
  this.stopsContainer = r("div", {
568
570
  className: "cp-stops-container"
569
- }), this.renderStops(), l.appendChild(this.stopsContainer);
570
- const d = r(
571
+ }), this.renderStops(), h.appendChild(this.stopsContainer);
572
+ const p = r(
571
573
  "button",
572
574
  {
573
575
  className: "cp-button"
574
576
  },
575
577
  ["+ Add Stop"]
576
578
  );
577
- d.addEventListener("click", () => {
579
+ p.addEventListener("click", () => {
578
580
  this.stops.push({ color: "#ffffff", position: 0.5 }), this.sortStops(), this.renderStops(), this.updateOutput();
579
- }), l.appendChild(d);
580
- const h = r("hr", { className: "cp-separator" });
581
- l.appendChild(h), this.signalHandler = new k({
582
- container: l,
583
- onChange: (y, p) => this.applySignal(y, p)
584
- }), s.appendChild(l), this.appendWidget(s), this.updateOutput(0);
581
+ }), h.appendChild(p);
582
+ const c = r("hr", { className: "cp-separator" });
583
+ h.appendChild(c), this.signalHandler = new P({
584
+ container: h,
585
+ onChange: (y, u) => this.applySignal(y, u)
586
+ }), s.appendChild(h), this.appendWidget(s), this.updateOutput(0);
585
587
  }
586
588
  sortStops() {
587
589
  this.stops.sort((t, e) => t.position - e.position);
@@ -593,8 +595,8 @@ class Q extends m {
593
595
  className: "cp-input-color",
594
596
  value: t.color
595
597
  });
596
- s.addEventListener("input", (l) => {
597
- t.color = l.target.value, this.updateOutput();
598
+ s.addEventListener("input", (h) => {
599
+ t.color = h.target.value, this.updateOutput();
598
600
  });
599
601
  const a = r("input", {
600
602
  type: "number",
@@ -604,9 +606,9 @@ class Q extends m {
604
606
  step: "0.01",
605
607
  value: String(t.position)
606
608
  });
607
- a.addEventListener("change", (l) => {
608
- let d = parseFloat(l.target.value);
609
- isNaN(d) && (d = 0), t.position = Math.max(0, Math.min(1, d)), this.sortStops(), this.renderStops(), this.updateOutput();
609
+ a.addEventListener("change", (h) => {
610
+ let p = parseFloat(h.target.value);
611
+ isNaN(p) && (p = 0), t.position = Math.max(0, Math.min(1, p)), this.sortStops(), this.renderStops(), this.updateOutput();
610
612
  });
611
613
  const o = B(() => {
612
614
  this.stops.splice(e, 1), this.renderStops(), this.updateOutput();
@@ -630,8 +632,8 @@ class Q extends m {
630
632
  for (let i = 0; i < this.stops.length - 1; i++) {
631
633
  const s = this.stops[i], a = this.stops[i + 1];
632
634
  if (t >= s.position && t <= a.position) {
633
- const o = a.position - s.position, l = o === 0 ? 0 : (t - s.position) / o;
634
- e = K(s.color, a.color, l);
635
+ const o = a.position - s.position, h = o === 0 ? 0 : (t - s.position) / o;
636
+ e = K(s.color, a.color, h);
635
637
  break;
636
638
  }
637
639
  }
@@ -679,15 +681,15 @@ class X extends m {
679
681
  className: "cp-controller-summary"
680
682
  }), o = r("div", {
681
683
  className: "cp-controller-summary-content"
682
- }), l = r("span", { className: "cp-value-display" }, [
684
+ }), h = r("span", { className: "cp-value-display" }, [
683
685
  `${this.items.length} items`
684
686
  ]);
685
- o.appendChild(l), a.appendChild(o), s.appendChild(a);
686
- const d = r("div", { className: "cp-number-settings" });
687
+ o.appendChild(h), a.appendChild(o), s.appendChild(a);
688
+ const p = r("div", { className: "cp-number-settings" });
687
689
  this.itemsContainer = r("div", {
688
690
  className: "cp-stops-container"
689
- }), this.renderItems(), d.appendChild(this.itemsContainer);
690
- const h = r(
691
+ }), this.renderItems(), p.appendChild(this.itemsContainer);
692
+ const c = r(
691
693
  "button",
692
694
  {
693
695
  className: "cp-button cp-input-small",
@@ -695,9 +697,9 @@ class X extends m {
695
697
  },
696
698
  ["+ Add Item"]
697
699
  );
698
- h.addEventListener("click", () => {
700
+ c.addEventListener("click", () => {
699
701
  this.addItem();
700
- }), d.appendChild(h), s.appendChild(d), this.appendWidget(s);
702
+ }), p.appendChild(c), s.appendChild(p), this.appendWidget(s);
701
703
  }
702
704
  parseValue(t) {
703
705
  return !t || t.trim() === "" ? [] : t.split(",").map((e) => e.trim());
@@ -784,16 +786,16 @@ class Y {
784
786
  cancelAnimationFrame(this.rafId);
785
787
  }
786
788
  }
787
- class P {
789
+ class k {
788
790
  constructor() {
789
791
  this.controllers = [], this.folders = [];
790
792
  }
791
793
  addNumber(t, e, i = {}) {
792
- const s = new q(t, e, i);
794
+ const s = new H(t, e, i);
793
795
  return this.contentElement.appendChild(s.domElement), this.controllers.push(s), s;
794
796
  }
795
797
  addSelect(t, e, i = {}) {
796
- const s = new H(t, e, i);
798
+ const s = new U(t, e, i);
797
799
  return this.contentElement.appendChild(s.domElement), this.controllers.push(s), s;
798
800
  }
799
801
  addBoolean(t, e, i = {}) {
@@ -805,11 +807,11 @@ class P {
805
807
  return this.contentElement.appendChild(s.domElement), this.controllers.push(s), s;
806
808
  }
807
809
  addRadio(t, e, i = {}) {
808
- const s = new G(t, e, i);
810
+ const s = new J(t, e, i);
809
811
  return this.contentElement.appendChild(s.domElement), this.controllers.push(s), s;
810
812
  }
811
813
  addColor(t, e, i = {}) {
812
- const s = new J(t, e, i);
814
+ const s = new G(t, e, i);
813
815
  return this.contentElement.appendChild(s.domElement), this.controllers.push(s), s;
814
816
  }
815
817
  addGradient(t, e, i = {}) {
@@ -859,7 +861,7 @@ class P {
859
861
  t.reset();
860
862
  }
861
863
  }
862
- class tt extends P {
864
+ class tt extends k {
863
865
  constructor(t) {
864
866
  super(), this.title = t, this.domElement = r("details", {
865
867
  className: "cp-folder",
@@ -877,7 +879,7 @@ class tt extends P {
877
879
  );
878
880
  }
879
881
  }
880
- class et extends P {
882
+ class et extends k {
881
883
  constructor(t, e = {}) {
882
884
  super(), this.domElement = r("details", {
883
885
  className: "cp-root",
@@ -885,7 +887,9 @@ class et extends P {
885
887
  }), this.summaryElement = r("summary", {
886
888
  className: "cp-summary cp-summary-root"
887
889
  }), this.domElement.appendChild(this.summaryElement);
888
- const i = r("span", {}, [e.title || "GUI"]);
890
+ const i = r("span", {}, [
891
+ e.title || "ControlPanel"
892
+ ]);
889
893
  this.summaryElement.appendChild(i), this.stats = new Y(), this.summaryElement.appendChild(this.stats.domElement), this.contentElement = r("div", { className: "cp-content" }), this.domElement.appendChild(this.contentElement);
890
894
  const s = this.addFolder("_Signals"), a = {
891
895
  audioInput: null,
@@ -894,72 +898,72 @@ class et extends P {
894
898
  s.addRadio(a, "audioInput", {
895
899
  label: "Audio Signal",
896
900
  options: ["microphone", "browser"]
897
- }).onChange((c) => {
898
- x.setInput(c);
901
+ }).onChange((l) => {
902
+ x.setInput(l);
899
903
  }), s.addSelect(a, "fftSize", {
900
904
  label: "FFT Size",
901
905
  options: [256, 512, 1024, 2048]
902
- }).onChange((c) => {
903
- x.setFFTSize(c);
906
+ }).onChange((l) => {
907
+ x.setFFTSize(l);
904
908
  }), s.addNumber(x, "smoothingTimeConstant", {
905
909
  min: 0,
906
910
  max: 0.99,
907
911
  step: 0.01,
908
912
  label: "Smoothing"
909
- }).onChange((c) => {
910
- x.analyser.smoothingTimeConstant = c;
913
+ }).onChange((l) => {
914
+ x.analyser.smoothingTimeConstant = l;
911
915
  }), s.addNumber(x, "spectrumBoost", {
912
916
  min: 1,
913
917
  max: 5,
914
918
  step: 0.1,
915
919
  label: "Compression"
916
920
  }), t ? t.appendChild(this.domElement) : document.body.appendChild(this.domElement);
917
- const o = e.title || "GUI";
921
+ const o = e.title || "ControlPanel";
918
922
  this.presetStoragePrefix = `cp-presets-${o}-`;
919
- const l = this.addFolder("_User Presets"), d = () => {
920
- const c = ["Default"];
921
- if (typeof localStorage > "u") return c;
922
- for (let u = 0; u < localStorage.length; u++) {
923
- const f = localStorage.key(u);
923
+ const h = this.addFolder("_User Presets"), p = () => {
924
+ const l = ["Default"];
925
+ if (typeof localStorage > "u") return l;
926
+ for (let d = 0; d < localStorage.length; d++) {
927
+ const f = localStorage.key(d);
924
928
  if (f && f.startsWith(this.presetStoragePrefix)) {
925
929
  const v = f.substring(this.presetStoragePrefix.length);
926
- v !== "Default" && !c.includes(v) && c.push(v);
930
+ v !== "Default" && !l.includes(v) && l.push(v);
927
931
  }
928
932
  }
929
- return c.sort();
930
- }, h = {
933
+ return l.sort();
934
+ }, c = {
931
935
  selected: "Default",
932
936
  save: () => {
933
- const c = prompt("Preset Name:", h.selected);
934
- if (c) {
935
- if (c === "Default") {
937
+ const l = prompt("Preset Name:", c.selected);
938
+ if (l) {
939
+ if (l === "Default") {
936
940
  alert("Cannot overwrite Default preset");
937
941
  return;
938
942
  }
939
- const u = this.presetStoragePrefix + c;
940
- this.saveToLocalStorage(u);
941
- const f = d();
942
- p.setOptions(f), h.selected = c, p.setValue(c);
943
+ const d = this.presetStoragePrefix + l;
944
+ this.saveToLocalStorage(d);
945
+ const f = p();
946
+ u.setOptions(f), c.selected = l, u.setValue(l);
943
947
  }
944
948
  },
945
949
  load: () => {
946
- const c = h.selected, u = this.presetStoragePrefix + c;
947
- this.loadFromLocalStorage(u), h.selected = c, p.setValue(c);
950
+ const l = c.selected, d = this.presetStoragePrefix + l;
951
+ this.loadFromLocalStorage(d), c.selected = l, u.setValue(l);
948
952
  },
949
953
  delete: () => {
950
- if (h.selected === "Default") {
954
+ if (c.selected === "Default") {
951
955
  alert("Cannot delete Default preset");
952
956
  return;
953
957
  }
954
- if (confirm(`Delete preset "${h.selected}"?`)) {
955
- const c = this.presetStoragePrefix + h.selected;
956
- localStorage.removeItem(c);
957
- const u = d();
958
- p.setOptions(u), h.selected = "Default", p.setValue("Default"), this.reset();
958
+ if (confirm(`Delete preset "${c.selected}"?`)) {
959
+ const l = this.presetStoragePrefix + c.selected;
960
+ localStorage.removeItem(l);
961
+ const d = p();
962
+ u.setOptions(d), c.selected = "Default", u.setValue("Default"), this.reset();
959
963
  }
960
964
  },
961
965
  export: () => {
962
- const c = this.save(), u = (T) => {
966
+ const l = this.save(), d = (T) => {
963
967
  const N = {
964
968
  controllers: {},
965
969
  folders: {}
@@ -969,22 +973,22 @@ class et extends P {
969
973
  for (const [E, L] of Object.entries(
970
974
  T.folders
971
975
  ))
972
- E.startsWith("_") || (N.folders[E] = u(L));
976
+ E.startsWith("_") || (N.folders[E] = d(L));
973
977
  return N;
974
- }, f = u(c), v = {
975
- _presetName: h.selected || "CustomPreset",
978
+ }, f = d(l), v = {
979
+ _presetName: c.selected || "CustomPreset",
976
980
  _exportDate: (/* @__PURE__ */ new Date()).toISOString(),
977
981
  _instructions: "To add as factory preset: Copy 'controllers' and 'folders' fields into the presets.json file",
978
982
  ...f
979
983
  }, I = JSON.stringify(v, null, 2), S = new Blob([I], { type: "application/json" }), C = URL.createObjectURL(S), g = document.createElement("a");
980
984
  g.href = C;
981
- const b = (/* @__PURE__ */ new Date()).toISOString().split("T")[0], V = h.selected.replace(/[^a-z0-9]/gi, "-").toLowerCase();
985
+ const b = (/* @__PURE__ */ new Date()).toISOString().split("T")[0], V = c.selected.replace(/[^a-z0-9]/gi, "-").toLowerCase();
982
986
  g.download = `${o.toLowerCase()}-preset-${V}-${b}.json`, document.body.appendChild(g), g.click(), document.body.removeChild(g), URL.revokeObjectURL(C);
983
987
  },
984
988
  import: () => {
985
- const c = document.createElement("input");
986
- c.type = "file", c.accept = ".json", c.onchange = (u) => {
987
- const f = u.target.files?.[0];
989
+ const l = document.createElement("input");
990
+ l.type = "file", l.accept = ".json", l.onchange = (d) => {
991
+ const f = d.target.files?.[0];
988
992
  if (!f) return;
989
993
  const v = new FileReader();
990
994
  v.onload = (I) => {
@@ -1006,8 +1010,8 @@ class et extends P {
1006
1010
  )) {
1007
1011
  const T = this.presetStoragePrefix + b;
1008
1012
  this.saveToLocalStorage(T);
1009
- const N = d();
1010
- p.setOptions(N), h.selected = b, p.setValue(b);
1013
+ const N = p();
1014
+ u.setOptions(N), c.selected = b, u.setValue(b);
1011
1015
  }
1012
1016
  } catch (S) {
1013
1017
  alert(
@@ -1015,20 +1019,20 @@ class et extends P {
1015
1019
  ), console.error("Import error:", S);
1016
1020
  }
1017
1021
  }, v.readAsText(f);
1018
- }, c.click();
1022
+ }, l.click();
1019
1023
  }
1020
- }, y = d(), p = l.addSelect(h, "selected", {
1024
+ }, y = p(), u = h.addSelect(c, "selected", {
1021
1025
  label: "Preset",
1022
1026
  options: y
1023
1027
  });
1024
- l.addButton("Load", () => h.load()), l.addButton("Save / New", () => h.save()), l.addButton("Delete", () => h.delete()), l.addButton("Export JSON", () => h.export()), l.addButton("Import JSON", () => h.import());
1028
+ h.addButton("Load", () => c.load()), h.addButton("Save / New", () => c.save()), h.addButton("Delete", () => c.delete()), h.addButton("Export JSON", () => c.export()), h.addButton("Import JSON", () => c.import());
1025
1029
  }
1026
1030
  saveToLocalStorage(t) {
1027
1031
  const e = this.save();
1028
1032
  try {
1029
1033
  localStorage.setItem(t, JSON.stringify(e));
1030
1034
  } catch (i) {
1031
- console.warn("GUI: Failed to save to localStorage", i);
1035
+ console.warn("ControlPanel: Failed to save to localStorage", i);
1032
1036
  }
1033
1037
  }
1034
1038
  loadFromLocalStorage(t) {
@@ -1039,7 +1043,7 @@ class et extends P {
1039
1043
  this.load(i);
1040
1044
  }
1041
1045
  } catch (e) {
1042
- console.warn("GUI: Failed to load from localStorage", e);
1046
+ console.warn("ControlPanel: Failed to load from localStorage", e);
1043
1047
  }
1044
1048
  }
1045
1049
  saveDefaultPreset() {
@@ -1055,14 +1059,14 @@ export {
1055
1059
  z as AudioSignals,
1056
1060
  W as BooleanController,
1057
1061
  _ as ButtonController,
1058
- J as ColorController,
1062
+ G as ColorController,
1063
+ et as ControlPanel,
1059
1064
  m as Controller,
1060
- et as GUI,
1061
1065
  Q as GradientController,
1062
1066
  $ as MidiSignals,
1063
- q as NumberController,
1064
- G as RadioController,
1065
- H as SelectController,
1067
+ H as NumberController,
1068
+ J as RadioController,
1069
+ U as SelectController,
1066
1070
  x as audioSignals,
1067
- U as midiSignals
1071
+ q as midiSignals
1068
1072
  };
@@ -0,0 +1 @@
1
+ (function(d,o){typeof exports=="object"&&typeof module<"u"?o(exports):typeof define=="function"&&define.amd?define(["exports"],o):(d=typeof globalThis<"u"?globalThis:d||self,o(d.ControlPanel={}))})(this,(function(d){"use strict";function o(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 D(n){const t=o("button",{className:"cp-button cp-button-delete"},["×"]);return t.addEventListener("click",n),t}function K(n){return n.replace(/([A-Z])/g," $1").replace(/^./,t=>t.toUpperCase()).trim()}function O(n,t,e){return Math.min(Math.max(n,t),e)}function Q(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],r=e[i],h=e[i+1];return(n-s)/(a-s)*(h-r)+r}class B{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 p=this.dataArray.length;for(let c=0;c<p;c++){const f=1+c/p*(this.spectrumBoost-1);this.dataArray[c]=Math.min(255,this.dataArray[c]*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]),r=this.getAverage(i[0],i[1]),h=this.getAverage(0,i[1]);this.processLevel("bass",s),this.processLevel("mids",a),this.processLevel("highs",r),this.processLevel("volume",h)}processLevel(t,e){this.peaks[t]-=5e-4,this.peaks[t]=O(this.peaks[t],.1,1),e>this.peaks[t]&&(this.peaks[t]=e),this.levels[t]=O(Q(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 x=new B;class F{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),r=this.normalizeValue(e);this.values.set(a,r),this.isListening&&this.resolveListen&&r>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,r=t.currentTarget.name||"unknown",h=a===144||a===128?"note":"ctrl",p=r.replace(/[^a-zA-Z0-9]/g,"");return`${s}_${h}_${p}`}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 P=new F,M=class M{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=o("div",{className:"cp-controller"});const s=i.label??K(e),a=o("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)}};M.audio=x,M.midi=P;let m=M;const k={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 R{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=k[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"],r=>this.setSignalType(r));this.signalSelect=e.select,t.appendChild(e.row),this.midiRow=o("div",{className:"cp-setting-row",style:"display: none;"});const i=o("label",{className:"cp-setting-label"},["Midi"]);this.midiBtn=o("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 r=await m.midi.listen();this.setMidiId(r)}),this.midiRow.appendChild(i),this.midiRow.appendChild(this.midiBtn),t.appendChild(this.midiRow);const s=this.createSettingSelect("behaviour",["forward","backward","loopForward","loopBackward","pingpong"],r=>this.setBehaviour(r));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(k),r=>this.setEase(r));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=o("div",{className:"cp-setting-row"}),a=o("label",{className:"cp-setting-label"},[t]),r=o("select",{className:"cp-select cp-input-small"});return e.forEach(h=>{const p=o("option",{value:h},[h]);r.appendChild(p)}),r.addEventListener("change",()=>i(r.value)),s.appendChild(a),s.appendChild(r),{row:s,select:r}}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 j 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=o("details",{className:"cp-controller-details"}),a=o("summary",{className:"cp-controller-summary"});this.input=o("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=o("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 r=o("div",{className:"cp-controller-summary-content"});r.appendChild(this.input),r.appendChild(this.display),a.appendChild(r),s.appendChild(a);const h=o("div",{className:"cp-number-settings"}),p=this.createSetting("min",i.min,l=>this.setMin(l));this.minInput=p.input,h.appendChild(p.row);const c=this.createSetting("max",i.max,l=>this.setMax(l));this.maxInput=c.input,h.appendChild(c.row);const v=this.createSetting("step",i.step,l=>this.setStep(l));this.stepInput=v.input,h.appendChild(v.row);const f=o("hr",{className:"cp-separator"});h.appendChild(f),this.signalHandler=new R({container:h,onChange:(l,u)=>this.applySignal(l,u)}),s.appendChild(h),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=o("div",{className:"cp-setting-row"}),a=o("label",{className:"cp-setting-label"},[t]),r=o("input",{type:"number",className:"cp-input-number cp-input-small",step:"any"});return e!==void 0&&(r.value=String(e)),r.addEventListener("input",()=>i(r.value)),s.appendChild(a),s.appendChild(r),{row:s,input:r}}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 z extends m{constructor(t,e,i){super(t,e,i),this.optionValues=[],this.select=o("select",{className:"cp-select"}),this.optionValues=i.options||[],this.optionValues.forEach((s,a)=>{const r=o("option",{value:String(a)},[String(s)]);this.select.appendChild(r)}),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=o("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 $ extends m{constructor(t,e,i={}){const s={action:e};super(s,"action",i);const a=i.label??t;this.button=o("button",{className:"cp-button"},[String(a)]),this.button.addEventListener("click",()=>{const r=this.value;typeof r=="function"&&r(),this.emitChange(r)}),this.appendWidget(this.button)}updateDisplay(){}}class q extends m{constructor(t,e,i={}){super(t,e,i),this.input=o("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 H extends m{constructor(t,e,i){super(t,e,i),this.buttons=[],this.optionValues=[],this.container=o("div",{className:"cp-radios"}),this.optionValues=i.options||[],this.optionValues.forEach(s=>{const a=o("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 U extends m{constructor(t,e,i={}){super(t,e,i),this.input=o("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 _(n){const t=/^#?([a-f\d])([a-f\d])([a-f\d])$/i;n=n.replace(t,(i,s,a,r)=>s+s+a+a+r+r);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 X(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 Y(n,t,e){const[i,s,a]=_(n),[r,h,p]=_(t),c=I(i),v=I(s),f=I(a),l=I(r),u=I(h),g=I(p),S=c+e*(l-c),N=v+e*(u-v),C=f+e*(g-f),b=A(S),y=A(N),w=A(C);return X(b,y,w)}class W 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=o("details",{className:"cp-controller-details"}),a=o("summary",{className:"cp-controller-summary"}),r=o("div",{className:"cp-controller-summary-content"});this.displayColor=o("div",{className:"cp-color-swatch",style:`background-color: ${this.value}`}),this.displayText=o("span",{className:"cp-value-display"},[String(this.value)]),r.appendChild(this.displayColor),r.appendChild(this.displayText),a.appendChild(r),s.appendChild(a);const h=o("div",{className:"cp-number-settings"});this.stopsContainer=o("div",{className:"cp-stops-container"}),this.renderStops(),h.appendChild(this.stopsContainer);const p=o("button",{className:"cp-button"},["+ Add Stop"]);p.addEventListener("click",()=>{this.stops.push({color:"#ffffff",position:.5}),this.sortStops(),this.renderStops(),this.updateOutput()}),h.appendChild(p);const c=o("hr",{className:"cp-separator"});h.appendChild(c),this.signalHandler=new R({container:h,onChange:(v,f)=>this.applySignal(v,f)}),s.appendChild(h),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=o("div",{className:"cp-setting-row"}),s=o("input",{type:"color",className:"cp-input-color",value:t.color});s.addEventListener("input",h=>{t.color=h.target.value,this.updateOutput()});const a=o("input",{type:"number",className:"cp-input-number cp-input-small",min:"0",max:"1",step:"0.01",value:String(t.position)});a.addEventListener("change",h=>{let p=parseFloat(h.target.value);isNaN(p)&&(p=0),t.position=Math.max(0,Math.min(1,p)),this.sortStops(),this.renderStops(),this.updateOutput()});const r=D(()=>{this.stops.splice(e,1),this.renderStops(),this.updateOutput()});i.appendChild(s),i.appendChild(a),i.appendChild(r),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 r=a.position-s.position,h=r===0?0:(t-s.position)/r;e=Y(s.color,a.color,h);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 J 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=o("details",{className:"cp-controller-details"}),a=o("summary",{className:"cp-controller-summary"}),r=o("div",{className:"cp-controller-summary-content"}),h=o("span",{className:"cp-value-display"},[`${this.items.length} items`]);r.appendChild(h),a.appendChild(r),s.appendChild(a);const p=o("div",{className:"cp-number-settings"});this.itemsContainer=o("div",{className:"cp-stops-container"}),this.renderItems(),p.appendChild(this.itemsContainer);const c=o("button",{className:"cp-button cp-input-small",style:"margin-top: 8px; width: 100%;"},["+ Add Item"]);c.addEventListener("click",()=>{this.addItem()}),p.appendChild(c),s.appendChild(p),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=o("div",{className:"cp-setting-row"});let s;this.itemType==="color"?s=o("input",{type:"color",className:"cp-input-color",value:t}):this.itemType==="number"?s=o("input",{type:"number",className:"cp-input-number cp-input-small",step:"any",value:t}):s=o("input",{type:"text",className:"cp-input-number cp-input-small",value:t}),s.addEventListener("input",r=>{this.items[e]=r.target.value,this.updateValue()});const a=D(()=>{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 tt{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=o("span",{className:"cp-stats"}),this.rafId=requestAnimationFrame(this.render)}destroy(){cancelAnimationFrame(this.rafId)}}class G{constructor(){this.controllers=[],this.folders=[]}addNumber(t,e,i={}){const s=new j(t,e,i);return this.contentElement.appendChild(s.domElement),this.controllers.push(s),s}addSelect(t,e,i={}){const s=new z(t,e,i);return this.contentElement.appendChild(s.domElement),this.controllers.push(s),s}addBoolean(t,e,i={}){const s=new q(t,e,i);return this.contentElement.appendChild(s.domElement),this.controllers.push(s),s}addButton(t,e,i={}){const s=new $(t,e,i);return this.contentElement.appendChild(s.domElement),this.controllers.push(s),s}addRadio(t,e,i={}){const s=new H(t,e,i);return this.contentElement.appendChild(s.domElement),this.controllers.push(s),s}addColor(t,e,i={}){const s=new U(t,e,i);return this.contentElement.appendChild(s.domElement),this.controllers.push(s),s}addGradient(t,e,i={}){const s=new W(t,e,i);return this.contentElement.appendChild(s.domElement),this.controllers.push(s),s}addArray(t,e,i={}){const s=new J(t,e,i);return this.contentElement.appendChild(s.domElement),this.controllers.push(s),s}addFolder(t){const e=new et(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 et extends G{constructor(t){super(),this.title=t,this.domElement=o("details",{className:"cp-folder",open:!0}),this.summaryElement=o("summary",{className:"cp-summary"},[t]),this.domElement.appendChild(this.summaryElement),this.contentElement=o("div",{className:"cp-content cp-folder-content"}),this.domElement.appendChild(this.contentElement),this.domElement.appendChild(o("hr",{className:"cp-separator"}))}}class st extends G{constructor(t,e={}){super(),this.domElement=o("details",{className:"cp-root",open:!0}),this.summaryElement=o("summary",{className:"cp-summary cp-summary-root"}),this.domElement.appendChild(this.summaryElement);const i=o("span",{},[e.title||"ControlPanel"]);this.summaryElement.appendChild(i),this.stats=new tt,this.summaryElement.appendChild(this.stats.domElement),this.contentElement=o("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=>{x.setInput(l)}),s.addSelect(a,"fftSize",{label:"FFT Size",options:[256,512,1024,2048]}).onChange(l=>{x.setFFTSize(l)}),s.addNumber(x,"smoothingTimeConstant",{min:0,max:.99,step:.01,label:"Smoothing"}).onChange(l=>{x.analyser.smoothingTimeConstant=l}),s.addNumber(x,"spectrumBoost",{min:1,max:5,step:.1,label:"Compression"}),t?t.appendChild(this.domElement):document.body.appendChild(this.domElement);const r=e.title||"ControlPanel";this.presetStoragePrefix=`cp-presets-${r}-`;const h=this.addFolder("_User Presets"),p=()=>{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 S=g.substring(this.presetStoragePrefix.length);S!=="Default"&&!l.includes(S)&&l.push(S)}}return l.sort()},c={selected:"Default",save:()=>{const l=prompt("Preset Name:",c.selected);if(l){if(l==="Default"){alert("Cannot overwrite Default preset");return}const u=this.presetStoragePrefix+l;this.saveToLocalStorage(u);const g=p();f.setOptions(g),c.selected=l,f.setValue(l)}},load:()=>{const l=c.selected,u=this.presetStoragePrefix+l;this.loadFromLocalStorage(u),c.selected=l,f.setValue(l)},delete:()=>{if(c.selected==="Default"){alert("Cannot delete Default preset");return}if(confirm(`Delete preset "${c.selected}"?`)){const l=this.presetStoragePrefix+c.selected;localStorage.removeItem(l);const u=p();f.setOptions(u),c.selected="Default",f.setValue("Default"),this.reset()}},export:()=>{const l=this.save(),u=L=>{const E={controllers:{},folders:{}};for(const[T,V]of Object.entries(L.controllers))T.startsWith("_")||(E.controllers[T]=V);for(const[T,V]of Object.entries(L.folders))T.startsWith("_")||(E.folders[T]=u(V));return E},g=u(l),S={_presetName:c.selected||"CustomPreset",_exportDate:new Date().toISOString(),_instructions:"To add as factory preset: Copy 'controllers' and 'folders' fields into the presets.json file",...g},N=JSON.stringify(S,null,2),C=new Blob([N],{type:"application/json"}),b=URL.createObjectURL(C),y=document.createElement("a");y.href=b;const w=new Date().toISOString().split("T")[0],Z=c.selected.replace(/[^a-z0-9]/gi,"-").toLowerCase();y.download=`${r.toLowerCase()}-preset-${Z}-${w}.json`,document.body.appendChild(y),y.click(),document.body.removeChild(y),URL.revokeObjectURL(b)},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 S=new FileReader;S.onload=N=>{try{const C=N.target?.result,b=JSON.parse(C),y={controllers:b.controllers||{},folders:b.folders||{}};if(!y.controllers||!y.folders){alert("Invalid preset file: missing 'controllers' or 'folders'");return}this.load(y);const w=b._presetName||"ImportedPreset";if(confirm(`Preset loaded! Save as "${w}" to User Presets?`)){const L=this.presetStoragePrefix+w;this.saveToLocalStorage(L);const E=p();f.setOptions(E),c.selected=w,f.setValue(w)}}catch(C){alert(`Failed to import preset: ${C instanceof Error?C.message:"Invalid JSON"}`),console.error("Import error:",C)}},S.readAsText(g)},l.click()}},v=p(),f=h.addSelect(c,"selected",{label:"Preset",options:v});h.addButton("Load",()=>c.load()),h.addButton("Save / New",()=>c.save()),h.addButton("Delete",()=>c.delete()),h.addButton("Export JSON",()=>c.export()),h.addButton("Import JSON",()=>c.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=J,d.AudioSignals=B,d.BooleanController=q,d.ButtonController=$,d.ColorController=U,d.ControlPanel=st,d.Controller=m,d.GradientController=W,d.MidiSignals=F,d.NumberController=j,d.RadioController=H,d.SelectController=z,d.audioSignals=x,d.midiSignals=P,Object.defineProperty(d,Symbol.toStringTag,{value:"Module"})}));
@@ -0,0 +1,35 @@
1
+ export type AudioInputType = "microphone" | "browser";
2
+ export declare class AudioSignals {
3
+ ctx: AudioContext;
4
+ analyser: AnalyserNode;
5
+ source: MediaStreamAudioSourceNode | null;
6
+ stream: MediaStream | null;
7
+ fftSize: number;
8
+ dataArray: Uint8Array<ArrayBuffer>;
9
+ waveformArray: Uint8Array<ArrayBuffer>;
10
+ smoothingTimeConstant: number;
11
+ spectrumBoost: number;
12
+ levels: {
13
+ bass: number;
14
+ mids: number;
15
+ highs: number;
16
+ volume: number;
17
+ };
18
+ peaks: {
19
+ bass: number;
20
+ mids: number;
21
+ highs: number;
22
+ volume: number;
23
+ };
24
+ private _isAnalyzing;
25
+ constructor();
26
+ setFFTSize(size: 256 | 512 | 1024 | 2048): void;
27
+ setInput(type: AudioInputType): Promise<void>;
28
+ loop: () => void;
29
+ update(): void;
30
+ private processLevel;
31
+ private getAverage;
32
+ getSignal(type: "bass" | "mids" | "highs" | "volume"): () => number;
33
+ }
34
+ export declare const audioSignals: AudioSignals;
35
+ //# sourceMappingURL=AudioSignals.d.ts.map