@digitalmeadow/control-panel 1.0.11 → 1.0.12

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.
package/dist/index.js CHANGED
@@ -1,15 +1,33 @@
1
1
  const W = `
2
2
  .cp-root {
3
+ --cp-scale: 1;
4
+
5
+ /* Computed spacing values */
6
+ --cp-space-1: calc(1px * var(--cp-scale));
7
+ --cp-space-2: calc(2px * var(--cp-scale));
8
+ --cp-space-4: calc(4px * var(--cp-scale));
9
+ --cp-space-6: calc(6px * var(--cp-scale));
10
+ --cp-space-8: calc(8px * var(--cp-scale));
11
+
12
+ /* Computed font sizes */
13
+ --cp-font-size-main: calc(10px * var(--cp-scale));
14
+ --cp-font-size-details: calc(1.0em * var(--cp-scale));
15
+
16
+ /* Computed sizes */
17
+ --cp-swatch-size: calc(14px * var(--cp-scale));
18
+ --cp-controller-min-height: calc(18px * var(--cp-scale));
19
+ --cp-button-delete-width: calc(18px * var(--cp-scale));
20
+ --cp-icon-size: calc(8px * var(--cp-scale));
21
+ --cp-icon-position: calc(4px * var(--cp-scale));
22
+ --cp-select-arrow-space: calc(14px * var(--cp-scale));
23
+
3
24
  --cp-color-1: rgba(255, 255, 255, 0.15);
4
25
  --cp-color-2: rgba(255, 255, 255, 0.25);
5
26
  --cp-color-3: rgba(255, 255, 255, 0.35);
6
27
  --cp-color-4: rgba(255, 255, 255, 0.45);
7
28
  --cp-border-radius: 0px;
8
- --cp-swatch-size: 14px;
9
- --cp-font-size-main: 10px;
10
- --cp-font-size-details: 1.0em;
11
29
  --cp-font-weight-bold: 600;
12
- --cp-padding-v: 4px;
30
+ --cp-padding-v: calc(4px * var(--cp-scale));
13
31
 
14
32
  position: absolute;
15
33
  top: 0;
@@ -20,14 +38,14 @@ const W = `
20
38
  background: transparent;
21
39
  resize: both;
22
40
  color: #fff;
23
- min-width: 200px;
24
- min-height: 50px;
41
+ min-width: calc(200px * var(--cp-scale));
42
+ min-height: calc(50px * var(--cp-scale));
25
43
  font-family:
26
44
  var(--cp-font-family), monospace,
27
45
  sans-serif;
28
46
  font-size: var(--cp-font-size-main);
29
47
  line-height: 1;
30
- padding: 8px;
48
+ padding: var(--cp-space-8);
31
49
  }
32
50
 
33
51
  /* Apply blend mode to all children except color inputs */
@@ -58,8 +76,8 @@ const W = `
58
76
  }
59
77
 
60
78
  .cp-root::-webkit-scrollbar {
61
- width: 1px;
62
- height: 1px;
79
+ width: var(--cp-space-1);
80
+ height: var(--cp-space-1);
63
81
  }
64
82
  .cp-root::-webkit-scrollbar-track {
65
83
  background: transparent;
@@ -89,10 +107,10 @@ const W = `
89
107
  }
90
108
 
91
109
  .cp-content {
92
- margin-top: 4px;
110
+ margin-top: var(--cp-space-4);
93
111
  display: flex;
94
112
  flex-direction: column;
95
- gap: 2px;
113
+ gap: var(--cp-space-2);
96
114
  }
97
115
 
98
116
  .cp-folder {
@@ -100,12 +118,12 @@ const W = `
100
118
  }
101
119
 
102
120
  .cp-folder-content {
103
- margin: 0 0 6px 0;
104
- padding: 4px 0 0 0px;
121
+ margin: 0 0 calc(6px * var(--cp-scale)) 0;
122
+ padding: var(--cp-space-4) 0 0 0;
105
123
  }
106
124
 
107
125
  .cp-controller {
108
- min-height: 18px;
126
+ min-height: var(--cp-controller-min-height);
109
127
  display: grid;
110
128
  grid-template-columns: 50% 50%;
111
129
  align-items: center;
@@ -118,7 +136,7 @@ const W = `
118
136
  text-overflow: ellipsis;
119
137
  white-space: nowrap;
120
138
  user-select: none;
121
- padding-right: 8px;
139
+ padding-right: var(--cp-space-8);
122
140
  opacity: 0.8;
123
141
  }
124
142
 
@@ -126,14 +144,13 @@ const W = `
126
144
  background: transparent;
127
145
  border: 1px solid var(--cp-color-2);
128
146
  color: inherit;
129
- padding: 2px 4px;
147
+ padding: var(--cp-space-2) var(--cp-space-4);
130
148
  border-radius: var(--cp-border-radius);
131
149
  font-family: inherit;
132
150
  font-size: inherit;
133
151
  height: 100%;
134
152
  box-sizing: border-box;
135
153
  }
136
- }
137
154
 
138
155
  .cp-input-number:focus {
139
156
  outline: none;
@@ -145,8 +162,8 @@ const W = `
145
162
  color: inherit;
146
163
  background: var(--cp-color-1);
147
164
  border: none;
148
- padding: var(--cp-padding-v) 4px;
149
- padding-right: 18px;
165
+ padding: var(--cp-padding-v) var(--cp-space-4);
166
+ padding-right: calc(var(--cp-space-4) + var(--cp-select-arrow-space));
150
167
  border-radius: var(--cp-border-radius);
151
168
  font-size: inherit;
152
169
  line-height: inherit;
@@ -157,32 +174,33 @@ const W = `
157
174
  -moz-appearance: none;
158
175
  background-image: url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' width='8' height='8' viewBox='0 0 8 8'%3E%3Cpath fill='%23fff' d='M1 2h6l-3 4z'/%3E%3C/svg%3E");
159
176
  background-repeat: no-repeat;
160
- background-position: right 4px center;
177
+ background-position: right var(--cp-icon-position) center;
178
+ background-size: var(--cp-icon-size) var(--cp-icon-size);
161
179
  }
162
180
 
163
181
  .cp-checkbox {
164
182
  margin: auto 0;
165
- width: 14px;
166
- height: 14px;
183
+ width: var(--cp-controller-min-height);
184
+ height: var(--cp-controller-min-height);
167
185
  appearance: none;
168
186
  -webkit-appearance: none;
169
187
  -moz-appearance: none;
170
- border: none !important;
188
+ border: 1px solid var(--cp-color-1);
171
189
  border-radius: var(--cp-border-radius);
172
- background: var(--cp-color-1) !important;
190
+ background: transparent;
173
191
  cursor: pointer;
174
192
  padding: 0;
175
193
  outline: none;
176
194
  font-size: 0;
177
195
  line-height: 0;
178
196
  color: transparent;
197
+ vertical-align: middle;
198
+ display: inline-block;
199
+ box-sizing: border-box;
179
200
  }
180
201
 
181
202
  .cp-checkbox:checked {
182
- background-image: url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' width='10' height='10' viewBox='0 0 10 10'%3E%3Ccircle cx='5' cy='5' r='3' fill='%23fff'/%3E%3C/svg%3E") !important;
183
- background-repeat: no-repeat !important;
184
- background-position: center !important;
185
- background-size: 8px 8px !important;
203
+ background: var(--cp-color-1);
186
204
  }
187
205
 
188
206
  .cp-button {
@@ -190,7 +208,7 @@ const W = `
190
208
  color: inherit;
191
209
  background: var(--cp-color-1);
192
210
  border: none;
193
- padding: var(--cp-padding-v) 2px;
211
+ padding: var(--cp-padding-v) var(--cp-space-2);
194
212
  border-radius: var(--cp-border-radius);
195
213
  cursor: pointer;
196
214
  text-align: center;
@@ -205,7 +223,7 @@ const W = `
205
223
 
206
224
  .cp-button:active {
207
225
  background: var(--cp-color-3);
208
- transform: translateY(1px);
226
+ transform: translateY(var(--cp-space-1));
209
227
  }
210
228
 
211
229
  .cp-controller[data-disabled="true"] {
@@ -224,9 +242,9 @@ const W = `
224
242
  .cp-controller-summary-content {
225
243
  display: inline-flex;
226
244
  align-items: center;
227
- gap: 4px;
228
- width: calc(100% - 10px);
229
- min-height: 18px;
245
+ gap: var(--cp-space-4);
246
+ width: calc(100% - var(--cp-button-delete-width));
247
+ min-height: var(--cp-controller-min-height);
230
248
  vertical-align: middle;
231
249
  }
232
250
 
@@ -234,7 +252,7 @@ const W = `
234
252
  -webkit-appearance: none;
235
253
  flex: 1;
236
254
  min-width: 0;
237
- height: 2px;
255
+ height: calc(2px * var(--cp-scale));
238
256
  background: var(--cp-color-1);
239
257
  margin: 0;
240
258
  vertical-align: middle;
@@ -242,15 +260,15 @@ const W = `
242
260
  }
243
261
  .cp-input-range::-webkit-slider-thumb {
244
262
  -webkit-appearance: none;
245
- width: 4px;
246
- height: 8px;
247
- border-radius: 1px;
263
+ width: calc(4px * var(--cp-scale));
264
+ height: calc(8px * var(--cp-scale));
265
+ border-radius: var(--cp-border-radius);
248
266
  background: #fff;
249
267
  cursor: grab;
250
268
  }
251
269
  .cp-input-range::-moz-range-thumb {
252
- width: 4px;
253
- height: 16px;
270
+ width: calc(4px * var(--cp-scale));
271
+ height: calc(16px * var(--cp-scale));
254
272
  background: #fff;
255
273
  cursor: grab;
256
274
  }
@@ -260,7 +278,7 @@ const W = `
260
278
  }
261
279
 
262
280
  .cp-value-display {
263
- min-width: 24px;
281
+ min-width: calc(24px * var(--cp-scale));
264
282
  text-align: right;
265
283
  font-variant-numeric: tabular-nums;
266
284
  font-size: var(--cp-font-size-details);
@@ -269,17 +287,17 @@ const W = `
269
287
  }
270
288
 
271
289
  .cp-number-settings {
272
- margin-top: 4px;
290
+ margin-top: var(--cp-space-4);
273
291
  background: transparent;
274
292
  display: flex;
275
293
  flex-direction: column;
276
- gap: 2px;
294
+ gap: var(--cp-space-4);
277
295
  }
278
296
 
279
297
  .cp-separator {
280
298
  border: none;
281
299
  border-top: 1px solid var(--cp-color-1);
282
- margin: 4px 0;
300
+ margin: var(--cp-space-4) 0;
283
301
  width: 100%;
284
302
  }
285
303
 
@@ -287,28 +305,28 @@ const W = `
287
305
  display: grid;
288
306
  grid-template-columns: 50% 50%;
289
307
  align-items: center;
290
- gap: 2px;
308
+ gap: var(--cp-space-2);
291
309
  }
292
310
 
293
311
  .cp-array-row {
294
312
  display: grid;
295
313
  grid-template-columns: auto 1fr;
296
314
  align-items: stretch;
297
- gap: 2px;
315
+ gap: var(--cp-space-2);
298
316
  }
299
317
 
300
318
  .cp-gradient-stop {
301
319
  display: grid;
302
320
  grid-template-columns: 50% 50%;
303
321
  align-items: stretch;
304
- gap: 2px;
322
+ gap: var(--cp-space-2);
305
323
  }
306
324
 
307
325
  .cp-gradient-stop-row {
308
326
  display: grid;
309
327
  grid-template-columns: auto 1fr auto;
310
328
  align-items: center;
311
- gap: 2px;
329
+ gap: var(--cp-space-2);
312
330
  }
313
331
 
314
332
  .cp-setting-label {
@@ -321,13 +339,13 @@ const W = `
321
339
 
322
340
  .cp-radios {
323
341
  display: flex;
324
- gap: 2px;
342
+ gap: var(--cp-space-2);
325
343
  }
326
344
 
327
345
  .cp-radio {
328
346
  flex: 1;
329
347
  font-size: var(--cp-font-size-details);
330
- padding: var(--cp-padding-v) 2px;
348
+ padding: var(--cp-padding-v) var(--cp-space-2);
331
349
  }
332
350
 
333
351
  .cp-radio[data-active="true"] {
@@ -339,7 +357,7 @@ const W = `
339
357
  .cp-button-delete {
340
358
  grid-column: auto;
341
359
  width: 100%;
342
- min-width: 18px;
360
+ min-width: var(--cp-button-delete-width);
343
361
  padding: 0;
344
362
  display: flex;
345
363
  align-items: center;
@@ -363,41 +381,16 @@ const W = `
363
381
  border-radius: var(--cp-border-radius);
364
382
  }
365
383
 
366
- .cp-checkbox {
367
- margin: auto 0;
368
- width: 14px;
369
- height: 14px;
370
- appearance: none;
371
- -webkit-appearance: none;
372
- -moz-appearance: none;
373
- border: 1px solid var(--cp-color-1);
374
- border-radius: var(--cp-border-radius);
375
- background: transparent;
376
- cursor: pointer;
377
- padding: 0;
378
- outline: none;
379
- font-size: 0;
380
- line-height: 0;
381
- color: transparent;
382
- vertical-align: middle;
383
- display: inline-block;
384
- box-sizing: border-box;
385
- }
386
-
387
- .cp-checkbox:checked {
388
- background: var(--cp-color-1);
389
- }
390
-
391
384
  .cp-color-swatch {
392
385
  width: var(--cp-swatch-size);
393
386
  height: var(--cp-swatch-size);
394
- margin-right: 8px;
387
+ margin-right: var(--cp-space-8);
395
388
  }
396
389
 
397
390
  .cp-stops-container {
398
391
  display: flex;
399
392
  flex-direction: column;
400
- gap: 2px;
393
+ gap: var(--cp-space-2);
401
394
  }
402
395
  `;
403
396
  let B = !1;
@@ -485,8 +478,8 @@ class X {
485
478
  if (this.analyser.getByteFrequencyData(this.dataArray), this.analyser.getByteTimeDomainData(this.waveformArray), this.spectrumBoost !== 1) {
486
479
  const p = this.dataArray.length;
487
480
  for (let h = 0; h < p; h++) {
488
- const v = 1 + h / p * (this.spectrumBoost - 1);
489
- this.dataArray[h] = Math.min(255, this.dataArray[h] * v);
481
+ const y = 1 + h / p * (this.spectrumBoost - 1);
482
+ this.dataArray[h] = Math.min(255, this.dataArray[h] * y);
490
483
  }
491
484
  }
492
485
  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]), l = this.getAverage(0, s[1]);
@@ -511,7 +504,7 @@ class X {
511
504
  return () => this.levels[t];
512
505
  }
513
506
  }
514
- const T = new X();
507
+ const M = new X();
515
508
  class G {
516
509
  constructor() {
517
510
  this.midiAccess = null, this.values = /* @__PURE__ */ new Map(), this.isListening = !1, this.resolveListen = null, this.listeningCallback = null, this.init();
@@ -687,7 +680,7 @@ const Q = new K(), A = class A {
687
680
  this.domElement.appendChild(t);
688
681
  }
689
682
  };
690
- A.audio = T, A.midi = Z, A.math = Q;
683
+ A.audio = M, A.midi = Z, A.math = Q;
691
684
  let u = A;
692
685
  class tt extends u {
693
686
  constructor(t, e, s = {}) {
@@ -867,7 +860,7 @@ class H {
867
860
  0,
868
861
  1,
869
862
  0.01,
870
- (v) => u.math.setConfig(t, { phase: v })
863
+ (y) => u.math.setConfig(t, { phase: y })
871
864
  );
872
865
  this.mathParamsContainer.appendChild(d.row);
873
866
  }
@@ -964,8 +957,8 @@ class et extends u {
964
957
  (f) => this.setStep(f)
965
958
  );
966
959
  this.stepInput = d.input, l.appendChild(d.row);
967
- const v = r("hr", { className: "cp-separator" });
968
- l.appendChild(v), this.signalHandler = new H({
960
+ const y = r("hr", { className: "cp-separator" });
961
+ l.appendChild(y), this.signalHandler = new H({
969
962
  container: l,
970
963
  onChange: (f, b) => this.applySignal(f, b)
971
964
  }), i.appendChild(l), this.appendWidget(i);
@@ -1148,7 +1141,7 @@ function $(n) {
1148
1141
  function rt(n, t, e) {
1149
1142
  return "#" + ((1 << 24) + (Math.round(n) << 16) + (Math.round(t) << 8) + Math.round(e)).toString(16).slice(1);
1150
1143
  }
1151
- function k(n) {
1144
+ function T(n) {
1152
1145
  const t = n / 255;
1153
1146
  return t <= 0.04045 ? t / 12.92 : Math.pow((t + 0.055) / 1.055, 2.4);
1154
1147
  }
@@ -1156,8 +1149,8 @@ function F(n) {
1156
1149
  return n <= 31308e-7 ? n * 12.92 * 255 : (1.055 * Math.pow(n, 1 / 2.4) - 0.055) * 255;
1157
1150
  }
1158
1151
  function lt(n, t, e) {
1159
- const [s, i, a] = $(n), [o, l, p] = $(t), h = k(s), d = k(i), v = k(a), f = k(o), b = k(l), E = k(p), g = h + e * (f - h), V = d + e * (b - d), w = v + e * (E - v), c = F(g), m = F(V), y = F(w);
1160
- return rt(c, m, y);
1152
+ const [s, i, a] = $(n), [o, l, p] = $(t), h = T(s), d = T(i), y = T(a), f = T(o), b = T(l), E = T(p), g = h + e * (f - h), V = d + e * (b - d), S = y + e * (E - y), c = F(g), m = F(V), v = F(S);
1153
+ return rt(c, m, v);
1161
1154
  }
1162
1155
  class ct extends u {
1163
1156
  constructor(t, e, s = {}) {
@@ -1194,7 +1187,7 @@ class ct extends u {
1194
1187
  const h = r("hr", { className: "cp-separator" });
1195
1188
  l.appendChild(h), this.signalHandler = new H({
1196
1189
  container: l,
1197
- onChange: (d, v) => this.applySignal(d, v)
1190
+ onChange: (d, y) => this.applySignal(d, y)
1198
1191
  }), i.appendChild(l), this.appendWidget(i), this.updateOutput(0);
1199
1192
  }
1200
1193
  sortStops() {
@@ -1517,35 +1510,35 @@ class ut extends U {
1517
1510
  l = m.left, p = m.top, c.preventDefault();
1518
1511
  }), document.addEventListener("mousemove", (c) => {
1519
1512
  if (!i) return;
1520
- const m = c.clientX - a, y = c.clientY - o, x = l + m, M = p + y;
1521
- this.domElement.style.left = `${x}px`, this.domElement.style.top = `${M}px`, this.domElement.style.right = "auto", this.domElement.style.bottom = "auto";
1513
+ const m = c.clientX - a, v = c.clientY - o, w = l + m, k = p + v;
1514
+ this.domElement.style.left = `${w}px`, this.domElement.style.top = `${k}px`, this.domElement.style.right = "auto", this.domElement.style.bottom = "auto";
1522
1515
  }), document.addEventListener("mouseup", () => {
1523
1516
  i && (i = !1, this.savePositionAndSize());
1524
1517
  }), new ResizeObserver(() => {
1525
1518
  i || this.savePositionAndSize();
1526
1519
  }).observe(this.domElement), this.restorePositionAndSize(), this.contentElement = r("div", { className: "cp-content" }), this.domElement.appendChild(this.contentElement);
1527
- const d = this.addFolder("_Signals"), v = {
1520
+ const d = this.addFolder("_Signals"), y = {
1528
1521
  audioInput: null,
1529
1522
  fftSize: 2048
1530
1523
  };
1531
- d.addRadio(v, "audioInput", {
1524
+ d.addRadio(y, "audioInput", {
1532
1525
  label: "Audio Signal",
1533
1526
  options: ["microphone", "browser"]
1534
1527
  }).onChange((c) => {
1535
- T.setInput(c);
1536
- }), d.addSelect(v, "fftSize", {
1528
+ M.setInput(c);
1529
+ }), d.addSelect(y, "fftSize", {
1537
1530
  label: "FFT Size",
1538
1531
  options: [256, 512, 1024, 2048]
1539
1532
  }).onChange((c) => {
1540
- T.setFFTSize(c);
1541
- }), d.addRange(T, "smoothingTimeConstant", {
1533
+ M.setFFTSize(c);
1534
+ }), d.addRange(M, "smoothingTimeConstant", {
1542
1535
  min: 0,
1543
1536
  max: 0.99,
1544
1537
  step: 0.01,
1545
1538
  label: "Smoothing"
1546
1539
  }).onChange((c) => {
1547
- T.analyser.smoothingTimeConstant = c;
1548
- }), d.addRange(T, "spectrumBoost", {
1540
+ M.analyser.smoothingTimeConstant = c;
1541
+ }), d.addRange(M, "spectrumBoost", {
1549
1542
  min: 1,
1550
1543
  max: 5,
1551
1544
  step: 0.1,
@@ -1557,10 +1550,10 @@ class ut extends U {
1557
1550
  const c = ["Default"];
1558
1551
  if (typeof localStorage > "u") return c;
1559
1552
  for (let m = 0; m < localStorage.length; m++) {
1560
- const y = localStorage.key(m);
1561
- if (y && y.startsWith(this.presetStoragePrefix)) {
1562
- const x = y.substring(this.presetStoragePrefix.length);
1563
- x !== "Default" && !c.includes(x) && c.push(x);
1553
+ const v = localStorage.key(m);
1554
+ if (v && v.startsWith(this.presetStoragePrefix)) {
1555
+ const w = v.substring(this.presetStoragePrefix.length);
1556
+ w !== "Default" && !c.includes(w) && c.push(w);
1564
1557
  }
1565
1558
  }
1566
1559
  return c.sort();
@@ -1575,13 +1568,13 @@ class ut extends U {
1575
1568
  }
1576
1569
  const m = this.presetStoragePrefix + c;
1577
1570
  this.saveToLocalStorage(m);
1578
- const y = E();
1579
- w.setOptions(y), g.selected = c, w.setValue(c);
1571
+ const v = E();
1572
+ S.setOptions(v), g.selected = c, S.setValue(c);
1580
1573
  }
1581
1574
  },
1582
1575
  load: () => {
1583
1576
  const c = g.selected, m = this.presetStoragePrefix + c;
1584
- this.loadFromLocalStorage(m), g.selected = c, w.setValue(c);
1577
+ this.loadFromLocalStorage(m), g.selected = c, S.setValue(c);
1585
1578
  },
1586
1579
  delete: () => {
1587
1580
  if (g.selected === "Default") {
@@ -1592,7 +1585,7 @@ class ut extends U {
1592
1585
  const c = this.presetStoragePrefix + g.selected;
1593
1586
  localStorage.removeItem(c);
1594
1587
  const m = E();
1595
- w.setOptions(m), g.selected = "Default", w.setValue("Default"), this.reset();
1588
+ S.setOptions(m), g.selected = "Default", S.setValue("Default"), this.reset();
1596
1589
  }
1597
1590
  },
1598
1591
  export: () => {
@@ -1608,35 +1601,35 @@ class ut extends U {
1608
1601
  ))
1609
1602
  z.startsWith("_") || (L.folders[z] = m(O));
1610
1603
  return L;
1611
- }, y = m(c), x = {
1604
+ }, v = m(c), w = {
1612
1605
  _presetName: g.selected || "CustomPreset",
1613
1606
  _exportDate: (/* @__PURE__ */ new Date()).toISOString(),
1614
1607
  _instructions: "To add as factory preset: Copy 'controllers' and 'folders' fields into the presets.json file",
1615
- ...y
1616
- }, M = JSON.stringify(x, null, 2), C = new Blob([M], { type: "application/json" }), I = URL.createObjectURL(C), S = document.createElement("a");
1617
- S.href = I;
1608
+ ...v
1609
+ }, k = JSON.stringify(w, null, 2), C = new Blob([k], { type: "application/json" }), I = URL.createObjectURL(C), x = document.createElement("a");
1610
+ x.href = I;
1618
1611
  const N = (/* @__PURE__ */ new Date()).toISOString().split("T")[0], D = g.selected.replace(/[^a-z0-9]/gi, "-").toLowerCase();
1619
- S.download = `${f.toLowerCase()}-preset-${D}-${N}.json`, document.body.appendChild(S), S.click(), document.body.removeChild(S), URL.revokeObjectURL(I);
1612
+ x.download = `${f.toLowerCase()}-preset-${D}-${N}.json`, document.body.appendChild(x), x.click(), document.body.removeChild(x), URL.revokeObjectURL(I);
1620
1613
  },
1621
1614
  import: () => {
1622
1615
  const c = document.createElement("input");
1623
1616
  c.type = "file", c.accept = ".json", c.onchange = (m) => {
1624
- const y = m.target.files?.[0];
1625
- if (!y) return;
1626
- const x = new FileReader();
1627
- x.onload = (M) => {
1617
+ const v = m.target.files?.[0];
1618
+ if (!v) return;
1619
+ const w = new FileReader();
1620
+ w.onload = (k) => {
1628
1621
  try {
1629
- const C = M.target?.result, I = JSON.parse(C), S = {
1622
+ const C = k.target?.result, I = JSON.parse(C), x = {
1630
1623
  controllers: I.controllers || {},
1631
1624
  folders: I.folders || {}
1632
1625
  };
1633
- if (!S.controllers || !S.folders) {
1626
+ if (!x.controllers || !x.folders) {
1634
1627
  alert(
1635
1628
  "Invalid preset file: missing 'controllers' or 'folders'"
1636
1629
  );
1637
1630
  return;
1638
1631
  }
1639
- this.load(S);
1632
+ this.load(x);
1640
1633
  const N = I._presetName || "ImportedPreset";
1641
1634
  if (confirm(
1642
1635
  `Preset loaded! Save as "${N}" to User Presets?`
@@ -1644,17 +1637,17 @@ class ut extends U {
1644
1637
  const P = this.presetStoragePrefix + N;
1645
1638
  this.saveToLocalStorage(P);
1646
1639
  const L = E();
1647
- w.setOptions(L), g.selected = N, w.setValue(N);
1640
+ S.setOptions(L), g.selected = N, S.setValue(N);
1648
1641
  }
1649
1642
  } catch (C) {
1650
1643
  alert(
1651
1644
  `Failed to import preset: ${C instanceof Error ? C.message : "Invalid JSON"}`
1652
1645
  ), console.error("Import error:", C);
1653
1646
  }
1654
- }, x.readAsText(y);
1647
+ }, w.readAsText(v);
1655
1648
  }, c.click();
1656
1649
  }
1657
- }, V = E(), w = b.addSelect(g, "selected", {
1650
+ }, V = E(), S = b.addSelect(g, "selected", {
1658
1651
  label: "Preset",
1659
1652
  options: V
1660
1653
  });
@@ -1729,7 +1722,7 @@ export {
1729
1722
  at as RadioController,
1730
1723
  et as RangeController,
1731
1724
  st as SelectController,
1732
- T as audioSignals,
1725
+ M as audioSignals,
1733
1726
  Q as mathSignals,
1734
1727
  Z as midiSignals
1735
1728
  };
@@ -1,15 +1,33 @@
1
1
  (function(h,L){typeof exports=="object"&&typeof module<"u"?L(exports):typeof define=="function"&&define.amd?define(["exports"],L):(h=typeof globalThis<"u"?globalThis:h||self,L(h.ControlPanel={}))})(this,(function(h){"use strict";const L=`
2
2
  .cp-root {
3
+ --cp-scale: 1;
4
+
5
+ /* Computed spacing values */
6
+ --cp-space-1: calc(1px * var(--cp-scale));
7
+ --cp-space-2: calc(2px * var(--cp-scale));
8
+ --cp-space-4: calc(4px * var(--cp-scale));
9
+ --cp-space-6: calc(6px * var(--cp-scale));
10
+ --cp-space-8: calc(8px * var(--cp-scale));
11
+
12
+ /* Computed font sizes */
13
+ --cp-font-size-main: calc(10px * var(--cp-scale));
14
+ --cp-font-size-details: calc(1.0em * var(--cp-scale));
15
+
16
+ /* Computed sizes */
17
+ --cp-swatch-size: calc(14px * var(--cp-scale));
18
+ --cp-controller-min-height: calc(18px * var(--cp-scale));
19
+ --cp-button-delete-width: calc(18px * var(--cp-scale));
20
+ --cp-icon-size: calc(8px * var(--cp-scale));
21
+ --cp-icon-position: calc(4px * var(--cp-scale));
22
+ --cp-select-arrow-space: calc(14px * var(--cp-scale));
23
+
3
24
  --cp-color-1: rgba(255, 255, 255, 0.15);
4
25
  --cp-color-2: rgba(255, 255, 255, 0.25);
5
26
  --cp-color-3: rgba(255, 255, 255, 0.35);
6
27
  --cp-color-4: rgba(255, 255, 255, 0.45);
7
28
  --cp-border-radius: 0px;
8
- --cp-swatch-size: 14px;
9
- --cp-font-size-main: 10px;
10
- --cp-font-size-details: 1.0em;
11
29
  --cp-font-weight-bold: 600;
12
- --cp-padding-v: 4px;
30
+ --cp-padding-v: calc(4px * var(--cp-scale));
13
31
 
14
32
  position: absolute;
15
33
  top: 0;
@@ -20,14 +38,14 @@
20
38
  background: transparent;
21
39
  resize: both;
22
40
  color: #fff;
23
- min-width: 200px;
24
- min-height: 50px;
41
+ min-width: calc(200px * var(--cp-scale));
42
+ min-height: calc(50px * var(--cp-scale));
25
43
  font-family:
26
44
  var(--cp-font-family), monospace,
27
45
  sans-serif;
28
46
  font-size: var(--cp-font-size-main);
29
47
  line-height: 1;
30
- padding: 8px;
48
+ padding: var(--cp-space-8);
31
49
  }
32
50
 
33
51
  /* Apply blend mode to all children except color inputs */
@@ -58,8 +76,8 @@
58
76
  }
59
77
 
60
78
  .cp-root::-webkit-scrollbar {
61
- width: 1px;
62
- height: 1px;
79
+ width: var(--cp-space-1);
80
+ height: var(--cp-space-1);
63
81
  }
64
82
  .cp-root::-webkit-scrollbar-track {
65
83
  background: transparent;
@@ -89,10 +107,10 @@
89
107
  }
90
108
 
91
109
  .cp-content {
92
- margin-top: 4px;
110
+ margin-top: var(--cp-space-4);
93
111
  display: flex;
94
112
  flex-direction: column;
95
- gap: 2px;
113
+ gap: var(--cp-space-2);
96
114
  }
97
115
 
98
116
  .cp-folder {
@@ -100,12 +118,12 @@
100
118
  }
101
119
 
102
120
  .cp-folder-content {
103
- margin: 0 0 6px 0;
104
- padding: 4px 0 0 0px;
121
+ margin: 0 0 calc(6px * var(--cp-scale)) 0;
122
+ padding: var(--cp-space-4) 0 0 0;
105
123
  }
106
124
 
107
125
  .cp-controller {
108
- min-height: 18px;
126
+ min-height: var(--cp-controller-min-height);
109
127
  display: grid;
110
128
  grid-template-columns: 50% 50%;
111
129
  align-items: center;
@@ -118,7 +136,7 @@
118
136
  text-overflow: ellipsis;
119
137
  white-space: nowrap;
120
138
  user-select: none;
121
- padding-right: 8px;
139
+ padding-right: var(--cp-space-8);
122
140
  opacity: 0.8;
123
141
  }
124
142
 
@@ -126,14 +144,13 @@
126
144
  background: transparent;
127
145
  border: 1px solid var(--cp-color-2);
128
146
  color: inherit;
129
- padding: 2px 4px;
147
+ padding: var(--cp-space-2) var(--cp-space-4);
130
148
  border-radius: var(--cp-border-radius);
131
149
  font-family: inherit;
132
150
  font-size: inherit;
133
151
  height: 100%;
134
152
  box-sizing: border-box;
135
153
  }
136
- }
137
154
 
138
155
  .cp-input-number:focus {
139
156
  outline: none;
@@ -145,8 +162,8 @@
145
162
  color: inherit;
146
163
  background: var(--cp-color-1);
147
164
  border: none;
148
- padding: var(--cp-padding-v) 4px;
149
- padding-right: 18px;
165
+ padding: var(--cp-padding-v) var(--cp-space-4);
166
+ padding-right: calc(var(--cp-space-4) + var(--cp-select-arrow-space));
150
167
  border-radius: var(--cp-border-radius);
151
168
  font-size: inherit;
152
169
  line-height: inherit;
@@ -157,32 +174,33 @@
157
174
  -moz-appearance: none;
158
175
  background-image: url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' width='8' height='8' viewBox='0 0 8 8'%3E%3Cpath fill='%23fff' d='M1 2h6l-3 4z'/%3E%3C/svg%3E");
159
176
  background-repeat: no-repeat;
160
- background-position: right 4px center;
177
+ background-position: right var(--cp-icon-position) center;
178
+ background-size: var(--cp-icon-size) var(--cp-icon-size);
161
179
  }
162
180
 
163
181
  .cp-checkbox {
164
182
  margin: auto 0;
165
- width: 14px;
166
- height: 14px;
183
+ width: var(--cp-controller-min-height);
184
+ height: var(--cp-controller-min-height);
167
185
  appearance: none;
168
186
  -webkit-appearance: none;
169
187
  -moz-appearance: none;
170
- border: none !important;
188
+ border: 1px solid var(--cp-color-1);
171
189
  border-radius: var(--cp-border-radius);
172
- background: var(--cp-color-1) !important;
190
+ background: transparent;
173
191
  cursor: pointer;
174
192
  padding: 0;
175
193
  outline: none;
176
194
  font-size: 0;
177
195
  line-height: 0;
178
196
  color: transparent;
197
+ vertical-align: middle;
198
+ display: inline-block;
199
+ box-sizing: border-box;
179
200
  }
180
201
 
181
202
  .cp-checkbox:checked {
182
- background-image: url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' width='10' height='10' viewBox='0 0 10 10'%3E%3Ccircle cx='5' cy='5' r='3' fill='%23fff'/%3E%3C/svg%3E") !important;
183
- background-repeat: no-repeat !important;
184
- background-position: center !important;
185
- background-size: 8px 8px !important;
203
+ background: var(--cp-color-1);
186
204
  }
187
205
 
188
206
  .cp-button {
@@ -190,7 +208,7 @@
190
208
  color: inherit;
191
209
  background: var(--cp-color-1);
192
210
  border: none;
193
- padding: var(--cp-padding-v) 2px;
211
+ padding: var(--cp-padding-v) var(--cp-space-2);
194
212
  border-radius: var(--cp-border-radius);
195
213
  cursor: pointer;
196
214
  text-align: center;
@@ -205,7 +223,7 @@
205
223
 
206
224
  .cp-button:active {
207
225
  background: var(--cp-color-3);
208
- transform: translateY(1px);
226
+ transform: translateY(var(--cp-space-1));
209
227
  }
210
228
 
211
229
  .cp-controller[data-disabled="true"] {
@@ -224,9 +242,9 @@
224
242
  .cp-controller-summary-content {
225
243
  display: inline-flex;
226
244
  align-items: center;
227
- gap: 4px;
228
- width: calc(100% - 10px);
229
- min-height: 18px;
245
+ gap: var(--cp-space-4);
246
+ width: calc(100% - var(--cp-button-delete-width));
247
+ min-height: var(--cp-controller-min-height);
230
248
  vertical-align: middle;
231
249
  }
232
250
 
@@ -234,7 +252,7 @@
234
252
  -webkit-appearance: none;
235
253
  flex: 1;
236
254
  min-width: 0;
237
- height: 2px;
255
+ height: calc(2px * var(--cp-scale));
238
256
  background: var(--cp-color-1);
239
257
  margin: 0;
240
258
  vertical-align: middle;
@@ -242,15 +260,15 @@
242
260
  }
243
261
  .cp-input-range::-webkit-slider-thumb {
244
262
  -webkit-appearance: none;
245
- width: 4px;
246
- height: 8px;
247
- border-radius: 1px;
263
+ width: calc(4px * var(--cp-scale));
264
+ height: calc(8px * var(--cp-scale));
265
+ border-radius: var(--cp-border-radius);
248
266
  background: #fff;
249
267
  cursor: grab;
250
268
  }
251
269
  .cp-input-range::-moz-range-thumb {
252
- width: 4px;
253
- height: 16px;
270
+ width: calc(4px * var(--cp-scale));
271
+ height: calc(16px * var(--cp-scale));
254
272
  background: #fff;
255
273
  cursor: grab;
256
274
  }
@@ -260,7 +278,7 @@
260
278
  }
261
279
 
262
280
  .cp-value-display {
263
- min-width: 24px;
281
+ min-width: calc(24px * var(--cp-scale));
264
282
  text-align: right;
265
283
  font-variant-numeric: tabular-nums;
266
284
  font-size: var(--cp-font-size-details);
@@ -269,17 +287,17 @@
269
287
  }
270
288
 
271
289
  .cp-number-settings {
272
- margin-top: 4px;
290
+ margin-top: var(--cp-space-4);
273
291
  background: transparent;
274
292
  display: flex;
275
293
  flex-direction: column;
276
- gap: 2px;
294
+ gap: var(--cp-space-4);
277
295
  }
278
296
 
279
297
  .cp-separator {
280
298
  border: none;
281
299
  border-top: 1px solid var(--cp-color-1);
282
- margin: 4px 0;
300
+ margin: var(--cp-space-4) 0;
283
301
  width: 100%;
284
302
  }
285
303
 
@@ -287,28 +305,28 @@
287
305
  display: grid;
288
306
  grid-template-columns: 50% 50%;
289
307
  align-items: center;
290
- gap: 2px;
308
+ gap: var(--cp-space-2);
291
309
  }
292
310
 
293
311
  .cp-array-row {
294
312
  display: grid;
295
313
  grid-template-columns: auto 1fr;
296
314
  align-items: stretch;
297
- gap: 2px;
315
+ gap: var(--cp-space-2);
298
316
  }
299
317
 
300
318
  .cp-gradient-stop {
301
319
  display: grid;
302
320
  grid-template-columns: 50% 50%;
303
321
  align-items: stretch;
304
- gap: 2px;
322
+ gap: var(--cp-space-2);
305
323
  }
306
324
 
307
325
  .cp-gradient-stop-row {
308
326
  display: grid;
309
327
  grid-template-columns: auto 1fr auto;
310
328
  align-items: center;
311
- gap: 2px;
329
+ gap: var(--cp-space-2);
312
330
  }
313
331
 
314
332
  .cp-setting-label {
@@ -321,13 +339,13 @@
321
339
 
322
340
  .cp-radios {
323
341
  display: flex;
324
- gap: 2px;
342
+ gap: var(--cp-space-2);
325
343
  }
326
344
 
327
345
  .cp-radio {
328
346
  flex: 1;
329
347
  font-size: var(--cp-font-size-details);
330
- padding: var(--cp-padding-v) 2px;
348
+ padding: var(--cp-padding-v) var(--cp-space-2);
331
349
  }
332
350
 
333
351
  .cp-radio[data-active="true"] {
@@ -339,7 +357,7 @@
339
357
  .cp-button-delete {
340
358
  grid-column: auto;
341
359
  width: 100%;
342
- min-width: 18px;
360
+ min-width: var(--cp-button-delete-width);
343
361
  padding: 0;
344
362
  display: flex;
345
363
  align-items: center;
@@ -363,40 +381,15 @@
363
381
  border-radius: var(--cp-border-radius);
364
382
  }
365
383
 
366
- .cp-checkbox {
367
- margin: auto 0;
368
- width: 14px;
369
- height: 14px;
370
- appearance: none;
371
- -webkit-appearance: none;
372
- -moz-appearance: none;
373
- border: 1px solid var(--cp-color-1);
374
- border-radius: var(--cp-border-radius);
375
- background: transparent;
376
- cursor: pointer;
377
- padding: 0;
378
- outline: none;
379
- font-size: 0;
380
- line-height: 0;
381
- color: transparent;
382
- vertical-align: middle;
383
- display: inline-block;
384
- box-sizing: border-box;
385
- }
386
-
387
- .cp-checkbox:checked {
388
- background: var(--cp-color-1);
389
- }
390
-
391
384
  .cp-color-swatch {
392
385
  width: var(--cp-swatch-size);
393
386
  height: var(--cp-swatch-size);
394
- margin-right: 8px;
387
+ margin-right: var(--cp-space-8);
395
388
  }
396
389
 
397
390
  .cp-stops-container {
398
391
  display: flex;
399
392
  flex-direction: column;
400
- gap: 2px;
393
+ gap: var(--cp-space-2);
401
394
  }
402
- `;let R=!1;function lt(){if(R)return;const n=document.createElement("style");n.id="control-panel-styles",n.textContent=L,document.head.appendChild(n),R=!0}function o(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 j(n){const t=o("button",{className:"cp-button cp-button-delete"},["×"]);return t.addEventListener("click",n),t}function ct(n){return n.replace(/([A-Z])/g," $1").replace(/^./,t=>t.toUpperCase()).trim()}function $(n,t,e){return Math.min(Math.max(n,t),e)}function pt(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],r=e[s],l=e[s+1];return(n-i)/(a-i)*(l-r)+r}class H{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 u=0;u<p;u++){const b=1+u/p*(this.spectrumBoost-1);this.dataArray[u]=Math.min(255,this.dataArray[u]*b)}}const t=[2,10],e=[10,150],s=[150,600],i=this.getAverage(t[0],t[1]),a=this.getAverage(e[0],e[1]),r=this.getAverage(s[0],s[1]),l=this.getAverage(0,s[1]);this.processLevel("bass",i),this.processLevel("mids",a),this.processLevel("highs",r),this.processLevel("volume",l)}processLevel(t,e){this.peaks[t]-=5e-4,this.peaks[t]=$(this.peaks[t],.1,1),e>this.peaks[t]&&(this.peaks[t]=e),this.levels[t]=$(pt(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 E=new H;class U{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),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,[s,i]=e,a=s&240,r=t.currentTarget.name||"unknown",l=a===144||a===128?"note":"ctrl",p=r.replace(/[^a-zA-Z0-9]/g,"");return`${i}_${l}_${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 W=new U;class _{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,r=(Math.sin(s*e.frequency*Math.PI*2+i)+1)/2;return this.normalizeOutput(r,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,r=a<.5?a*2:2-a*2;return this.normalizeOutput(r,e)}square(t){const e=t?{...this.configs.get("square"),...t}:this.configs.get("square"),s=this.getTime(),i=e.phase,r=(s*e.frequency+i)%1<.5?0:1;return this.normalizeOutput(r,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 _,A=class A{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=o("div",{className:"cp-controller"});const i=s.label??ct(e),a=o("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)}};A.audio=E,A.midi=W,A.math=J;let d=A;class Y extends d{constructor(t,e,s={}){super(t,e,s),this.input=o("input",{type:"number",className:"cp-input-number",value:String(this.value)}),this.input.addEventListener("input",()=>{}),this.input.addEventListener("blur",()=>{const i=parseFloat(this.input.value);isNaN(i)?this.input.value=String(this.value):this.setValue(i)}),this.input.addEventListener("keydown",i=>{i.key==="Enter"&&this.input.blur()}),this.appendWidget(this.input)}updateDisplay(){this.input.value=String(this.value)}}const X={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 G{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=d.midi.getSignal(this.currentMidiId)()):["constant","sine","sawtooth","triangle","square","random"].includes(this.currentSignalType)?e=d.math.getSignal(this.currentSignalType)():["volume","bass","mids","highs"].includes(this.currentSignalType)&&(e=d.audio.getSignal(this.currentSignalType)());const s=X[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"],l=>this.setSignalType(l));this.signalSelect=e.select,t.appendChild(e.row),this.midiRow=o("div",{className:"cp-setting-row",style:"display: none;"});const s=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..."){d.midi.cancelListen(),this.setMidiId(null);return}this.midiBtn.textContent="Listening...";const l=await d.midi.listen();this.setMidiId(l)}),this.midiRow.appendChild(s),this.midiRow.appendChild(this.midiBtn),t.appendChild(this.midiRow),this.mathParamsContainer=o("div",{style:"display: none; flex-direction: column; gap: 4px;"}),t.appendChild(this.mathParamsContainer);const i=this.createSettingSelect("behaviour",["forward","backward","loopForward","loopBackward","pingpong"],l=>this.setBehaviour(l));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(X),l=>this.setEase(l));this.easeRow=a.row,this.easeSelect=a.select,this.easeRow.style.display="none",this.easeSelect.value=this.currentEase,t.appendChild(this.easeRow);const r=o("hr",{className:"cp-separator"});t.appendChild(r)}createSettingSelect(t,e,s){const i=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(l=>{const p=o("option",{value:l},[l]);r.appendChild(p)}),r.addEventListener("change",()=>s(r.value)),i.appendChild(a),i.appendChild(r),{row:i,select:r}}createNumberInput(t,e,s,i,a,r){const l=o("div",{className:"cp-setting-row"}),p=o("label",{className:"cp-setting-label"},[t]),u=o("input",{className:"cp-input-number cp-input-small",type:"number",value:String(e),min:String(s),max:String(i),step:String(a)});return u.addEventListener("input",()=>{const m=parseFloat(u.value);isNaN(m)||r(m)}),l.appendChild(p),l.appendChild(u),{row:l,input:u}}updateMathParams(t){this.mathParamsContainer.innerHTML="";const e=d.math.getConfig(t);let s="Frequency",i=.1,a=10,r=.1;t==="random"?a=30:t==="constant"&&(s="Speed",i=.01,a=2,r=.01);const l=this.createNumberInput(s,e.frequency,i,a,r,m=>d.math.setConfig(t,{frequency:m}));this.mathParamsContainer.appendChild(l.row);const p=this.createNumberInput("Amplitude",e.amplitude,0,2,.1,m=>d.math.setConfig(t,{amplitude:m}));this.mathParamsContainer.appendChild(p.row);const u=this.createNumberInput("Offset",e.offset,0,1,.1,m=>d.math.setConfig(t,{offset:m}));if(this.mathParamsContainer.appendChild(u.row),["sine","sawtooth","triangle","square"].includes(t)){const m=this.createNumberInput("Phase",e.phase,0,1,.01,b=>d.math.setConfig(t,{phase:b}));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?"grid":"none",this.mathParamsContainer.style.display=s?"grid":"none",s&&this.updateMathParams(t),this.easeRow.style.display="grid",this.behaviourRow.style.display="grid",i?(this.currentMidiId=null,d.audio.ctx.state==="suspended"&&d.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 Z extends d{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=o("details",{className:"cp-controller-details"}),a=o("summary",{className:"cp-controller-summary"});this.input=o("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=o("span",{className:"cp-value-display"},[String(this.value.toFixed(1))]),this.input.addEventListener("input",()=>{const y=parseFloat(this.input.value);isNaN(y)||(this.setValue(y),this.display.textContent=String(y.toFixed(1)))}),this.input.addEventListener("click",y=>{y.stopPropagation()});const r=o("div",{className:"cp-controller-summary-content"});r.appendChild(this.input),r.appendChild(this.display),a.appendChild(r),i.appendChild(a);const l=o("div",{className:"cp-number-settings"}),p=this.createSetting("min",s.min,y=>this.setMin(y));this.minInput=p.input,l.appendChild(p.row);const u=this.createSetting("max",s.max,y=>this.setMax(y));this.maxInput=u.input,l.appendChild(u.row);const m=this.createSetting("step",s.step,y=>this.setStep(y));this.stepInput=m.input,l.appendChild(m.row);const b=o("hr",{className:"cp-separator"});l.appendChild(b),this.signalHandler=new G({container:l,onChange:(y,S)=>this.applySignal(y,S)}),i.appendChild(l),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=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",()=>s(r.value)),i.appendChild(a),i.appendChild(r),{row:i,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 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 K extends d{constructor(t,e,s){super(t,e,s),this.optionValues=[],this.select=o("select",{className:"cp-select"}),this.optionValues=s.options||[],this.optionValues.forEach((i,a)=>{const r=o("option",{value:String(a)},[String(i)]);this.select.appendChild(r)}),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=o("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 Q extends d{constructor(t,e,s={}){const i={action:e};super(i,"action",s),this.domElement.querySelector(".cp-label")?.remove();const a=s.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 tt extends d{constructor(t,e,s={}){super(t,e,s),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 et extends d{constructor(t,e,s){super(t,e,s),this.buttons=[],this.optionValues=[],this.container=o("div",{className:"cp-radios"}),this.optionValues=s.options||[],this.optionValues.forEach(i=>{const a=o("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 st extends d{constructor(t,e,s={}){super(t,e,s),this.input=o("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 it(n){const t=/^#?([a-f\d])([a-f\d])([a-f\d])$/i;n=n.replace(t,(s,i,a,r)=>i+i+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 ht(n,t,e){return"#"+((1<<24)+(Math.round(n)<<16)+(Math.round(t)<<8)+Math.round(e)).toString(16).slice(1)}function N(n){const t=n/255;return t<=.04045?t/12.92:Math.pow((t+.055)/1.055,2.4)}function F(n){return n<=.0031308?n*12.92*255:(1.055*Math.pow(n,1/2.4)-.055)*255}function dt(n,t,e){const[s,i,a]=it(n),[r,l,p]=it(t),u=N(s),m=N(i),b=N(a),y=N(r),S=N(l),k=N(p),f=u+e*(y-u),B=m+e*(S-m),x=b+e*(k-b),c=F(f),g=F(B),v=F(x);return ht(c,g,v)}class nt extends d{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=o("details",{className:"cp-controller-details"}),a=o("summary",{className:"cp-controller-summary"}),r=o("div",{className:"cp-controller-summary-content"});this.displayText=o("span",{className:"cp-value-display"},[String(this.value)]),r.appendChild(this.displayText),a.appendChild(r),i.appendChild(a);const l=o("div",{className:"cp-number-settings"});this.stopsContainer=o("div",{className:"cp-stops-container"}),this.renderStops(),l.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()}),l.appendChild(p);const u=o("hr",{className:"cp-separator"});l.appendChild(u),this.signalHandler=new G({container:l,onChange:(m,b)=>this.applySignal(m,b)}),i.appendChild(l),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=o("div",{className:"cp-gradient-stop-row"}),i=o("input",{type:"color",className:"cp-input-color",value:t.color});i.addEventListener("input",l=>{t.color=l.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",l=>{let p=parseFloat(l.target.value);isNaN(p)&&(p=0),t.position=Math.max(0,Math.min(1,p)),this.sortStops(),this.renderStops(),this.updateOutput()});const r=j(()=>{this.stops.splice(e,1),this.renderStops(),this.updateOutput()});s.appendChild(i),s.appendChild(a),s.appendChild(r),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 r=a.position-i.position,l=r===0?0:(t-i.position)/r;e=dt(i.color,a.color,l);break}}this.setValue(e),this.updateDisplay()}updateDisplay(){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 at extends d{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=o("details",{className:"cp-controller-details"}),a=o("summary",{className:"cp-controller-summary"}),r=o("div",{className:"cp-controller-summary-content"}),l=o("span",{className:"cp-value-display"},[`${this.items.length} items`]);r.appendChild(l),a.appendChild(r),i.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 u=o("button",{className:"cp-button cp-input-small",style:"margin-top: 8px; width: 100%;"},["+ Add Item"]);u.addEventListener("click",()=>{this.addItem()}),p.appendChild(u),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=o("div",{className:"cp-array-row"});let i;this.itemType==="color"?i=o("input",{type:"color",className:"cp-input-color",value:t}):this.itemType==="number"?i=o("input",{type:"number",className:"cp-input-number cp-input-small",step:"any",value:t}):i=o("input",{type:"text",className:"cp-input-number cp-input-small",value:t}),i.addEventListener("input",r=>{this.items[e]=r.target.value,this.updateValue()});const a=j(()=>{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 ut{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=o("span",{className:"cp-stats"}),this.rafId=requestAnimationFrame(this.render)}destroy(){cancelAnimationFrame(this.rafId)}}class D{constructor(){this.controllers=[],this.folders=[]}addNumber(t,e,s={}){const i=new Y(t,e,s);return this.contentElement.appendChild(i.domElement),this.controllers.push(i),i}addRange(t,e,s={}){const i=new Z(t,e,s);return this.contentElement.appendChild(i.domElement),this.controllers.push(i),i}addSelect(t,e,s={}){const i=new K(t,e,s);return this.contentElement.appendChild(i.domElement),this.controllers.push(i),i}addBoolean(t,e,s={}){const i=new tt(t,e,s);return this.contentElement.appendChild(i.domElement),this.controllers.push(i),i}addButton(t,e,s={}){const i=new Q(t,e,s);return this.contentElement.appendChild(i.domElement),this.controllers.push(i),i}addRadio(t,e,s={}){const i=new et(t,e,s);return this.contentElement.appendChild(i.domElement),this.controllers.push(i),i}addColor(t,e,s={}){const i=new st(t,e,s);return this.contentElement.appendChild(i.domElement),this.controllers.push(i),i}addGradient(t,e,s={}){const i=new nt(t,e,s);return this.contentElement.appendChild(i.domElement),this.controllers.push(i),i}addArray(t,e,s={}){const i=new at(t,e,s);return this.contentElement.appendChild(i.domElement),this.controllers.push(i),i}addFolder(t){const e=new ot(t);return this.addSeparator(),this.contentElement.appendChild(e.domElement),this.folders.push(e),e}addSeparator(){const t=o("hr",{className:"cp-separator"});this.contentElement.appendChild(t)}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 ot extends D{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)}}class mt extends D{constructor(t,e={}){super(),lt(),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 s=o("span",{},[e.title||"ControlPanel"]);this.summaryElement.appendChild(s),this.stats=new ut,this.summaryElement.appendChild(this.stats.domElement);let i=!1,a=0,r=0,l=0,p=0;this.summaryElement.addEventListener("mousedown",c=>{if(c.target!==this.summaryElement&&c.target!==s)return;i=!0,a=c.clientX,r=c.clientY;const g=this.domElement.getBoundingClientRect();l=g.left,p=g.top,c.preventDefault()}),document.addEventListener("mousemove",c=>{if(!i)return;const g=c.clientX-a,v=c.clientY-r,w=l+g,z=p+v;this.domElement.style.left=`${w}px`,this.domElement.style.top=`${z}px`,this.domElement.style.right="auto",this.domElement.style.bottom="auto"}),document.addEventListener("mouseup",()=>{i&&(i=!1,this.savePositionAndSize())}),new ResizeObserver(()=>{i||this.savePositionAndSize()}).observe(this.domElement),this.restorePositionAndSize(),this.contentElement=o("div",{className:"cp-content"}),this.domElement.appendChild(this.contentElement);const m=this.addFolder("_Signals"),b={audioInput:null,fftSize:2048};m.addRadio(b,"audioInput",{label:"Audio Signal",options:["microphone","browser"]}).onChange(c=>{E.setInput(c)}),m.addSelect(b,"fftSize",{label:"FFT Size",options:[256,512,1024,2048]}).onChange(c=>{E.setFFTSize(c)}),m.addRange(E,"smoothingTimeConstant",{min:0,max:.99,step:.01,label:"Smoothing"}).onChange(c=>{E.analyser.smoothingTimeConstant=c}),m.addRange(E,"spectrumBoost",{min:1,max:5,step:.1,label:"Compression"}),t?t.appendChild(this.domElement):document.body.appendChild(this.domElement);const y=e.title||"ControlPanel";this.presetStoragePrefix=`cp-presets-${y}-`;const S=this.addFolder("_User Presets"),k=()=>{const c=["Default"];if(typeof localStorage>"u")return c;for(let g=0;g<localStorage.length;g++){const v=localStorage.key(g);if(v&&v.startsWith(this.presetStoragePrefix)){const w=v.substring(this.presetStoragePrefix.length);w!=="Default"&&!c.includes(w)&&c.push(w)}}return c.sort()},f={selected:"Default",save:()=>{const c=prompt("Preset Name:",f.selected);if(c){if(c==="Default"){alert("Cannot overwrite Default preset");return}const g=this.presetStoragePrefix+c;this.saveToLocalStorage(g);const v=k();x.setOptions(v),f.selected=c,x.setValue(c)}},load:()=>{const c=f.selected,g=this.presetStoragePrefix+c;this.loadFromLocalStorage(g),f.selected=c,x.setValue(c)},delete:()=>{if(f.selected==="Default"){alert("Cannot delete Default preset");return}if(confirm(`Delete preset "${f.selected}"?`)){const c=this.presetStoragePrefix+f.selected;localStorage.removeItem(c);const g=k();x.setOptions(g),f.selected="Default",x.setValue("Default"),this.reset()}},export:()=>{const c=this.save(),g=O=>{const P={controllers:{},folders:{}};for(const[V,q]of Object.entries(O.controllers))V.startsWith("_")||(P.controllers[V]=q);for(const[V,q]of Object.entries(O.folders))V.startsWith("_")||(P.folders[V]=g(q));return P},v=g(c),w={_presetName:f.selected||"CustomPreset",_exportDate:new Date().toISOString(),_instructions:"To add as factory preset: Copy 'controllers' and 'folders' fields into the presets.json file",...v},z=JSON.stringify(w,null,2),I=new Blob([z],{type:"application/json"}),T=URL.createObjectURL(I),C=document.createElement("a");C.href=T;const M=new Date().toISOString().split("T")[0],rt=f.selected.replace(/[^a-z0-9]/gi,"-").toLowerCase();C.download=`${y.toLowerCase()}-preset-${rt}-${M}.json`,document.body.appendChild(C),C.click(),document.body.removeChild(C),URL.revokeObjectURL(T)},import:()=>{const c=document.createElement("input");c.type="file",c.accept=".json",c.onchange=g=>{const v=g.target.files?.[0];if(!v)return;const w=new FileReader;w.onload=z=>{try{const I=z.target?.result,T=JSON.parse(I),C={controllers:T.controllers||{},folders:T.folders||{}};if(!C.controllers||!C.folders){alert("Invalid preset file: missing 'controllers' or 'folders'");return}this.load(C);const M=T._presetName||"ImportedPreset";if(confirm(`Preset loaded! Save as "${M}" to User Presets?`)){const O=this.presetStoragePrefix+M;this.saveToLocalStorage(O);const P=k();x.setOptions(P),f.selected=M,x.setValue(M)}}catch(I){alert(`Failed to import preset: ${I instanceof Error?I.message:"Invalid JSON"}`),console.error("Import error:",I)}},w.readAsText(v)},c.click()}},B=k(),x=S.addSelect(f,"selected",{label:"Preset",options:B});S.addButton("Load",()=>f.load()),S.addButton("Save",()=>f.save()),S.addButton("Delete",()=>f.delete()),S.addButton("Export",()=>f.export()),S.addButton("Import",()=>f.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)}savePositionAndSize(){const t=this.domElement.getBoundingClientRect(),e=`cp-position-${this.presetStoragePrefix}`,s={left:t.left,top:t.top,width:this.domElement.offsetWidth,height:this.domElement.offsetHeight};try{sessionStorage.setItem(e,JSON.stringify(s))}catch(i){console.warn("Failed to save panel position/size",i)}}restorePositionAndSize(){const t=`cp-position-${this.presetStoragePrefix}`;try{const e=sessionStorage.getItem(t);if(e){const s=JSON.parse(e);this.domElement.style.left=`${s.left}px`,this.domElement.style.top=`${s.top}px`,this.domElement.style.right="auto",this.domElement.style.bottom="auto",this.domElement.style.width=`${s.width}px`,this.domElement.style.height=`${s.height}px`}}catch(e){console.warn("Failed to restore panel position/size",e)}}destroy(){this.stats.destroy(),this.domElement.remove(),this.controllers=[],this.folders=[]}}h.ArrayController=at,h.AudioSignals=H,h.BooleanController=tt,h.ButtonController=Q,h.ColorController=st,h.ControlPanel=mt,h.ControlPanelContainer=D,h.Controller=d,h.Folder=ot,h.GradientController=nt,h.MathSignals=_,h.MidiSignals=U,h.NumberController=Y,h.RadioController=et,h.RangeController=Z,h.SelectController=K,h.audioSignals=E,h.mathSignals=J,h.midiSignals=W,Object.defineProperty(h,Symbol.toStringTag,{value:"Module"})}));
395
+ `;let R=!1;function lt(){if(R)return;const n=document.createElement("style");n.id="control-panel-styles",n.textContent=L,document.head.appendChild(n),R=!0}function o(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 j(n){const t=o("button",{className:"cp-button cp-button-delete"},["×"]);return t.addEventListener("click",n),t}function ct(n){return n.replace(/([A-Z])/g," $1").replace(/^./,t=>t.toUpperCase()).trim()}function $(n,t,e){return Math.min(Math.max(n,t),e)}function pt(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],r=e[s],l=e[s+1];return(n-i)/(a-i)*(l-r)+r}class H{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 u=0;u<p;u++){const b=1+u/p*(this.spectrumBoost-1);this.dataArray[u]=Math.min(255,this.dataArray[u]*b)}}const t=[2,10],e=[10,150],s=[150,600],i=this.getAverage(t[0],t[1]),a=this.getAverage(e[0],e[1]),r=this.getAverage(s[0],s[1]),l=this.getAverage(0,s[1]);this.processLevel("bass",i),this.processLevel("mids",a),this.processLevel("highs",r),this.processLevel("volume",l)}processLevel(t,e){this.peaks[t]-=5e-4,this.peaks[t]=$(this.peaks[t],.1,1),e>this.peaks[t]&&(this.peaks[t]=e),this.levels[t]=$(pt(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 E=new H;class U{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),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,[s,i]=e,a=s&240,r=t.currentTarget.name||"unknown",l=a===144||a===128?"note":"ctrl",p=r.replace(/[^a-zA-Z0-9]/g,"");return`${i}_${l}_${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 W=new U;class _{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,r=(Math.sin(s*e.frequency*Math.PI*2+i)+1)/2;return this.normalizeOutput(r,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,r=a<.5?a*2:2-a*2;return this.normalizeOutput(r,e)}square(t){const e=t?{...this.configs.get("square"),...t}:this.configs.get("square"),s=this.getTime(),i=e.phase,r=(s*e.frequency+i)%1<.5?0:1;return this.normalizeOutput(r,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 _,z=class z{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=o("div",{className:"cp-controller"});const i=s.label??ct(e),a=o("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)}};z.audio=E,z.midi=W,z.math=J;let d=z;class Y extends d{constructor(t,e,s={}){super(t,e,s),this.input=o("input",{type:"number",className:"cp-input-number",value:String(this.value)}),this.input.addEventListener("input",()=>{}),this.input.addEventListener("blur",()=>{const i=parseFloat(this.input.value);isNaN(i)?this.input.value=String(this.value):this.setValue(i)}),this.input.addEventListener("keydown",i=>{i.key==="Enter"&&this.input.blur()}),this.appendWidget(this.input)}updateDisplay(){this.input.value=String(this.value)}}const X={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 G{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=d.midi.getSignal(this.currentMidiId)()):["constant","sine","sawtooth","triangle","square","random"].includes(this.currentSignalType)?e=d.math.getSignal(this.currentSignalType)():["volume","bass","mids","highs"].includes(this.currentSignalType)&&(e=d.audio.getSignal(this.currentSignalType)());const s=X[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"],l=>this.setSignalType(l));this.signalSelect=e.select,t.appendChild(e.row),this.midiRow=o("div",{className:"cp-setting-row",style:"display: none;"});const s=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..."){d.midi.cancelListen(),this.setMidiId(null);return}this.midiBtn.textContent="Listening...";const l=await d.midi.listen();this.setMidiId(l)}),this.midiRow.appendChild(s),this.midiRow.appendChild(this.midiBtn),t.appendChild(this.midiRow),this.mathParamsContainer=o("div",{style:"display: none; flex-direction: column; gap: 4px;"}),t.appendChild(this.mathParamsContainer);const i=this.createSettingSelect("behaviour",["forward","backward","loopForward","loopBackward","pingpong"],l=>this.setBehaviour(l));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(X),l=>this.setEase(l));this.easeRow=a.row,this.easeSelect=a.select,this.easeRow.style.display="none",this.easeSelect.value=this.currentEase,t.appendChild(this.easeRow);const r=o("hr",{className:"cp-separator"});t.appendChild(r)}createSettingSelect(t,e,s){const i=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(l=>{const p=o("option",{value:l},[l]);r.appendChild(p)}),r.addEventListener("change",()=>s(r.value)),i.appendChild(a),i.appendChild(r),{row:i,select:r}}createNumberInput(t,e,s,i,a,r){const l=o("div",{className:"cp-setting-row"}),p=o("label",{className:"cp-setting-label"},[t]),u=o("input",{className:"cp-input-number cp-input-small",type:"number",value:String(e),min:String(s),max:String(i),step:String(a)});return u.addEventListener("input",()=>{const m=parseFloat(u.value);isNaN(m)||r(m)}),l.appendChild(p),l.appendChild(u),{row:l,input:u}}updateMathParams(t){this.mathParamsContainer.innerHTML="";const e=d.math.getConfig(t);let s="Frequency",i=.1,a=10,r=.1;t==="random"?a=30:t==="constant"&&(s="Speed",i=.01,a=2,r=.01);const l=this.createNumberInput(s,e.frequency,i,a,r,m=>d.math.setConfig(t,{frequency:m}));this.mathParamsContainer.appendChild(l.row);const p=this.createNumberInput("Amplitude",e.amplitude,0,2,.1,m=>d.math.setConfig(t,{amplitude:m}));this.mathParamsContainer.appendChild(p.row);const u=this.createNumberInput("Offset",e.offset,0,1,.1,m=>d.math.setConfig(t,{offset:m}));if(this.mathParamsContainer.appendChild(u.row),["sine","sawtooth","triangle","square"].includes(t)){const m=this.createNumberInput("Phase",e.phase,0,1,.01,b=>d.math.setConfig(t,{phase:b}));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?"grid":"none",this.mathParamsContainer.style.display=s?"grid":"none",s&&this.updateMathParams(t),this.easeRow.style.display="grid",this.behaviourRow.style.display="grid",i?(this.currentMidiId=null,d.audio.ctx.state==="suspended"&&d.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 Z extends d{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=o("details",{className:"cp-controller-details"}),a=o("summary",{className:"cp-controller-summary"});this.input=o("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=o("span",{className:"cp-value-display"},[String(this.value.toFixed(1))]),this.input.addEventListener("input",()=>{const v=parseFloat(this.input.value);isNaN(v)||(this.setValue(v),this.display.textContent=String(v.toFixed(1)))}),this.input.addEventListener("click",v=>{v.stopPropagation()});const r=o("div",{className:"cp-controller-summary-content"});r.appendChild(this.input),r.appendChild(this.display),a.appendChild(r),i.appendChild(a);const l=o("div",{className:"cp-number-settings"}),p=this.createSetting("min",s.min,v=>this.setMin(v));this.minInput=p.input,l.appendChild(p.row);const u=this.createSetting("max",s.max,v=>this.setMax(v));this.maxInput=u.input,l.appendChild(u.row);const m=this.createSetting("step",s.step,v=>this.setStep(v));this.stepInput=m.input,l.appendChild(m.row);const b=o("hr",{className:"cp-separator"});l.appendChild(b),this.signalHandler=new G({container:l,onChange:(v,S)=>this.applySignal(v,S)}),i.appendChild(l),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=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",()=>s(r.value)),i.appendChild(a),i.appendChild(r),{row:i,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 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 K extends d{constructor(t,e,s){super(t,e,s),this.optionValues=[],this.select=o("select",{className:"cp-select"}),this.optionValues=s.options||[],this.optionValues.forEach((i,a)=>{const r=o("option",{value:String(a)},[String(i)]);this.select.appendChild(r)}),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=o("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 Q extends d{constructor(t,e,s={}){const i={action:e};super(i,"action",s),this.domElement.querySelector(".cp-label")?.remove();const a=s.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 tt extends d{constructor(t,e,s={}){super(t,e,s),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 et extends d{constructor(t,e,s){super(t,e,s),this.buttons=[],this.optionValues=[],this.container=o("div",{className:"cp-radios"}),this.optionValues=s.options||[],this.optionValues.forEach(i=>{const a=o("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 st extends d{constructor(t,e,s={}){super(t,e,s),this.input=o("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 it(n){const t=/^#?([a-f\d])([a-f\d])([a-f\d])$/i;n=n.replace(t,(s,i,a,r)=>i+i+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 ht(n,t,e){return"#"+((1<<24)+(Math.round(n)<<16)+(Math.round(t)<<8)+Math.round(e)).toString(16).slice(1)}function N(n){const t=n/255;return t<=.04045?t/12.92:Math.pow((t+.055)/1.055,2.4)}function F(n){return n<=.0031308?n*12.92*255:(1.055*Math.pow(n,1/2.4)-.055)*255}function dt(n,t,e){const[s,i,a]=it(n),[r,l,p]=it(t),u=N(s),m=N(i),b=N(a),v=N(r),S=N(l),T=N(p),f=u+e*(v-u),B=m+e*(S-m),C=b+e*(T-b),c=F(f),g=F(B),y=F(C);return ht(c,g,y)}class nt extends d{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=o("details",{className:"cp-controller-details"}),a=o("summary",{className:"cp-controller-summary"}),r=o("div",{className:"cp-controller-summary-content"});this.displayText=o("span",{className:"cp-value-display"},[String(this.value)]),r.appendChild(this.displayText),a.appendChild(r),i.appendChild(a);const l=o("div",{className:"cp-number-settings"});this.stopsContainer=o("div",{className:"cp-stops-container"}),this.renderStops(),l.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()}),l.appendChild(p);const u=o("hr",{className:"cp-separator"});l.appendChild(u),this.signalHandler=new G({container:l,onChange:(m,b)=>this.applySignal(m,b)}),i.appendChild(l),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=o("div",{className:"cp-gradient-stop-row"}),i=o("input",{type:"color",className:"cp-input-color",value:t.color});i.addEventListener("input",l=>{t.color=l.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",l=>{let p=parseFloat(l.target.value);isNaN(p)&&(p=0),t.position=Math.max(0,Math.min(1,p)),this.sortStops(),this.renderStops(),this.updateOutput()});const r=j(()=>{this.stops.splice(e,1),this.renderStops(),this.updateOutput()});s.appendChild(i),s.appendChild(a),s.appendChild(r),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 r=a.position-i.position,l=r===0?0:(t-i.position)/r;e=dt(i.color,a.color,l);break}}this.setValue(e),this.updateDisplay()}updateDisplay(){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 at extends d{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=o("details",{className:"cp-controller-details"}),a=o("summary",{className:"cp-controller-summary"}),r=o("div",{className:"cp-controller-summary-content"}),l=o("span",{className:"cp-value-display"},[`${this.items.length} items`]);r.appendChild(l),a.appendChild(r),i.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 u=o("button",{className:"cp-button cp-input-small",style:"margin-top: 8px; width: 100%;"},["+ Add Item"]);u.addEventListener("click",()=>{this.addItem()}),p.appendChild(u),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=o("div",{className:"cp-array-row"});let i;this.itemType==="color"?i=o("input",{type:"color",className:"cp-input-color",value:t}):this.itemType==="number"?i=o("input",{type:"number",className:"cp-input-number cp-input-small",step:"any",value:t}):i=o("input",{type:"text",className:"cp-input-number cp-input-small",value:t}),i.addEventListener("input",r=>{this.items[e]=r.target.value,this.updateValue()});const a=j(()=>{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 ut{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=o("span",{className:"cp-stats"}),this.rafId=requestAnimationFrame(this.render)}destroy(){cancelAnimationFrame(this.rafId)}}class D{constructor(){this.controllers=[],this.folders=[]}addNumber(t,e,s={}){const i=new Y(t,e,s);return this.contentElement.appendChild(i.domElement),this.controllers.push(i),i}addRange(t,e,s={}){const i=new Z(t,e,s);return this.contentElement.appendChild(i.domElement),this.controllers.push(i),i}addSelect(t,e,s={}){const i=new K(t,e,s);return this.contentElement.appendChild(i.domElement),this.controllers.push(i),i}addBoolean(t,e,s={}){const i=new tt(t,e,s);return this.contentElement.appendChild(i.domElement),this.controllers.push(i),i}addButton(t,e,s={}){const i=new Q(t,e,s);return this.contentElement.appendChild(i.domElement),this.controllers.push(i),i}addRadio(t,e,s={}){const i=new et(t,e,s);return this.contentElement.appendChild(i.domElement),this.controllers.push(i),i}addColor(t,e,s={}){const i=new st(t,e,s);return this.contentElement.appendChild(i.domElement),this.controllers.push(i),i}addGradient(t,e,s={}){const i=new nt(t,e,s);return this.contentElement.appendChild(i.domElement),this.controllers.push(i),i}addArray(t,e,s={}){const i=new at(t,e,s);return this.contentElement.appendChild(i.domElement),this.controllers.push(i),i}addFolder(t){const e=new ot(t);return this.addSeparator(),this.contentElement.appendChild(e.domElement),this.folders.push(e),e}addSeparator(){const t=o("hr",{className:"cp-separator"});this.contentElement.appendChild(t)}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 ot extends D{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)}}class mt extends D{constructor(t,e={}){super(),lt(),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 s=o("span",{},[e.title||"ControlPanel"]);this.summaryElement.appendChild(s),this.stats=new ut,this.summaryElement.appendChild(this.stats.domElement);let i=!1,a=0,r=0,l=0,p=0;this.summaryElement.addEventListener("mousedown",c=>{if(c.target!==this.summaryElement&&c.target!==s)return;i=!0,a=c.clientX,r=c.clientY;const g=this.domElement.getBoundingClientRect();l=g.left,p=g.top,c.preventDefault()}),document.addEventListener("mousemove",c=>{if(!i)return;const g=c.clientX-a,y=c.clientY-r,w=l+g,A=p+y;this.domElement.style.left=`${w}px`,this.domElement.style.top=`${A}px`,this.domElement.style.right="auto",this.domElement.style.bottom="auto"}),document.addEventListener("mouseup",()=>{i&&(i=!1,this.savePositionAndSize())}),new ResizeObserver(()=>{i||this.savePositionAndSize()}).observe(this.domElement),this.restorePositionAndSize(),this.contentElement=o("div",{className:"cp-content"}),this.domElement.appendChild(this.contentElement);const m=this.addFolder("_Signals"),b={audioInput:null,fftSize:2048};m.addRadio(b,"audioInput",{label:"Audio Signal",options:["microphone","browser"]}).onChange(c=>{E.setInput(c)}),m.addSelect(b,"fftSize",{label:"FFT Size",options:[256,512,1024,2048]}).onChange(c=>{E.setFFTSize(c)}),m.addRange(E,"smoothingTimeConstant",{min:0,max:.99,step:.01,label:"Smoothing"}).onChange(c=>{E.analyser.smoothingTimeConstant=c}),m.addRange(E,"spectrumBoost",{min:1,max:5,step:.1,label:"Compression"}),t?t.appendChild(this.domElement):document.body.appendChild(this.domElement);const v=e.title||"ControlPanel";this.presetStoragePrefix=`cp-presets-${v}-`;const S=this.addFolder("_User Presets"),T=()=>{const c=["Default"];if(typeof localStorage>"u")return c;for(let g=0;g<localStorage.length;g++){const y=localStorage.key(g);if(y&&y.startsWith(this.presetStoragePrefix)){const w=y.substring(this.presetStoragePrefix.length);w!=="Default"&&!c.includes(w)&&c.push(w)}}return c.sort()},f={selected:"Default",save:()=>{const c=prompt("Preset Name:",f.selected);if(c){if(c==="Default"){alert("Cannot overwrite Default preset");return}const g=this.presetStoragePrefix+c;this.saveToLocalStorage(g);const y=T();C.setOptions(y),f.selected=c,C.setValue(c)}},load:()=>{const c=f.selected,g=this.presetStoragePrefix+c;this.loadFromLocalStorage(g),f.selected=c,C.setValue(c)},delete:()=>{if(f.selected==="Default"){alert("Cannot delete Default preset");return}if(confirm(`Delete preset "${f.selected}"?`)){const c=this.presetStoragePrefix+f.selected;localStorage.removeItem(c);const g=T();C.setOptions(g),f.selected="Default",C.setValue("Default"),this.reset()}},export:()=>{const c=this.save(),g=O=>{const P={controllers:{},folders:{}};for(const[V,q]of Object.entries(O.controllers))V.startsWith("_")||(P.controllers[V]=q);for(const[V,q]of Object.entries(O.folders))V.startsWith("_")||(P.folders[V]=g(q));return P},y=g(c),w={_presetName:f.selected||"CustomPreset",_exportDate:new Date().toISOString(),_instructions:"To add as factory preset: Copy 'controllers' and 'folders' fields into the presets.json file",...y},A=JSON.stringify(w,null,2),I=new Blob([A],{type:"application/json"}),M=URL.createObjectURL(I),x=document.createElement("a");x.href=M;const k=new Date().toISOString().split("T")[0],rt=f.selected.replace(/[^a-z0-9]/gi,"-").toLowerCase();x.download=`${v.toLowerCase()}-preset-${rt}-${k}.json`,document.body.appendChild(x),x.click(),document.body.removeChild(x),URL.revokeObjectURL(M)},import:()=>{const c=document.createElement("input");c.type="file",c.accept=".json",c.onchange=g=>{const y=g.target.files?.[0];if(!y)return;const w=new FileReader;w.onload=A=>{try{const I=A.target?.result,M=JSON.parse(I),x={controllers:M.controllers||{},folders:M.folders||{}};if(!x.controllers||!x.folders){alert("Invalid preset file: missing 'controllers' or 'folders'");return}this.load(x);const k=M._presetName||"ImportedPreset";if(confirm(`Preset loaded! Save as "${k}" to User Presets?`)){const O=this.presetStoragePrefix+k;this.saveToLocalStorage(O);const P=T();C.setOptions(P),f.selected=k,C.setValue(k)}}catch(I){alert(`Failed to import preset: ${I instanceof Error?I.message:"Invalid JSON"}`),console.error("Import error:",I)}},w.readAsText(y)},c.click()}},B=T(),C=S.addSelect(f,"selected",{label:"Preset",options:B});S.addButton("Load",()=>f.load()),S.addButton("Save",()=>f.save()),S.addButton("Delete",()=>f.delete()),S.addButton("Export",()=>f.export()),S.addButton("Import",()=>f.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)}savePositionAndSize(){const t=this.domElement.getBoundingClientRect(),e=`cp-position-${this.presetStoragePrefix}`,s={left:t.left,top:t.top,width:this.domElement.offsetWidth,height:this.domElement.offsetHeight};try{sessionStorage.setItem(e,JSON.stringify(s))}catch(i){console.warn("Failed to save panel position/size",i)}}restorePositionAndSize(){const t=`cp-position-${this.presetStoragePrefix}`;try{const e=sessionStorage.getItem(t);if(e){const s=JSON.parse(e);this.domElement.style.left=`${s.left}px`,this.domElement.style.top=`${s.top}px`,this.domElement.style.right="auto",this.domElement.style.bottom="auto",this.domElement.style.width=`${s.width}px`,this.domElement.style.height=`${s.height}px`}}catch(e){console.warn("Failed to restore panel position/size",e)}}destroy(){this.stats.destroy(),this.domElement.remove(),this.controllers=[],this.folders=[]}}h.ArrayController=at,h.AudioSignals=H,h.BooleanController=tt,h.ButtonController=Q,h.ColorController=st,h.ControlPanel=mt,h.ControlPanelContainer=D,h.Controller=d,h.Folder=ot,h.GradientController=nt,h.MathSignals=_,h.MidiSignals=U,h.NumberController=Y,h.RadioController=et,h.RangeController=Z,h.SelectController=K,h.audioSignals=E,h.mathSignals=J,h.midiSignals=W,Object.defineProperty(h,Symbol.toStringTag,{value:"Module"})}));
@@ -1 +1 @@
1
- {"version":3,"file":"styles.d.ts","sourceRoot":"","sources":["../src/styles.ts"],"names":[],"mappings":"AAqZA,wBAAgB,YAAY,SAS3B"}
1
+ {"version":3,"file":"styles.d.ts","sourceRoot":"","sources":["../src/styles.ts"],"names":[],"mappings":"AA8YA,wBAAgB,YAAY,SAS3B"}
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@digitalmeadow/control-panel",
3
- "version": "1.0.11",
3
+ "version": "1.0.12",
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)",