@kitlangton/knobs 0.0.0-stage → 0.1.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (109) hide show
  1. package/LICENSE +21 -0
  2. package/README.md +1270 -2
  3. package/dist/dev/chunk-e13znr7c.js +155 -0
  4. package/dist/dev/chunk-kdewv0mt.js +1334 -0
  5. package/dist/dev/chunk-ktsn42p7.js +15668 -0
  6. package/dist/dev/chunk-qtt698hp.js +1118 -0
  7. package/dist/dev/index.js +1389 -0
  8. package/dist/index.js +2367 -0
  9. package/dist/panel.js +23635 -0
  10. package/dist/react.js +326 -0
  11. package/dist/solid.js +336 -0
  12. package/dist/themes.js +235 -0
  13. package/dist/types/core/adapter.d.ts +21 -0
  14. package/dist/types/core/code.d.ts +3 -0
  15. package/dist/types/core/color.d.ts +27 -0
  16. package/dist/types/core/easing.d.ts +21 -0
  17. package/dist/types/core/internal.d.ts +18 -0
  18. package/dist/types/core/keys.d.ts +21 -0
  19. package/dist/types/core/mutate.d.ts +21 -0
  20. package/dist/types/core/ramp.d.ts +150 -0
  21. package/dist/types/core/random.d.ts +2 -0
  22. package/dist/types/core/readout.d.ts +73 -0
  23. package/dist/types/core/remote.d.ts +258 -0
  24. package/dist/types/core/schema.d.ts +526 -0
  25. package/dist/types/core/spring.d.ts +38 -0
  26. package/dist/types/core/storage.d.ts +8 -0
  27. package/dist/types/core/store.d.ts +474 -0
  28. package/dist/types/core/theme.d.ts +27 -0
  29. package/dist/types/core/variants.d.ts +17 -0
  30. package/dist/types/index.d.ts +13 -0
  31. package/dist/types/panel/attach.d.ts +5 -0
  32. package/dist/types/panel/choice.d.ts +56 -0
  33. package/dist/types/panel/die.d.ts +121 -0
  34. package/dist/types/panel/editors.d.ts +159 -0
  35. package/dist/types/panel/folds.d.ts +14 -0
  36. package/dist/types/panel/give.d.ts +73 -0
  37. package/dist/types/panel/gpu/atlas.d.ts +40 -0
  38. package/dist/types/panel/gpu/color.d.ts +128 -0
  39. package/dist/types/panel/gpu/die.wgsl.d.ts +1 -0
  40. package/dist/types/panel/gpu/renderer.d.ts +712 -0
  41. package/dist/types/panel/gpu/shader.wgsl.d.ts +2 -0
  42. package/dist/types/panel/haptics.d.ts +8 -0
  43. package/dist/types/panel/has.d.ts +14 -0
  44. package/dist/types/panel/hints.d.ts +45 -0
  45. package/dist/types/panel/icons.d.ts +30 -0
  46. package/dist/types/panel/index.d.ts +10 -0
  47. package/dist/types/panel/inspector.d.ts +20 -0
  48. package/dist/types/panel/measure.d.ts +6 -0
  49. package/dist/types/panel/menu.d.ts +38 -0
  50. package/dist/types/panel/morph.d.ts +38 -0
  51. package/dist/types/panel/motion.d.ts +77 -0
  52. package/dist/types/panel/page-hints.d.ts +82 -0
  53. package/dist/types/panel/page.d.ts +423 -0
  54. package/dist/types/panel/panel.d.ts +43 -0
  55. package/dist/types/panel/position.d.ts +5 -0
  56. package/dist/types/panel/prefs.d.ts +28 -0
  57. package/dist/types/panel/presets.d.ts +48 -0
  58. package/dist/types/panel/ramps.d.ts +14 -0
  59. package/dist/types/panel/readout-row.d.ts +21 -0
  60. package/dist/types/panel/reveal.d.ts +15 -0
  61. package/dist/types/panel/rolling-css.d.ts +2 -0
  62. package/dist/types/panel/search.d.ts +36 -0
  63. package/dist/types/panel/seg.d.ts +15 -0
  64. package/dist/types/panel/session.d.ts +21 -0
  65. package/dist/types/panel/slider.d.ts +92 -0
  66. package/dist/types/panel/smear.d.ts +7 -0
  67. package/dist/types/panel/spin.d.ts +13 -0
  68. package/dist/types/panel/styles.d.ts +11 -0
  69. package/dist/types/panel/swipe.d.ts +114 -0
  70. package/dist/types/panel/switcher.d.ts +20 -0
  71. package/dist/types/panel/tabs.d.ts +31 -0
  72. package/dist/types/panel/thumb.d.ts +36 -0
  73. package/dist/types/react/Knobs.d.ts +15 -0
  74. package/dist/types/react/VariantSwitcher.d.ts +10 -0
  75. package/dist/types/react/index.d.ts +3 -0
  76. package/dist/types/react/useControls.d.ts +11 -0
  77. package/dist/types/solid/index.d.ts +50 -0
  78. package/dist/types/themes/index.d.ts +1 -0
  79. package/dist/types/ui/bento-css.d.ts +1 -0
  80. package/dist/types/ui/bento-model.d.ts +96 -0
  81. package/dist/types/ui/bento.d.ts +77 -0
  82. package/dist/types/ui/commands.d.ts +11 -0
  83. package/dist/types/ui/confirm.d.ts +13 -0
  84. package/dist/types/ui/index.d.ts +20 -0
  85. package/dist/types/ui/layer.d.ts +70 -0
  86. package/dist/types/ui/look.d.ts +53 -0
  87. package/dist/types/ui/menu.d.ts +63 -0
  88. package/dist/types/ui/overlay-css.d.ts +1 -0
  89. package/dist/types/ui/overlay-model.d.ts +87 -0
  90. package/dist/types/ui/palette.d.ts +38 -0
  91. package/dist/types/ui/popover.d.ts +35 -0
  92. package/dist/types/ui/react.d.ts +45 -0
  93. package/dist/types/ui/readout-css.d.ts +1 -0
  94. package/dist/types/ui/readout-model.d.ts +137 -0
  95. package/dist/types/ui/readouts.d.ts +85 -0
  96. package/dist/types/ui/rgba.d.ts +6 -0
  97. package/dist/types/ui/solid.d.ts +40 -0
  98. package/dist/types/ui/split-model.d.ts +81 -0
  99. package/dist/types/ui/split.d.ts +75 -0
  100. package/dist/types/ui/timeline-model.d.ts +83 -0
  101. package/dist/types/ui/timeline.d.ts +76 -0
  102. package/dist/types/ui/toast.d.ts +19 -0
  103. package/dist/types/ui/tooltip.d.ts +19 -0
  104. package/dist/types/ui/tree-model.d.ts +125 -0
  105. package/dist/types/ui/tree.d.ts +95 -0
  106. package/dist/ui-react.js +6558 -0
  107. package/dist/ui-solid.js +6511 -0
  108. package/dist/ui.js +9238 -0
  109. package/package.json +99 -4
@@ -0,0 +1,1118 @@
1
+ var __chunkGraphs, __chunkSeen, __chunkNonce, __preload = (id, seenOnly) => {
2
+ for (var [base, graph, ids] of __chunkGraphs || [])
3
+ for (var stack = [id], g = globalThis, d = g.document, head, j, node, k, link;j = stack.pop(); )
4
+ if (!__chunkSeen[j] && (node = graph[j])) {
5
+ __chunkSeen[j] = 1;
6
+ for (k = 1;k < node.length; k++)
7
+ stack.push(ids[node[k]]);
8
+ if (!seenOnly && j !== id && (head = d && d.head)) {
9
+ if (__chunkNonce === void 0)
10
+ __chunkNonce = (k = d.querySelector("meta[property=csp-nonce]")) && (k.nonce || k.getAttribute("nonce")) || "";
11
+ if (link = d.createElement("link"), link.rel = "modulepreload", link.crossOrigin = "", __chunkNonce)
12
+ link.nonce = __chunkNonce;
13
+ link.href = new g.URL(node[0], base), head.appendChild(link);
14
+ }
15
+ }
16
+ }, __chunks = (base, ids, nodes, entry) => {
17
+ for (var graph = {}, i = 0;i < ids.length; i++)
18
+ graph[ids[i]] = nodes[i];
19
+ (__chunkGraphs ||= []).push([base, graph, ids]), __chunkSeen ||= {}, __preload(ids[entry], 1);
20
+ };
21
+
22
+ // src/core/color.ts
23
+ var round = (n, places) => Number(n.toFixed(places)), clamp01 = (n) => Math.min(1, Math.max(0, n)), gamma = (c) => c <= 0.0031308 ? 12.92 * c : 1.055 * c ** 0.4166666666666667 - 0.055, linear = (c) => c <= 0.04045 ? c / 12.92 : ((c + 0.055) / 1.055) ** 2.4;
24
+ function oklchToLinear({ l, c, h }) {
25
+ let a = c * Math.cos(h * Math.PI / 180), b = c * Math.sin(h * Math.PI / 180), l_ = (l + 0.3963377774 * a + 0.2158037573 * b) ** 3, m_ = (l - 0.1055613458 * a - 0.0638541728 * b) ** 3, s_ = (l - 0.0894841775 * a - 1.291485548 * b) ** 3;
26
+ return [
27
+ 4.0767416621 * l_ - 3.3077115913 * m_ + 0.2309699292 * s_,
28
+ -1.2684380046 * l_ + 2.6097574011 * m_ - 0.3413193965 * s_,
29
+ -0.0041960863 * l_ - 0.7034186147 * m_ + 1.707614701 * s_
30
+ ];
31
+ }
32
+ var inGamut = (rgb) => rgb.every((v) => v >= -0.0001 && v <= 1.0001);
33
+ function maxChroma(l, h) {
34
+ if (l <= 0 || l >= 1)
35
+ return 0;
36
+ let lo = 0, hi = 0.37;
37
+ if (inGamut(oklchToLinear({ l, c: hi, h })))
38
+ return hi;
39
+ for (let i = 0;i < 18; i++) {
40
+ let mid = (lo + hi) / 2;
41
+ if (inGamut(oklchToLinear({ l, c: mid, h })))
42
+ lo = mid;
43
+ else
44
+ hi = mid;
45
+ }
46
+ return lo;
47
+ }
48
+ function oklchToRgb(color) {
49
+ let rgb = oklchToLinear(color);
50
+ if (!inGamut(rgb))
51
+ rgb = oklchToLinear({ ...color, c: Math.min(color.c, maxChroma(color.l, color.h)) });
52
+ let [r, g, b] = rgb;
53
+ return [clamp01(gamma(r)), clamp01(gamma(g)), clamp01(gamma(b))];
54
+ }
55
+ function rgbToOklch(r, g, b) {
56
+ let [lr, lg, lb] = [linear(r), linear(g), linear(b)], l_ = Math.cbrt(0.4122214708 * lr + 0.5363377612 * lg + 0.0514459929 * lb), m_ = Math.cbrt(0.2119034982 * lr + 0.6806995451 * lg + 0.1073969566 * lb), s_ = Math.cbrt(0.0883024619 * lr + 0.2817188376 * lg + 0.6299787005 * lb), L = 0.2104542553 * l_ + 0.793617785 * m_ - 0.0040720468 * s_, a = 1.9779984951 * l_ - 2.428592205 * m_ + 0.4505937099 * s_, bb = 0.0259040371 * l_ + 0.7827717662 * m_ - 0.808675766 * s_, c = Math.hypot(a, bb), h = c < 0.0001 ? 0 : (Math.atan2(bb, a) * 180 / Math.PI + 360) % 360;
57
+ return { l: L, c, h };
58
+ }
59
+ function parseColor2(input) {
60
+ if (typeof input === "object" && input !== null) {
61
+ let { l, c, h } = input;
62
+ return [l, c, h].every((n) => typeof n === "number" && Number.isFinite(n)) ? { l, c, h } : void 0;
63
+ }
64
+ if (typeof input !== "string")
65
+ return;
66
+ let s = input.trim().toLowerCase(), hex = /^#([0-9a-f]{3}|[0-9a-f]{6})$/.exec(s)?.[1];
67
+ if (hex) {
68
+ let full = hex.length === 3 ? [...hex].map((d) => d + d).join("") : hex, [r, g, b] = [0, 2, 4].map((i) => parseInt(full.slice(i, i + 2), 16) / 255);
69
+ return rgbToOklch(r, g, b);
70
+ }
71
+ let m = /^oklch\(\s*([\d.]+)(%?)\s+([\d.]+)\s+([\d.]+)(?:deg)?\s*\)$/.exec(s);
72
+ if (!m)
73
+ return;
74
+ let parts = [Number(m[1]) / (m[2] ? 100 : 1), Number(m[3]), Number(m[4])];
75
+ return parts.every(Number.isFinite) ? { l: parts[0], c: parts[1], h: parts[2] } : void 0;
76
+ }
77
+ function fitColor({ l, c, h }) {
78
+ let L = round(clamp01(l), 5), H = round((h % 360 + 360) % 360, 3) % 360, C0 = round(Math.min(0.37, Math.max(0, c)), 5);
79
+ if (inGamut(oklchToLinear({ l: L, c: C0, h: H })))
80
+ return { l: L, c: C0, h: H };
81
+ let max = Math.min(0.37, maxChroma(L, H));
82
+ return { l: L, c: Math.min(C0, Math.floor(max * 1e5) / 1e5), h: H };
83
+ }
84
+ function formatColor({ l, c, h }) {
85
+ return `oklch(${round(l, 5)} ${round(c, 5)} ${round(h, 3)})`;
86
+ }
87
+ function colorHex2(value) {
88
+ let color = parseColor2(value);
89
+ if (!color)
90
+ return "#000000";
91
+ return `#${oklchToRgb(color).map((v) => Math.round(v * 255).toString(16).padStart(2, "0")).join("")}`;
92
+ }
93
+
94
+ // src/core/easing.ts
95
+ var EASINGS2 = {
96
+ ease: [0.25, 0.1, 0.25, 1],
97
+ in: [0.42, 0, 1, 1],
98
+ out: [0, 0, 0.58, 1],
99
+ "in-out": [0.42, 0, 0.58, 1],
100
+ linear: [0, 0, 1, 1],
101
+ smooth: [0.65, 0, 0.35, 1],
102
+ snappy: [0.32, 0.72, 0, 1],
103
+ back: [0.34, 1.56, 0.64, 1]
104
+ }, EASING_Y = [-0.6, 1.6];
105
+ function namedEasing(name) {
106
+ let key = name.replace(/^ease-(?=in|out)/, "");
107
+ return Object.hasOwn(EASINGS2, key) ? EASINGS2[key] : void 0;
108
+ }
109
+ function easingName(value) {
110
+ return Object.entries(EASINGS2).find(([, b]) => b.every((n, i) => n === value[i]))?.[0];
111
+ }
112
+ function bezierPoint2([x1, y1, x2, y2], t) {
113
+ let u = 1 - t, a = 3 * u * u * t, b = 3 * u * t * t, c = t * t * t;
114
+ return [a * x1 + b * x2 + c, a * y1 + b * y2 + c];
115
+ }
116
+ var bezierCss2 = (b) => `cubic-bezier(${b.join(", ")})`;
117
+
118
+ // src/core/keys.ts
119
+ var MODS = ["Ctrl", "Alt", "Shift", "Meta"], ALIASES = {
120
+ ctrl: "Ctrl",
121
+ control: "Ctrl",
122
+ "⌃": "Ctrl",
123
+ alt: "Alt",
124
+ option: "Alt",
125
+ opt: "Alt",
126
+ "⌥": "Alt",
127
+ shift: "Shift",
128
+ "⇧": "Shift",
129
+ meta: "Meta",
130
+ cmd: "Meta",
131
+ command: "Meta",
132
+ "⌘": "Meta",
133
+ mod: "Mod"
134
+ }, NAMED = {
135
+ " ": "Space",
136
+ space: "Space",
137
+ spacebar: "Space",
138
+ esc: "Escape",
139
+ escape: "Escape",
140
+ enter: "Enter",
141
+ return: "Enter",
142
+ tab: "Tab",
143
+ backspace: "Backspace",
144
+ delete: "Delete",
145
+ del: "Delete",
146
+ up: "ArrowUp",
147
+ down: "ArrowDown",
148
+ left: "ArrowLeft",
149
+ right: "ArrowRight",
150
+ arrowup: "ArrowUp",
151
+ arrowdown: "ArrowDown",
152
+ arrowleft: "ArrowLeft",
153
+ arrowright: "ArrowRight",
154
+ home: "Home",
155
+ end: "End",
156
+ pageup: "PageUp",
157
+ pagedown: "PageDown"
158
+ }, isApple = () => typeof navigator < "u" && /mac|iphone|ipad|ipod/i.test(navigator.userAgentData?.platform ?? navigator.platform ?? ""), keyName = (raw) => {
159
+ if (!raw)
160
+ return "";
161
+ let named = NAMED[raw.toLowerCase()];
162
+ if (named)
163
+ return named;
164
+ if (raw.length === 1)
165
+ return raw.toUpperCase();
166
+ if (/^f\d{1,2}$/i.test(raw))
167
+ return raw.toUpperCase();
168
+ return raw.charAt(0).toUpperCase() + raw.slice(1);
169
+ };
170
+ function normalizeCombo2(combo) {
171
+ let trimmed = combo.trim();
172
+ if (!trimmed)
173
+ return "";
174
+ let parts = trimmed === "+" ? ["+"] : trimmed.replace(/\+\+$/, "+plus").split("+"), mods = /* @__PURE__ */ new Set, key = "";
175
+ for (let part of parts) {
176
+ let alias = ALIASES[part.trim().toLowerCase()] ?? ALIASES[part.trim()];
177
+ if (alias === "Mod")
178
+ mods.add(isApple() ? "Meta" : "Ctrl");
179
+ else if (alias)
180
+ mods.add(alias);
181
+ else
182
+ key = part === "plus" ? "+" : keyName(part.trim() || part);
183
+ }
184
+ if (!key)
185
+ return "";
186
+ return [...MODS.filter((m) => mods.has(m)), key].join("+");
187
+ }
188
+ var layout = null, layoutAsked = !1;
189
+ function askLayout() {
190
+ if (layoutAsked)
191
+ return;
192
+ layoutAsked = !0, globalThis.navigator?.keyboard?.getLayoutMap?.().then((map) => {
193
+ layout = map;
194
+ }).catch(() => {});
195
+ }
196
+ function letterOf2(event) {
197
+ askLayout();
198
+ let code = event.code ?? "";
199
+ if (/^[a-z]$/i.test(event.key) && !event.altKey)
200
+ return event.key.toUpperCase();
201
+ let mapped = layout?.get(code);
202
+ if (mapped && /^[a-z]$/i.test(mapped))
203
+ return mapped.toUpperCase();
204
+ return /^Key[A-Z]$/.test(code) ? code.slice(3) : "";
205
+ }
206
+ function comboFromEvent2(event) {
207
+ if (["Shift", "Alt", "Meta", "Control", "CapsLock", "Fn", "OS"].includes(event.key))
208
+ return "";
209
+ let code = event.code ?? "", letter = letterOf2(event), key = letter ? letter : /^Digit\d$/.test(code) ? code.slice(5) : code === "Space" ? "Space" : keyName(event.key);
210
+ return [
211
+ event.ctrlKey ? "Ctrl" : "",
212
+ event.altKey ? "Alt" : "",
213
+ event.shiftKey ? "Shift" : "",
214
+ event.metaKey ? "Meta" : "",
215
+ key
216
+ ].filter(Boolean).join("+");
217
+ }
218
+ function heldMods(event) {
219
+ return MODS.filter((m) => m === "Ctrl" ? event.ctrlKey : m === "Alt" ? event.altKey : m === "Shift" ? event.shiftKey : event.metaKey);
220
+ }
221
+ function matchesCombo2(combo, event) {
222
+ let want = normalizeCombo2(combo);
223
+ return want !== "" && want === comboFromEvent2(event);
224
+ }
225
+ var GLYPHS = { Ctrl: "⌃", Alt: "⌥", Shift: "⇧", Meta: "⌘" }, WORDS = { Ctrl: "Ctrl", Alt: "Alt", Shift: "Shift", Meta: "Meta" }, KEY_GLYPHS = {
226
+ ArrowUp: "↑",
227
+ ArrowDown: "↓",
228
+ ArrowLeft: "←",
229
+ ArrowRight: "→",
230
+ Backspace: "⌫",
231
+ Delete: "⌦",
232
+ Enter: "↩",
233
+ Escape: "Esc",
234
+ Tab: "⇥",
235
+ PageUp: "PgUp",
236
+ PageDown: "PgDn"
237
+ };
238
+ function comboParts2(combo, apple = isApple()) {
239
+ let norm = normalizeCombo2(combo);
240
+ if (!norm)
241
+ return [];
242
+ let parts = norm === "+" ? ["+"] : norm.split("+");
243
+ if (norm.endsWith("++"))
244
+ parts.splice(-2, 2, "+");
245
+ let key = parts.pop(), mods = parts, shown = KEY_GLYPHS[key] ?? key;
246
+ return apple ? [...mods.map((m) => GLYPHS[m]), shown] : [...mods.map((m) => WORDS[m]), shown];
247
+ }
248
+ function comboLabel2(combo, apple = isApple()) {
249
+ return comboParts2(combo, apple).join(apple ? "" : "+");
250
+ }
251
+
252
+ // src/core/ramp.ts
253
+ var MAX_STOPS = 32, MAX_POINTS = 64, GRADIENT_SAMPLES = 64, CURVE_SAMPLES = 129, clamp = (v, lo, hi) => Math.min(hi, Math.max(lo, v)), finite = (v) => typeof v === "number" && Number.isFinite(v), round4 = (v) => Math.round(v * 1e4) / 1e4 || 0;
254
+ function normalizeStops(input) {
255
+ if (!Array.isArray(input))
256
+ return;
257
+ let stops = [];
258
+ for (let s of input.slice(0, MAX_STOPS)) {
259
+ if (typeof s !== "object" || s === null)
260
+ continue;
261
+ let { at, color } = s, parsed = parseColor2(color);
262
+ if (!finite(at) || !parsed)
263
+ continue;
264
+ stops.push(Object.freeze({ at: round4(clamp(at, 0, 1)), color: formatColor(fitColor(parsed)) }));
265
+ }
266
+ if (stops.length < 2)
267
+ return;
268
+ return Object.freeze(stops.map((s, i) => [s, i]).sort((a, b) => a[0].at - b[0].at || a[1] - b[1]).map(([s]) => s));
269
+ }
270
+ function normalizePoints(input, range) {
271
+ if (!Array.isArray(input))
272
+ return;
273
+ let pts = [], span = range[1] - range[0], roundY = (y) => span > 0 ? Number((range[0] + Math.round((y - range[0]) / span * 1e5) / 1e5 * span).toPrecision(12)) : range[0];
274
+ for (let p of input.slice(0, MAX_POINTS)) {
275
+ if (typeof p !== "object" || p === null)
276
+ continue;
277
+ let { x, y } = p;
278
+ if (!finite(x) || !finite(y))
279
+ continue;
280
+ pts.push({ x: round4(clamp(x, 0, 1)), y: roundY(clamp(y, range[0], range[1])) });
281
+ }
282
+ if (pts.length < 2)
283
+ return;
284
+ let sorted = pts.map((p, i) => [p, i]).sort((a, b) => a[0].x - b[0].x || a[1] - b[1]).map(([p]) => p);
285
+ return sorted[0] = { ...sorted[0], x: 0 }, sorted[sorted.length - 1] = { ...sorted[sorted.length - 1], x: 1 }, Object.freeze(sorted.map((p) => Object.freeze(p)));
286
+ }
287
+ var toLab = ({ l, c, h }) => [
288
+ l,
289
+ c * Math.cos(h * Math.PI / 180),
290
+ c * Math.sin(h * Math.PI / 180)
291
+ ], fromLab = ([l, a, b]) => {
292
+ let c = Math.hypot(a, b);
293
+ return { l, c, h: c < 0.00001 ? 0 : (Math.atan2(b, a) * 180 / Math.PI + 360) % 360 };
294
+ }, linearOf = (c) => c <= 0.04045 ? c / 12.92 : ((c + 0.055) / 1.055) ** 2.4;
295
+ function prepare(stops, space) {
296
+ let at = stops.map((s) => s.at), ok = stops.map((s) => parseColor2(s.color) ?? { l: 0, c: 0, h: 0 }), lab = ok.map(toLab), rgb = ok.map((c) => oklchToRgb(c));
297
+ return (t) => {
298
+ let n = at.length, i = 0;
299
+ while (i < n && at[i] <= t)
300
+ i++;
301
+ if (i === 0)
302
+ return [...rgb[0]];
303
+ if (i === n)
304
+ return [...rgb[n - 1]];
305
+ let a = i - 1, span = at[i] - at[a], f = span > 0 ? (t - at[a]) / span : 1;
306
+ if (space === "srgb") {
307
+ let p = rgb[a], q = rgb[i];
308
+ return [p[0] + (q[0] - p[0]) * f, p[1] + (q[1] - p[1]) * f, p[2] + (q[2] - p[2]) * f];
309
+ }
310
+ let p = lab[a], q = lab[i];
311
+ return oklchToRgb(fromLab([p[0] + (q[0] - p[0]) * f, p[1] + (q[1] - p[1]) * f, p[2] + (q[2] - p[2]) * f]));
312
+ };
313
+ }
314
+ function sampleGradient2(stops, t, options = {}) {
315
+ let valid = normalizeStops(stops) ?? stops;
316
+ if (!valid.length)
317
+ return options.as === "rgb" || options.as === "linear" ? [0, 0, 0] : "#000000";
318
+ let rgb = prepare(valid, options.space ?? "oklab")(clamp(Number.isFinite(t) ? t : 0, 0, 1));
319
+ if (options.as === "rgb")
320
+ return rgb;
321
+ if (options.as === "linear")
322
+ return [linearOf(rgb[0]), linearOf(rgb[1]), linearOf(rgb[2])];
323
+ let color = formatColor(fitColor(rgbToOklch(...rgb)));
324
+ return options.as === "hex" ? colorHex2(color) : color;
325
+ }
326
+ function gradientSamples(stops, space, n = GRADIENT_SAMPLES) {
327
+ let at = prepare(stops, space), out = [];
328
+ for (let i = 0;i < n; i++)
329
+ out.push(...at(i / (n - 1)));
330
+ return out;
331
+ }
332
+ function gradientCss2(stops, options = {}) {
333
+ let valid = normalizeStops(stops) ?? stops, s = gradientSamples(valid, options.space ?? "oklab", 17), parts = [];
334
+ for (let i = 0;i < 17; i++) {
335
+ let [r, g, b] = [s[i * 3], s[i * 3 + 1], s[i * 3 + 2]].map((v) => Math.round(v * 255));
336
+ parts.push(`rgb(${r} ${g} ${b}) ${round4(i / 16 * 100)}%`);
337
+ }
338
+ return `linear-gradient(${options.direction ?? "to right"},${parts.join(",")})`;
339
+ }
340
+ function stopColorAt(stops, space, t) {
341
+ return formatColor(fitColor(rgbToOklch(...prepare(stops, space)(clamp(t, 0, 1)))));
342
+ }
343
+ function tangents(xs, ys) {
344
+ let n = xs.length, h = [], s = [];
345
+ for (let i = 0;i < n - 1; i++)
346
+ h.push(xs[i + 1] - xs[i]), s.push(h[i] > 0 ? (ys[i + 1] - ys[i]) / h[i] : 0);
347
+ let m = Array(n).fill(0);
348
+ if (n === 2)
349
+ return m[0] = m[1] = s[0], m;
350
+ let sign = Math.sign;
351
+ for (let i = 1;i < n - 1; i++) {
352
+ let h0 = h[i - 1], h1 = h[i];
353
+ if (h0 <= 0 || h1 <= 0)
354
+ continue;
355
+ let p = (s[i - 1] * h1 + s[i] * h0) / (h0 + h1);
356
+ m[i] = (sign(s[i - 1]) + sign(s[i])) * Math.min(Math.abs(s[i - 1]), Math.abs(s[i]), 0.5 * Math.abs(p));
357
+ }
358
+ let end = (a, b, ha, hb) => {
359
+ if (ha <= 0)
360
+ return 0;
361
+ let p = a * (1 + ha / (ha + hb)) - b * ha / (ha + hb);
362
+ if (sign(p) !== sign(a))
363
+ return 0;
364
+ return Math.abs(p) > 2 * Math.abs(a) ? 2 * a : p;
365
+ };
366
+ return m[0] = end(s[0], s[1], h[0], h[1]), m[n - 1] = end(s[n - 2], s[n - 3], h[n - 2], h[n - 3]), m;
367
+ }
368
+ function prepareCurve(points, interp) {
369
+ let xs = points.map((p) => p.x), ys = points.map((p) => p.y), m = interp === "smooth" ? tangents(xs, ys) : [], n = xs.length;
370
+ return (x) => {
371
+ if (!n)
372
+ return 0;
373
+ if (x <= xs[0])
374
+ return ys[0];
375
+ if (x >= xs[n - 1])
376
+ return ys[n - 1];
377
+ let i = 0;
378
+ while (i < n - 2 && xs[i + 1] <= x)
379
+ i++;
380
+ let x0 = xs[i], span = xs[i + 1] - x0;
381
+ if (interp === "step" || span <= 0)
382
+ return ys[i];
383
+ let t = (x - x0) / span;
384
+ if (interp === "linear")
385
+ return ys[i] + (ys[i + 1] - ys[i]) * t;
386
+ let t2 = t * t, t3 = t2 * t;
387
+ return (2 * t3 - 3 * t2 + 1) * ys[i] + (t3 - 2 * t2 + t) * span * m[i] + (-2 * t3 + 3 * t2) * ys[i + 1] + (t3 - t2) * span * m[i + 1];
388
+ };
389
+ }
390
+ function sampleCurve2(points, x, options = {}) {
391
+ return prepareCurve(points, options.interp ?? "smooth")(Number.isFinite(x) ? x : 0);
392
+ }
393
+ function curveSamples(points, interp, n = CURVE_SAMPLES) {
394
+ let at = prepareCurve(points, interp), xs = Array.from({ length: n }, (_, i) => i / (n - 1));
395
+ for (let p of points) {
396
+ let j = Math.round(p.x * (n - 1));
397
+ if (xs[j] = p.x, interp === "step" && j > 0 && p.x > 0)
398
+ xs[j - 1] = p.x;
399
+ }
400
+ xs.sort((a, b) => a - b);
401
+ let out = [];
402
+ for (let i = 0;i < n; i++) {
403
+ let x = xs[i], rising = interp === "step" && xs[i + 1] === x && xs[i - 1] !== x;
404
+ out.push(x, at(rising ? x - 0.000000001 : x));
405
+ }
406
+ return out;
407
+ }
408
+ var CURVE_SHAPES2 = {
409
+ linear: [
410
+ { x: 0, y: 0 },
411
+ { x: 1, y: 1 }
412
+ ],
413
+ "ease-out": [
414
+ { x: 0, y: 0 },
415
+ { x: 0.35, y: 0.62 },
416
+ { x: 1, y: 1 }
417
+ ],
418
+ "inverse²": [
419
+ { x: 0, y: 1 },
420
+ { x: 0.1, y: 0.85 },
421
+ { x: 0.25, y: 0.47 },
422
+ { x: 0.5, y: 0.15 },
423
+ { x: 1, y: 0 }
424
+ ],
425
+ smoothstep: [
426
+ { x: 0, y: 0 },
427
+ { x: 0.25, y: 0.156 },
428
+ { x: 0.5, y: 0.5 },
429
+ { x: 0.75, y: 0.844 },
430
+ { x: 1, y: 1 }
431
+ ],
432
+ bell: [
433
+ { x: 0, y: 0 },
434
+ { x: 0.25, y: 0.25 },
435
+ { x: 0.5, y: 1 },
436
+ { x: 0.75, y: 0.25 },
437
+ { x: 1, y: 0 }
438
+ ]
439
+ }, inRange = (shape, range) => shape.map((p) => ({ x: p.x, y: range[0] + p.y * (range[1] - range[0]) }));
440
+ function sameRamp(a, b) {
441
+ if (a.length !== b.length)
442
+ return !1;
443
+ return a.every((p, i) => {
444
+ let q = b[i];
445
+ if ("at" in p && "at" in q)
446
+ return Math.abs(p.at - q.at) < 0.0001 && p.color === q.color;
447
+ if ("x" in p && "x" in q)
448
+ return Math.abs(p.x - q.x) < 0.0001 && Math.abs(p.y - q.y) < 0.000001;
449
+ return !1;
450
+ });
451
+ }
452
+ var between = (min, max, r) => min + (max - min) * r, signed = (random) => random() < 0.5 ? -1 : 1;
453
+ function randomStops(count, random) {
454
+ let n = Math.max(2, count), hue = random() * 360, turn = signed(random) * between(20, 70, random()), rising = random() < 0.5, [l0, l1] = [between(0.2, 0.4, random()), between(0.75, 0.95, random())], vivid = between(0.3, 0.9, random());
455
+ return Array.from({ length: n }, (_, i) => {
456
+ let t = i / (n - 1), jitter = i === 0 || i === n - 1 ? 0 : (random() - 0.5) * (0.5 / (n - 1)), at = clamp(t + jitter, 0, 1), l = rising ? l0 + (l1 - l0) * t : l1 + (l0 - l1) * t, h = (hue + turn * t + 360) % 360, c = vivid * maxChroma(l, h) * between(0.7, 1, random());
457
+ return { at, color: formatColor(fitColor({ l, c, h })) };
458
+ });
459
+ }
460
+ function mutateStops(stops, random, hue, lightness) {
461
+ let out = stops.map((s) => ({ ...s }));
462
+ for (let [i, s] of out.entries()) {
463
+ let c = parseColor2(s.color) ?? { l: 0.5, c: 0, h: 0 }, max0 = maxChroma(c.l, c.h), rel = max0 > 0.0001 ? clamp(c.c / max0, 0, 1) : 0.5, h = (c.h + signed(random) * between(hue[0], hue[1], random()) + 360) % 360, l = clamp(c.l + signed(random) * between(lightness[0], lightness[1], random()), 0, 1), r = clamp(rel + signed(random) * between(0.04, 0.1, random()), 0, 1);
464
+ s.color = formatColor(fitColor({ l, c: r * maxChroma(l, h), h }));
465
+ let lo = i > 0 ? out[i - 1].at : 0, hi = i < out.length - 1 ? stops[i + 1].at : 1;
466
+ s.at = round4(clamp(s.at + signed(random) * between(0.02, 0.05, random()), lo, hi));
467
+ }
468
+ return out;
469
+ }
470
+ function randomPoints(range, random) {
471
+ let shapes = Object.values(CURVE_SHAPES2), shape = shapes[Math.floor(Math.min(0.999999, random()) * shapes.length)], flip = random() < 0.3;
472
+ return inRange(shape.map((p, i) => ({
473
+ x: p.x,
474
+ y: i === 0 || i === shape.length - 1 ? flip ? 1 - p.y : p.y : clamp((flip ? 1 - p.y : p.y) + (random() - 0.5) * 0.12, 0, 1)
475
+ })), range);
476
+ }
477
+ function mutatePoints(points, range, random, share) {
478
+ let span = range[1] - range[0], out = points.map((p) => ({ ...p }));
479
+ for (let [i, p] of out.entries()) {
480
+ let by = span * between(share[0], share[1], random()) * 0.6, y = p.y + signed(random) * by;
481
+ if (y > range[1] || y < range[0])
482
+ y = p.y - (y - p.y);
483
+ if (p.y = clamp(y, range[0], range[1]), i === 0 || i === out.length - 1)
484
+ continue;
485
+ let lo = out[i - 1].x + 0.01, hi = points[i + 1].x - 0.01;
486
+ if (hi > lo)
487
+ p.x = round4(clamp(p.x + signed(random) * between(0.02, 0.05, random()), lo, hi));
488
+ }
489
+ return out;
490
+ }
491
+
492
+ // src/core/schema.ts
493
+ var META_KEYS = [
494
+ "label",
495
+ "group",
496
+ "groupLabel",
497
+ "section",
498
+ "sectionLabel",
499
+ "groupDescription",
500
+ "sectionDescription",
501
+ "shortcut",
502
+ "random",
503
+ "disabled",
504
+ "readOnly",
505
+ "folded",
506
+ "sectionFolded",
507
+ "tab",
508
+ "subsection"
509
+ ];
510
+ function pickMeta(source, skip = []) {
511
+ let out = {};
512
+ if (source) {
513
+ for (let k of META_KEYS)
514
+ if (source[k] !== void 0 && !skip.includes(k))
515
+ out[k] = source[k];
516
+ }
517
+ return out;
518
+ }
519
+ var READOUT_KINDS = /* @__PURE__ */ new Set(["watch", "sparkline", "graph", "meter", "table", "log"]), isReadout2 = (field) => READOUT_KINDS.has(field.kind), hasValue2 = (field) => field.kind !== "action" && field.kind !== "toolbar" && !READOUT_KINDS.has(field.kind), SPRING2 = {
520
+ duration: { min: 0.05, max: 2, step: 0.01 },
521
+ bounce: { min: -0.5, max: 1, step: 0.01 }
522
+ };
523
+ function checkShortcut(shortcut) {
524
+ if (shortcut !== void 0 && !/^[a-z]$/i.test(shortcut))
525
+ throw Error("Shortcut must be a letter.");
526
+ }
527
+ function valueKeys(schema) {
528
+ return Object.keys(schema).filter((k) => hasValue2(schema[k]));
529
+ }
530
+ function withMeta(base, meta) {
531
+ return checkShortcut(meta?.shortcut), Object.freeze({ ...base, ...pickMeta(meta) });
532
+ }
533
+ function knob2(value, options) {
534
+ let step = options.step ?? ((options.max - options.min) / 100 || 1);
535
+ if (![value, options.min, options.max, step].every(Number.isFinite) || options.max < options.min || step <= 0)
536
+ throw Error("A knob needs finite bounds and a positive step.");
537
+ if (options.decimals !== void 0 && !(Number.isInteger(options.decimals) && options.decimals >= 0))
538
+ throw Error("A knob's decimals must be a whole number.");
539
+ checkShortcut(options.shortcut);
540
+ let field = { ...options, kind: "knob", value, step };
541
+ return Object.freeze({ ...field, value: normalizeField(field, value) });
542
+ }
543
+ function choice2(value, options, meta) {
544
+ if (!options.includes(value) || new Set(options).size !== options.length)
545
+ throw Error("Invalid choice options/default.");
546
+ let labels = meta?.labels ? Object.freeze(Object.fromEntries(Object.entries(meta.labels).filter(([k, v]) => options.includes(k) && typeof v === "string" && v !== k))) : void 0;
547
+ return withMeta({
548
+ kind: "choice",
549
+ value,
550
+ options: Object.freeze([...options]),
551
+ ...labels && Object.keys(labels).length ? { labels } : {},
552
+ ...meta?.display ? { display: meta.display } : {}
553
+ }, meta);
554
+ }
555
+ function action2(label, meta) {
556
+ return withMeta({ kind: "action", ...meta?.run ? { run: meta.run } : {} }, { ...meta, ...label !== void 0 ? { label } : {} });
557
+ }
558
+ function flatSpring2(parts = {}, meta = {}) {
559
+ let id = meta.id ?? meta.label ?? `spring-${++springIds}`, half = (part, input, fallback) => {
560
+ let base = typeof input === "object" ? input : knob2(input ?? fallback, {
561
+ ...SPRING2[part],
562
+ ...part === "duration" ? { unit: "s" } : {}
563
+ });
564
+ return Object.freeze({
565
+ ...base,
566
+ ...base.label === void 0 ? { label: part === "duration" ? "Duration" : "Bounce" } : {},
567
+ ...pickMeta(meta, ["label", "shortcut"]),
568
+ spring: Object.freeze({
569
+ id,
570
+ part,
571
+ ...meta.label !== void 0 ? { label: meta.label } : {}
572
+ })
573
+ });
574
+ };
575
+ return {
576
+ duration: half("duration", parts.duration, 0.5),
577
+ bounce: half("bounce", parts.bounce, 0)
578
+ };
579
+ }
580
+ var springIds = 0;
581
+ function toggle2(value, meta) {
582
+ return withMeta({ kind: "toggle", value }, meta);
583
+ }
584
+ function spring2(value = {}, meta) {
585
+ let field = { kind: "spring", value: { duration: 0.5, bounce: 0 } };
586
+ return withMeta({ ...field, value: normalizeField(field, value) }, meta);
587
+ }
588
+ function color2(value, meta) {
589
+ let parsed = parseColor2(value);
590
+ if (!parsed)
591
+ throw Error("A color needs #rgb, #rrggbb, oklch(L C H) or { l, c, h }.");
592
+ return withMeta({ kind: "color", value: formatColor(fitColor(parsed)) }, meta);
593
+ }
594
+ var axes = (v) => typeof v === "number" ? { x: v, y: v } : v;
595
+ function point2(value, options) {
596
+ let min = axes(options.min), max = axes(options.max), step = options.step === void 0 ? void 0 : axes(options.step), field = {
597
+ kind: "point",
598
+ value,
599
+ min: Object.freeze({ ...min }),
600
+ max: Object.freeze({ ...max }),
601
+ step: Object.freeze({
602
+ x: step?.x ?? ((max.x - min.x) / 100 || 1),
603
+ y: step?.y ?? ((max.y - min.y) / 100 || 1)
604
+ })
605
+ };
606
+ if (![value.x, value.y, min.x, min.y, max.x, max.y, field.step.x, field.step.y].every(Number.isFinite) || max.x < min.x || max.y < min.y || field.step.x <= 0 || field.step.y <= 0)
607
+ throw Error("A point needs finite bounds and positive steps.");
608
+ return withMeta({ ...field, value: normalizeField(field, value) }, options);
609
+ }
610
+ function easing2(value, meta) {
611
+ let curve = typeof value === "string" ? namedEasing(value) : value;
612
+ if (!curve || curve.length !== 4 || !curve.every(Number.isFinite))
613
+ throw Error("An easing needs four finite numbers or a known name.");
614
+ let field = { kind: "easing", value: curve };
615
+ return withMeta({ ...field, value: normalizeField(field, curve) }, meta);
616
+ }
617
+ function bounds(o, what) {
618
+ let step = o.step ?? ((o.max - o.min) / 100 || 1);
619
+ if (![o.min, o.max, step].every(Number.isFinite) || o.max < o.min || step <= 0)
620
+ throw Error(`${what} needs finite bounds and a positive step.`);
621
+ if (o.decimals !== void 0 && !(Number.isInteger(o.decimals) && o.decimals >= 0))
622
+ throw Error(`${what}'s decimals must be a whole number.`);
623
+ return step;
624
+ }
625
+ var numberMeta = (o) => ({
626
+ ...o.unit !== void 0 ? { unit: o.unit } : {},
627
+ ...o.decimals !== void 0 ? { decimals: o.decimals } : {}
628
+ });
629
+ function vec(axes, value, options) {
630
+ let step = bounds(options, "A vector"), names = options.axes;
631
+ if (names && (names.length !== axes || names.some((n) => typeof n !== "string" || !n.trim())))
632
+ throw Error(`A vector's axes need ${axes} names.`);
633
+ let field = {
634
+ kind: "vec",
635
+ axes,
636
+ value,
637
+ min: options.min,
638
+ max: options.max,
639
+ step,
640
+ ...numberMeta(options),
641
+ ...options.link || options.linked ? { link: !0 } : {},
642
+ ...options.linked ? { linked: !0 } : {},
643
+ ...names ? { axisLabels: Object.freeze(names.map((n) => n.trim())) } : {}
644
+ };
645
+ return withMeta({ ...field, value: normalizeField(field, value) }, options);
646
+ }
647
+ function vec22(value, options) {
648
+ return vec(2, value, options);
649
+ }
650
+ function vec32(value, options) {
651
+ return vec(3, value, options);
652
+ }
653
+ function range2(value, options) {
654
+ let step = bounds(options, "A range"), field = {
655
+ kind: "range",
656
+ value,
657
+ min: options.min,
658
+ max: options.max,
659
+ step,
660
+ ...numberMeta(options)
661
+ };
662
+ return withMeta({ ...field, value: normalizeField(field, value) }, options);
663
+ }
664
+ function angle2(value, options = {}) {
665
+ let min = options.min ?? 0, max = options.max ?? 360, step = options.step ?? 1;
666
+ bounds({ min, max, step }, "An angle");
667
+ let field = {
668
+ kind: "angle",
669
+ value,
670
+ min,
671
+ max,
672
+ step,
673
+ wrap: options.wrap ?? max - min === 360
674
+ };
675
+ return withMeta({ ...field, value: normalizeField(field, value) }, options);
676
+ }
677
+ function seed2(value, options = {}) {
678
+ let field = {
679
+ kind: "seed",
680
+ value,
681
+ min: Math.ceil(options.min ?? 0),
682
+ max: Math.floor(options.max ?? 999999)
683
+ };
684
+ return bounds({ min: field.min, max: field.max, step: 1 }, "A seed"), withMeta({ ...field, value: normalizeField(field, value) }, { random: !1, ...options });
685
+ }
686
+ function keybind2(value = "", meta) {
687
+ let field = { kind: "keybind", value: normalizeCombo2(value) };
688
+ return withMeta(field, { random: !1, ...meta });
689
+ }
690
+ function gradient2(stops, options = {}) {
691
+ let value = normalizeStops(stops);
692
+ if (!value)
693
+ throw Error("A gradient needs at least two stops with a position and a colour.");
694
+ let presets = options.presets ? Object.freeze(Object.fromEntries(Object.entries(options.presets).flatMap(([name, p]) => {
695
+ let s = normalizeStops(p);
696
+ return s ? [[name, s]] : [];
697
+ }))) : void 0;
698
+ return withMeta({
699
+ kind: "gradient",
700
+ value,
701
+ space: options.space ?? "oklab",
702
+ ...presets && Object.keys(presets).length ? { presets } : {}
703
+ }, options);
704
+ }
705
+ function curve2(points, options = {}) {
706
+ let range = options.range ?? [0, 1];
707
+ if (!(Number.isFinite(range[0]) && Number.isFinite(range[1]) && range[1] > range[0]))
708
+ throw Error("A curve's range needs finite bounds, max above min.");
709
+ let given = typeof points === "string" ? inRange(CURVE_SHAPES2[points], range) : points, value = normalizePoints(given, range);
710
+ if (!value)
711
+ throw Error("A curve needs at least two points.");
712
+ let presets = options.presets ? Object.freeze(Object.fromEntries(Object.entries(options.presets).flatMap(([name, p]) => {
713
+ let s = normalizePoints(p, range);
714
+ return s ? [[name, s]] : [];
715
+ }))) : void 0;
716
+ return withMeta({
717
+ kind: "curve",
718
+ value,
719
+ range: Object.freeze([range[0], range[1]]),
720
+ interp: options.interp ?? "smooth",
721
+ shapes: options.shapes ?? !0,
722
+ ...presets && Object.keys(presets).length ? { presets } : {}
723
+ }, options);
724
+ }
725
+ function toolbarShortcut2(item) {
726
+ let s = item.shortcut;
727
+ if (!s)
728
+ return;
729
+ if (typeof s === "string")
730
+ return { key: s, scope: item.global ? "page" : "panel" };
731
+ return s.key ? { key: s.key, scope: s.scope ?? (item.global ? "page" : "panel") } : void 0;
732
+ }
733
+ function toolbar2(items, meta) {
734
+ if (!items.length)
735
+ throw Error("A toolbar needs at least one item.");
736
+ return withMeta({
737
+ kind: "toolbar",
738
+ items: Object.freeze(items.map((item) => Object.freeze({ ...item })))
739
+ }, meta);
740
+ }
741
+ var readoutMeta = (o) => {
742
+ if (o.decimals !== void 0 && !(Number.isInteger(o.decimals) && o.decimals >= 0))
743
+ throw Error("A readout's decimals must be a whole number.");
744
+ if (o.hz !== void 0 && !(o.hz > 0))
745
+ throw Error("A readout's hz must be above 0.");
746
+ return {
747
+ ...pickMeta(o),
748
+ ...o.get ? { get: o.get } : {},
749
+ ...o.unit !== void 0 ? { unit: o.unit } : {},
750
+ ...o.decimals !== void 0 ? { decimals: o.decimals } : {},
751
+ ...o.format ? { format: o.format } : {},
752
+ ...o.hz !== void 0 ? { hz: o.hz } : {}
753
+ };
754
+ }, whole = (n, fallback, what) => {
755
+ let v = n ?? fallback;
756
+ if (!(Number.isInteger(v) && v > 0))
757
+ throw Error(`${what} must be a whole number above 0.`);
758
+ return v;
759
+ };
760
+ function watch2(options = {}) {
761
+ return Object.freeze({ kind: "watch", ...readoutMeta(options) });
762
+ }
763
+ function sparkline2(options = {}) {
764
+ return Object.freeze({
765
+ kind: "sparkline",
766
+ ...readoutMeta(options),
767
+ history: whole(options.history, 60, "A sparkline's history"),
768
+ ...options.min !== void 0 ? { min: options.min } : {},
769
+ ...options.max !== void 0 ? { max: options.max } : {},
770
+ ...options.color ? { color: options.color } : {}
771
+ });
772
+ }
773
+ function graph2(options = {}) {
774
+ let series = (options.series ?? [{ id: "value" }]).map((s) => Object.freeze(typeof s === "string" ? { id: s } : { ...s }));
775
+ if (!series.length || new Set(series.map((s) => s.id)).size !== series.length)
776
+ throw Error("A graph needs series with distinct ids.");
777
+ return Object.freeze({
778
+ kind: "graph",
779
+ ...readoutMeta(options),
780
+ series: Object.freeze(series),
781
+ history: whole(options.history, 120, "A graph's history"),
782
+ height: whole(options.height, 64, "A graph's height"),
783
+ ...options.min !== void 0 ? { min: options.min } : {},
784
+ ...options.max !== void 0 ? { max: options.max } : {},
785
+ thresholds: Object.freeze((options.thresholds ?? []).map((t) => Object.freeze(typeof t === "number" ? { value: t } : t)))
786
+ });
787
+ }
788
+ function meter2(options = {}) {
789
+ let min = options.min ?? 0, max = options.max ?? 1;
790
+ if (!(Number.isFinite(min) && Number.isFinite(max) && max > min))
791
+ throw Error("A meter needs finite bounds, max above min.");
792
+ return Object.freeze({
793
+ kind: "meter",
794
+ ...readoutMeta(options),
795
+ min,
796
+ max,
797
+ ...options.segments !== void 0 ? { segments: whole(options.segments, 1, "A meter's segments") } : {},
798
+ ...options.warn !== void 0 ? { warn: options.warn } : {},
799
+ ...options.critical !== void 0 ? { critical: options.critical } : {}
800
+ });
801
+ }
802
+ function table2(options = {}) {
803
+ if (options.columns && (options.columns.length < 1 || options.columns.length > 6))
804
+ throw Error("A table takes 1–6 columns.");
805
+ return Object.freeze({
806
+ kind: "table",
807
+ ...readoutMeta(options),
808
+ ...options.columns ? { columns: Object.freeze([...options.columns]) } : {}
809
+ });
810
+ }
811
+ function log2(options = {}) {
812
+ return Object.freeze({
813
+ kind: "log",
814
+ ...readoutMeta(options),
815
+ lines: whole(options.lines, 8, "A log's lines"),
816
+ max: whole(options.max, 1000, "A log's max")
817
+ });
818
+ }
819
+ var keep = (v, min, max, step) => {
820
+ let clamped = Math.min(max, Math.max(min, v));
821
+ return Number.isInteger(step) && Number.isInteger(min) ? Math.round(clamped) : Number(clamped.toPrecision(14));
822
+ }, wrapAngle2 = (v, min, max) => {
823
+ let span = max - min;
824
+ return span > 0 ? ((v - min) % span + span) % span + min : min;
825
+ }, snap = (v, min, max, step) => {
826
+ let bounded = Math.max(min, Math.min(max, v)), steps = Math.min(Math.floor((max - min) / step + 0.000000001), Math.round((bounded - min) / step));
827
+ return Number((min + steps * step).toPrecision(14));
828
+ }, num = (v, fallback) => typeof v === "number" && Number.isFinite(v) ? v : fallback;
829
+ function normalizeField(field, input) {
830
+ switch (field.kind) {
831
+ case "knob":
832
+ if (typeof input !== "number" || !Number.isFinite(input))
833
+ return field.value;
834
+ {
835
+ let clamped = Math.min(field.max, Math.max(field.min, input));
836
+ return Number.isInteger(field.step) && Number.isInteger(field.min) ? Math.round(clamped) : Number(clamped.toPrecision(14));
837
+ }
838
+ case "choice":
839
+ return typeof input === "string" && field.options.includes(input) ? input : field.value;
840
+ case "toggle":
841
+ return typeof input === "boolean" ? input : field.value;
842
+ case "spring": {
843
+ let v = isRecord(input) ? input : {}, { duration: d, bounce: b } = SPRING2;
844
+ return Object.freeze({
845
+ duration: snap(num(v.duration, field.value.duration), d.min, d.max, d.step),
846
+ bounce: snap(num(v.bounce, field.value.bounce), b.min, b.max, b.step)
847
+ });
848
+ }
849
+ case "color": {
850
+ let parsed = parseColor2(input);
851
+ return parsed ? formatColor(fitColor(parsed)) : field.value;
852
+ }
853
+ case "point": {
854
+ let v = isRecord(input) ? input : {};
855
+ return Object.freeze({
856
+ x: snap(num(v.x, field.value.x), field.min.x, field.max.x, field.step.x),
857
+ y: snap(num(v.y, field.value.y), field.min.y, field.max.y, field.step.y)
858
+ });
859
+ }
860
+ case "easing": {
861
+ let v = Array.isArray(input) && input.length === 4 ? input : field.value;
862
+ return Object.freeze([0, 1, 2, 3].map((i) => i % 2 === 0 ? snap(num(v[i], field.value[i]), 0, 1, 0.01) : snap(num(v[i], field.value[i]), EASING_Y[0], EASING_Y[1], 0.01)));
863
+ }
864
+ case "vec": {
865
+ let v = isRecord(input) ? input : {}, base = field.value, axis = (k) => keep(num(v[k], base[k] ?? 0), field.min, field.max, field.step);
866
+ return Object.freeze(field.axes === 3 ? { x: axis("x"), y: axis("y"), z: axis("z") } : { x: axis("x"), y: axis("y") });
867
+ }
868
+ case "range": {
869
+ let v = Array.isArray(input) && input.length === 2 ? input : field.value, a = keep(num(v[0], field.value[0]), field.min, field.max, field.step), b = keep(num(v[1], field.value[1]), field.min, field.max, field.step);
870
+ return Object.freeze([Math.min(a, b), Math.max(a, b)]);
871
+ }
872
+ case "angle": {
873
+ if (typeof input !== "number" || !Number.isFinite(input))
874
+ return field.value;
875
+ let v = field.wrap ? wrapAngle2(input, field.min, field.max) : input, kept = keep(v, field.min, field.max, field.step);
876
+ return field.wrap && kept >= field.max ? field.min : kept;
877
+ }
878
+ case "seed":
879
+ return typeof input === "number" && Number.isFinite(input) ? Math.round(Math.min(field.max, Math.max(field.min, input))) : field.value;
880
+ case "keybind":
881
+ return typeof input === "string" ? normalizeCombo2(input) : field.value;
882
+ case "gradient":
883
+ return normalizeStops(input) ?? field.value;
884
+ case "curve":
885
+ return normalizePoints(input, field.range) ?? field.value;
886
+ }
887
+ }
888
+ function normalize(schema, input) {
889
+ let record = isRecord(input) ? input : {};
890
+ return Object.freeze(Object.fromEntries(Object.entries(schema).flatMap(([key, field]) => hasValue2(field) ? [[key, normalizeField(field, record[key])]] : [])));
891
+ }
892
+ function isRecord(value) {
893
+ return typeof value === "object" && value !== null && !Array.isArray(value);
894
+ }
895
+ function same(a, b) {
896
+ if (a === b)
897
+ return !0;
898
+ if (typeof a !== "object" || typeof b !== "object" || a === null || b === null)
899
+ return !1;
900
+ if (Array.isArray(a) !== Array.isArray(b))
901
+ return !1;
902
+ let ka = Object.keys(a);
903
+ return ka.length === Object.keys(b).length && ka.every((k) => same(a[k], b[k]));
904
+ }
905
+ function equal(a, b) {
906
+ return Object.keys(a).length === Object.keys(b).length && Object.keys(a).every((key) => same(a[key], b[key]));
907
+ }
908
+
909
+ // src/core/spring.ts
910
+ class Spring {
911
+ x;
912
+ omega;
913
+ zeta;
914
+ eps;
915
+ v = 0;
916
+ target;
917
+ next;
918
+ constructor(x, omega = 24, zeta = 0.86, eps = 0.005) {
919
+ this.x = x;
920
+ this.omega = omega;
921
+ this.zeta = zeta;
922
+ this.eps = eps;
923
+ this.target = x;
924
+ }
925
+ set(target, delay = 0) {
926
+ if (delay > 0) {
927
+ if (target !== (this.next?.target ?? this.target))
928
+ this.next = { target, at: performance.now() + delay };
929
+ return this;
930
+ }
931
+ return this.next = void 0, this.target = target, this;
932
+ }
933
+ snap(x) {
934
+ return this.next = void 0, this.x = this.target = x, this.v = 0, this;
935
+ }
936
+ step(dt) {
937
+ if (this.next && performance.now() >= this.next.at)
938
+ this.target = this.next.target, this.next = void 0;
939
+ if (this.x === this.target && this.v === 0)
940
+ return this.x;
941
+ let clampedDt = Math.min(dt, 0.1), h = 0.0020833333333333333;
942
+ for (let t = 0;t < clampedDt; t += h) {
943
+ let k = Math.min(h, clampedDt - t), a = -this.omega * this.omega * (this.x - this.target) - 2 * this.zeta * this.omega * this.v;
944
+ this.v += a * k, this.x += this.v * k;
945
+ }
946
+ if (this.resting)
947
+ this.x = this.target, this.v = 0;
948
+ return this.x;
949
+ }
950
+ get resting() {
951
+ return !this.next && Math.abs(this.x - this.target) < this.eps && Math.abs(this.v) < this.eps * 4;
952
+ }
953
+ }
954
+ function springMotion2({ duration, bounce }) {
955
+ return {
956
+ omega: 2 * Math.PI / Math.max(0.01, duration),
957
+ zeta: bounce >= 0 ? 1 - bounce : 1 / (1 + bounce)
958
+ };
959
+ }
960
+ function springResponse(omega, zeta, seconds, dt = 0.016666666666666666) {
961
+ let s = new Spring(0, omega, zeta).set(1), out = [0];
962
+ for (let t = dt;t < seconds + dt / 2; t += dt)
963
+ out.push(s.step(dt));
964
+ return out;
965
+ }
966
+ function springEasing(omega, zeta) {
967
+ let s = new Spring(0, omega, zeta).set(1), points = ["0"], dt = 0.016666666666666666, t = 0;
968
+ while (t < 4 && !(Math.abs(s.x - 1) < 0.002 && Math.abs(s.v) < 0.05))
969
+ s.step(0.016666666666666666), t += 0.016666666666666666, points.push(String(Math.round(s.x * 1000) / 1000));
970
+ return points[points.length - 1] = "1", { easing: `linear(${points.join(",")})`, ms: Math.round(t * 1000) };
971
+ }
972
+ function springCss2(value) {
973
+ let { omega, zeta } = springMotion2(value), { easing, ms } = springEasing(omega, zeta);
974
+ return { easing, duration: ms };
975
+ }
976
+
977
+ // src/core/internal.ts
978
+ var RESHAPE = Symbol.for("knobs.reshape"), RESET_KEYS = Symbol.for("knobs.resetKeys"), REFRESHES = Symbol.for("knobs.refreshes");
979
+
980
+ // src/core/readout.ts
981
+ class Ring {
982
+ capacity;
983
+ data;
984
+ start = 0;
985
+ length = 0;
986
+ constructor(capacity) {
987
+ this.capacity = capacity;
988
+ this.data = new Float64Array(Math.max(1, capacity));
989
+ }
990
+ push(v) {
991
+ let cap = this.data.length;
992
+ if (this.length < cap)
993
+ this.data[(this.start + this.length++) % cap] = v;
994
+ else
995
+ this.data[this.start] = v, this.start = (this.start + 1) % cap;
996
+ }
997
+ at(i) {
998
+ return this.data[(this.start + i) % this.data.length];
999
+ }
1000
+ last() {
1001
+ return this.length ? this.at(this.length - 1) : Number.NaN;
1002
+ }
1003
+ clear() {
1004
+ this.start = this.length = 0;
1005
+ }
1006
+ extent() {
1007
+ let lo = 1 / 0, hi = -1 / 0;
1008
+ for (let i = 0;i < this.length; i++) {
1009
+ let v = this.at(i);
1010
+ if (!Number.isFinite(v))
1011
+ continue;
1012
+ if (v < lo)
1013
+ lo = v;
1014
+ if (v > hi)
1015
+ hi = v;
1016
+ }
1017
+ return lo <= hi ? [lo, hi] : [Number.NaN, Number.NaN];
1018
+ }
1019
+ toArray() {
1020
+ return Array.from({ length: this.length }, (_, i) => this.at(i));
1021
+ }
1022
+ }
1023
+
1024
+ class EntryLog {
1025
+ max;
1026
+ entries = [];
1027
+ total = 0;
1028
+ constructor(max) {
1029
+ this.max = max;
1030
+ }
1031
+ push(entry) {
1032
+ let time = entry.time ?? Date.now();
1033
+ if (this.entries.push({ ...entry, level: entry.level ?? "info", time, seq: this.total++ }), this.entries.length > this.max)
1034
+ this.entries.splice(0, this.entries.length - this.max);
1035
+ }
1036
+ clear() {
1037
+ this.entries = [];
1038
+ }
1039
+ }
1040
+ function readoutState(field) {
1041
+ if (field.kind === "sparkline")
1042
+ return { seq: 0, value: null, series: /* @__PURE__ */ new Map([["", new Ring(field.history ?? 60)]]) };
1043
+ if (field.kind === "graph")
1044
+ return {
1045
+ seq: 0,
1046
+ value: null,
1047
+ series: new Map((field.series ?? []).map((s) => [s.id, new Ring(field.history ?? 120)]))
1048
+ };
1049
+ if (field.kind === "log")
1050
+ return { seq: 0, value: null, log: new EntryLog(field.max ?? 1000) };
1051
+ return { seq: 0, value: null };
1052
+ }
1053
+ var levels = /* @__PURE__ */ new Set(["debug", "info", "warn", "error"]), asEntry = (v) => typeof v === "string" ? { text: v } : v && typeof v === "object" && typeof v.text === "string" ? {
1054
+ text: v.text,
1055
+ ...levels.has(v.level) ? { level: v.level } : {},
1056
+ ...typeof v.time === "number" ? { time: v.time } : {}
1057
+ } : void 0;
1058
+ function pushReadout(field, state, value) {
1059
+ if (state.seq++, value === null || value === void 0) {
1060
+ state.value = null;
1061
+ for (let ring of state.series?.values() ?? [])
1062
+ ring.clear();
1063
+ state.log?.clear();
1064
+ return;
1065
+ }
1066
+ if (field.kind === "sparkline") {
1067
+ let ring = state.series.get("");
1068
+ if (Array.isArray(value)) {
1069
+ ring.clear();
1070
+ for (let v of value)
1071
+ ring.push(Number(v));
1072
+ } else
1073
+ ring.push(Number(value));
1074
+ state.value = ring.last();
1075
+ return;
1076
+ }
1077
+ if (field.kind === "graph") {
1078
+ let record = typeof value === "number" ? { [field.series?.[0]?.id ?? ""]: value } : value;
1079
+ for (let [id, ring] of state.series) {
1080
+ let v = record[id];
1081
+ ring.push(typeof v === "number" ? v : Number.NaN);
1082
+ }
1083
+ state.value = record;
1084
+ return;
1085
+ }
1086
+ if (field.kind === "log") {
1087
+ let list = Array.isArray(value) ? value : [value];
1088
+ for (let item of list) {
1089
+ let entry = asEntry(item);
1090
+ if (entry)
1091
+ state.log.push(entry);
1092
+ }
1093
+ state.value = state.log.entries.at(-1) ?? null;
1094
+ return;
1095
+ }
1096
+ state.value = value;
1097
+ }
1098
+ var READOUT = Symbol.for("knobs.readout");
1099
+
1100
+ // src/core/storage.ts
1101
+ function memoryStorage2() {
1102
+ let data = /* @__PURE__ */ new Map;
1103
+ return {
1104
+ getItem: (key) => data.get(key) ?? null,
1105
+ setItem: (key, value) => {
1106
+ data.set(key, value);
1107
+ }
1108
+ };
1109
+ }
1110
+ function browserStorage() {
1111
+ return typeof window > "u" ? null : window.localStorage;
1112
+ }
1113
+ function urlFlag2(name) {
1114
+ return () => typeof location < "u" && new URLSearchParams(location.search).has(name);
1115
+ }
1116
+ var storageKey = (id, part) => `knobs:${encodeURIComponent(id)}:${part}:v1`;
1117
+
1118
+ export { __preload, __chunks, maxChroma, oklchToRgb, parseColor2, fitColor, formatColor, colorHex2, EASINGS2, EASING_Y, easingName, bezierPoint2, bezierCss2, normalizeCombo2, letterOf2, comboFromEvent2, heldMods, matchesCombo2, comboParts2, comboLabel2, GRADIENT_SAMPLES, sampleGradient2, gradientSamples, gradientCss2, stopColorAt, sampleCurve2, curveSamples, CURVE_SHAPES2, inRange, sameRamp, randomStops, mutateStops, randomPoints, mutatePoints, pickMeta, isReadout2, hasValue2, SPRING2, valueKeys, knob2, choice2, action2, flatSpring2, toggle2, spring2, color2, point2, easing2, vec22, vec32, range2, angle2, seed2, keybind2, gradient2, curve2, toolbarShortcut2, toolbar2, watch2, sparkline2, graph2, meter2, table2, log2, wrapAngle2, normalizeField, normalize, isRecord, same, equal, Spring, springMotion2, springResponse, springEasing, springCss2, RESHAPE, RESET_KEYS, REFRESHES, readoutState, pushReadout, READOUT, memoryStorage2, browserStorage, urlFlag2, storageKey };