@kitlangton/knobs 0.2.0 → 0.4.0-next.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.
- package/README.md +197 -19
- package/dist/dev/{chunk-gezhc24g.js → chunk-7xgkerd2.js} +14 -5
- package/dist/dev/{chunk-693jd1gj.js → chunk-pmkvb9ep.js} +1544 -160
- package/dist/dev/{chunk-5cr85vv6.js → chunk-stbcgz1y.js} +7924 -7931
- package/dist/dev/{chunk-4km87zfx.js → chunk-t68smqkc.js} +18 -7
- package/dist/dev/index.js +187 -31
- package/dist/index.js +197 -33
- package/dist/panel.js +20342 -18461
- package/dist/types/core/keys.d.ts +2 -0
- package/dist/types/core/remote.d.ts +2 -0
- package/dist/types/core/schema.d.ts +5 -0
- package/dist/types/core/store.d.ts +29 -2
- package/dist/types/core/unlock.d.ts +77 -0
- package/dist/types/index.d.ts +1 -0
- package/dist/types/panel/attach.d.ts +2 -0
- package/dist/types/panel/crumbs.d.ts +19 -0
- package/dist/types/panel/editors.d.ts +9 -0
- package/dist/types/panel/feel.d.ts +103 -0
- package/dist/types/panel/give.d.ts +34 -1
- package/dist/types/panel/gpu/color.d.ts +4 -4
- package/dist/types/panel/gpu/renderer.d.ts +32 -0
- package/dist/types/panel/gpu/shader.wgsl.d.ts +1 -1
- package/dist/types/panel/index.d.ts +2 -1
- package/dist/types/panel/inspector.d.ts +1 -1
- package/dist/types/panel/lens.d.ts +113 -0
- package/dist/types/panel/motion.d.ts +2 -0
- package/dist/types/panel/page-hints.d.ts +2 -1
- package/dist/types/panel/prefs.d.ts +2 -2
- package/dist/types/panel/presence.d.ts +20 -0
- package/dist/types/panel/scope.d.ts +29 -0
- package/dist/types/panel/slider.d.ts +21 -4
- package/dist/types/panel/tab-transition.d.ts +15 -0
- package/dist/types/panel/tabs.d.ts +1 -1
- package/dist/types/panel/toggle.d.ts +27 -0
- package/dist/types/ui/compare-css.d.ts +1 -0
- package/dist/types/ui/compare-frames.d.ts +41 -0
- package/dist/types/ui/compare-gpu.d.ts +58 -0
- package/dist/types/ui/compare-model.d.ts +169 -0
- package/dist/types/ui/compare-mp4.d.ts +29 -0
- package/dist/types/ui/compare-strip.d.ts +28 -0
- package/dist/types/ui/compare.d.ts +103 -0
- package/dist/types/ui/diff-css.d.ts +1 -0
- package/dist/types/ui/diff-model.d.ts +128 -0
- package/dist/types/ui/diff-scroll.d.ts +29 -0
- package/dist/types/ui/diff.d.ts +158 -0
- package/dist/types/ui/index.d.ts +9 -1
- package/dist/types/ui/look.d.ts +1 -1
- package/dist/types/ui/react.d.ts +66 -0
- package/dist/types/ui/scrollbar.d.ts +12 -0
- package/dist/types/ui/solid.d.ts +52 -0
- package/dist/types/ui/split-model.d.ts +4 -3
- package/dist/types/ui/split.d.ts +8 -6
- package/dist/types/ui/timeline.d.ts +35 -2
- package/dist/types/ui/tree.d.ts +7 -2
- package/dist/types/ui/walkthrough-css.d.ts +1 -0
- package/dist/types/ui/walkthrough-media.d.ts +29 -0
- package/dist/types/ui/walkthrough-model.d.ts +447 -0
- package/dist/types/ui/walkthrough.d.ts +63 -0
- package/dist/ui-react.js +16486 -8140
- package/dist/ui-solid.js +16030 -7775
- package/dist/ui.js +17510 -9506
- package/package.json +6 -1
|
@@ -1,10 +1,11 @@
|
|
|
1
1
|
import {
|
|
2
|
+
isTypingTarget,
|
|
2
3
|
GRADIENT_SAMPLES,
|
|
3
4
|
Spring,
|
|
4
5
|
springEasing,
|
|
5
6
|
springCss2,
|
|
6
7
|
storageKey
|
|
7
|
-
} from "./chunk-
|
|
8
|
+
} from "./chunk-7xgkerd2.js";
|
|
8
9
|
|
|
9
10
|
// src/panel/icons.ts
|
|
10
11
|
var ICONS = {
|
|
@@ -62,147 +63,6 @@ function writeSession(id, patch) {
|
|
|
62
63
|
} catch {}
|
|
63
64
|
}
|
|
64
65
|
|
|
65
|
-
// src/panel/motion.ts
|
|
66
|
-
var FILL = { omega: 30, zeta: 0.88 };
|
|
67
|
-
var LEAD = { omega: 34, zeta: 0.86 }, TRAIL = { omega: 25, zeta: 1 }, trailScale = (blur) => 0.6 + 0.4 * clamp(blur, 0, 2);
|
|
68
|
-
|
|
69
|
-
class Edges {
|
|
70
|
-
start;
|
|
71
|
-
end;
|
|
72
|
-
constructor(start = 0, end = 0) {
|
|
73
|
-
this.start = new Spring(start, LEAD.omega, LEAD.zeta, 0.05), this.end = new Spring(end, LEAD.omega, LEAD.zeta, 0.05);
|
|
74
|
-
}
|
|
75
|
-
to(start, end, delay = 0, trail = 1) {
|
|
76
|
-
if ((start !== this.start.target || end !== this.end.target) && delay <= 0) {
|
|
77
|
-
let forward = start + end > this.start.target + this.end.target, [lead, tail] = forward ? [this.end, this.start] : [this.start, this.end];
|
|
78
|
-
lead.omega = LEAD.omega, lead.zeta = LEAD.zeta, tail.omega = TRAIL.omega / trail, tail.zeta = TRAIL.zeta;
|
|
79
|
-
}
|
|
80
|
-
return this.start.set(start, delay), this.end.set(end, delay), this;
|
|
81
|
-
}
|
|
82
|
-
shift(ds, de) {
|
|
83
|
-
return this.start.x += ds, this.start.target += ds, this.end.x += de, this.end.target += de, this;
|
|
84
|
-
}
|
|
85
|
-
snap(start, end) {
|
|
86
|
-
return this.start.snap(start), this.end.snap(end), this;
|
|
87
|
-
}
|
|
88
|
-
step(dt) {
|
|
89
|
-
return this.start.step(dt), this.end.step(dt), this;
|
|
90
|
-
}
|
|
91
|
-
get resting() {
|
|
92
|
-
return this.start.resting && this.end.resting;
|
|
93
|
-
}
|
|
94
|
-
get velocity() {
|
|
95
|
-
return (this.start.v + this.end.v) / 2;
|
|
96
|
-
}
|
|
97
|
-
}
|
|
98
|
-
var POP_MS = 360;
|
|
99
|
-
function popScale(t, peak = 0.04) {
|
|
100
|
-
if (t <= 0 || t >= POP_MS)
|
|
101
|
-
return 1;
|
|
102
|
-
let u = t / 55;
|
|
103
|
-
return 1 + peak * u * Math.exp(1 - u);
|
|
104
|
-
}
|
|
105
|
-
|
|
106
|
-
class VelocityTracker {
|
|
107
|
-
samples = [];
|
|
108
|
-
add(x, y, t = performance.now()) {
|
|
109
|
-
this.samples.push({ t, x, y });
|
|
110
|
-
while (this.samples.length > 2 && t - this.samples[0].t > 100)
|
|
111
|
-
this.samples.shift();
|
|
112
|
-
}
|
|
113
|
-
reset() {
|
|
114
|
-
this.samples = [];
|
|
115
|
-
}
|
|
116
|
-
get velocity() {
|
|
117
|
-
let s = this.samples;
|
|
118
|
-
if (s.length < 2)
|
|
119
|
-
return [0, 0];
|
|
120
|
-
let a = s[0], b = s[s.length - 1], dt = (b.t - a.t) / 1000;
|
|
121
|
-
if (dt <= 0 || performance.now() - b.t > 60)
|
|
122
|
-
return [0, 0];
|
|
123
|
-
return [(b.x - a.x) / dt, (b.y - a.y) / dt];
|
|
124
|
-
}
|
|
125
|
-
}
|
|
126
|
-
var clamp = (x, a, b) => Math.min(b, Math.max(a, x));
|
|
127
|
-
|
|
128
|
-
class Roll {
|
|
129
|
-
from;
|
|
130
|
-
to = 0;
|
|
131
|
-
inherited = 0;
|
|
132
|
-
start = 0;
|
|
133
|
-
duration = 0;
|
|
134
|
-
constructor(position) {
|
|
135
|
-
this.from = this.to = position;
|
|
136
|
-
}
|
|
137
|
-
get target() {
|
|
138
|
-
return this.to;
|
|
139
|
-
}
|
|
140
|
-
sample(now) {
|
|
141
|
-
let t = now - this.start;
|
|
142
|
-
if (this.duration <= 0 || t >= this.duration)
|
|
143
|
-
return { position: this.to, velocity: 0 };
|
|
144
|
-
if (t <= 0)
|
|
145
|
-
return { position: this.from, velocity: 0 };
|
|
146
|
-
let seconds = this.duration / 1000, tau = t / this.duration, d = this.from - this.to, e = Math.exp(-10 * tau);
|
|
147
|
-
return {
|
|
148
|
-
position: this.to + (d + (this.inherited + 10 * d) * tau) * e,
|
|
149
|
-
velocity: e * (this.inherited - 10 * (this.inherited + 10 * d) * tau) / seconds
|
|
150
|
-
};
|
|
151
|
-
}
|
|
152
|
-
set(target, duration, now, delay = 0) {
|
|
153
|
-
let { position, velocity } = this.sample(now);
|
|
154
|
-
if (duration <= 0)
|
|
155
|
-
return this.snap(target);
|
|
156
|
-
let seconds = duration / 1000, distance = position - target, limit = Math.max(Math.abs(distance), 1) * 12 / seconds;
|
|
157
|
-
return this.inherited = clamp(delay > 0 ? 0 : velocity, -limit, limit) * seconds, this.from = position, this.to = target, this.start = now + delay, this.duration = duration, this;
|
|
158
|
-
}
|
|
159
|
-
snap(position) {
|
|
160
|
-
return this.from = this.to = position, this.duration = 0, this.inherited = 0, this;
|
|
161
|
-
}
|
|
162
|
-
resting(now) {
|
|
163
|
-
return this.duration <= 0 || now - this.start >= this.duration;
|
|
164
|
-
}
|
|
165
|
-
span(now) {
|
|
166
|
-
if (this.resting(now))
|
|
167
|
-
return [this.to, this.to];
|
|
168
|
-
return [Math.min(this.from, this.to), Math.max(this.from, this.to)];
|
|
169
|
-
}
|
|
170
|
-
}
|
|
171
|
-
function rollTarget(position, digit, trend) {
|
|
172
|
-
let target = Math.floor(position / 10) * 10 + digit;
|
|
173
|
-
if (trend > 0 && target < position - 0.001)
|
|
174
|
-
target += 10;
|
|
175
|
-
else if (trend < 0 && target > position + 0.001)
|
|
176
|
-
target -= 10;
|
|
177
|
-
else if (trend === 0)
|
|
178
|
-
target += Math.round((position - target) / 10) * 10;
|
|
179
|
-
return target;
|
|
180
|
-
}
|
|
181
|
-
var numeral = (position) => String((position % 10 + 10) % 10);
|
|
182
|
-
function entranceOffset(elapsed, duration, hold = 0.14) {
|
|
183
|
-
if (duration <= 0 || elapsed >= duration)
|
|
184
|
-
return 0;
|
|
185
|
-
let p = clamp((elapsed / duration - hold) / (1 - hold), 0, 1);
|
|
186
|
-
return (1 + 10 * p) * Math.exp(-10 * p);
|
|
187
|
-
}
|
|
188
|
-
|
|
189
|
-
// src/panel/flash.ts
|
|
190
|
-
var FLASH_MS = 320, flashes = /* @__PURE__ */ new WeakMap;
|
|
191
|
-
function flash(button, wake) {
|
|
192
|
-
if (!button || button.hidden)
|
|
193
|
-
return;
|
|
194
|
-
flashes.set(button, button.ownerDocument.defaultView?.performance.now() ?? performance.now()), button.dataset.kFlash = button.dataset.kFlash === "a" ? "b" : "a", wake?.();
|
|
195
|
-
}
|
|
196
|
-
function flashOf(button, now) {
|
|
197
|
-
let at = flashes.get(button);
|
|
198
|
-
if (at === void 0)
|
|
199
|
-
return 0;
|
|
200
|
-
let p = (now - at) / 320;
|
|
201
|
-
if (p >= 1 || p < 0)
|
|
202
|
-
return 0;
|
|
203
|
-
return (1 - p) ** 3;
|
|
204
|
-
}
|
|
205
|
-
|
|
206
66
|
// src/panel/rolling-css.ts
|
|
207
67
|
var rolling_css_default = `.rn-root {
|
|
208
68
|
display: inline-block;
|
|
@@ -393,8 +253,428 @@ var problemsPreset = {
|
|
|
393
253
|
}
|
|
394
254
|
};
|
|
395
255
|
|
|
256
|
+
// src/panel/motion.ts
|
|
257
|
+
var FILL = { omega: 30, zeta: 0.88 };
|
|
258
|
+
var LEAD = { omega: 34, zeta: 0.86 }, TRAIL = { omega: 25, zeta: 1 }, trailScale = (blur) => 0.6 + 0.4 * clamp(blur, 0, 2);
|
|
259
|
+
|
|
260
|
+
class Edges {
|
|
261
|
+
start;
|
|
262
|
+
end;
|
|
263
|
+
constructor(start = 0, end = 0) {
|
|
264
|
+
this.start = new Spring(start, LEAD.omega, LEAD.zeta, 0.05), this.end = new Spring(end, LEAD.omega, LEAD.zeta, 0.05);
|
|
265
|
+
}
|
|
266
|
+
to(start, end, delay = 0, trail = 1) {
|
|
267
|
+
if ((start !== this.start.target || end !== this.end.target) && delay <= 0) {
|
|
268
|
+
let forward = start + end > this.start.target + this.end.target, [lead, tail] = forward ? [this.end, this.start] : [this.start, this.end];
|
|
269
|
+
lead.omega = LEAD.omega, lead.zeta = LEAD.zeta, tail.omega = TRAIL.omega / trail, tail.zeta = TRAIL.zeta;
|
|
270
|
+
}
|
|
271
|
+
return this.start.set(start, delay), this.end.set(end, delay), this;
|
|
272
|
+
}
|
|
273
|
+
shift(ds, de) {
|
|
274
|
+
return this.start.x += ds, this.start.target += ds, this.end.x += de, this.end.target += de, this;
|
|
275
|
+
}
|
|
276
|
+
snap(start, end) {
|
|
277
|
+
return this.start.snap(start), this.end.snap(end), this;
|
|
278
|
+
}
|
|
279
|
+
step(dt) {
|
|
280
|
+
return this.start.step(dt), this.end.step(dt), this;
|
|
281
|
+
}
|
|
282
|
+
get resting() {
|
|
283
|
+
return this.start.resting && this.end.resting;
|
|
284
|
+
}
|
|
285
|
+
get velocity() {
|
|
286
|
+
return (this.start.v + this.end.v) / 2;
|
|
287
|
+
}
|
|
288
|
+
}
|
|
289
|
+
var POP_MS = 360;
|
|
290
|
+
function popScale(t, peak = 0.04) {
|
|
291
|
+
if (t <= 0 || t >= POP_MS)
|
|
292
|
+
return 1;
|
|
293
|
+
let u = t / 55;
|
|
294
|
+
return 1 + peak * u * Math.exp(1 - u);
|
|
295
|
+
}
|
|
296
|
+
var settleScale = (t) => popScale(t, 0.14);
|
|
297
|
+
|
|
298
|
+
class VelocityTracker {
|
|
299
|
+
samples = [];
|
|
300
|
+
add(x, y, t = performance.now()) {
|
|
301
|
+
this.samples.push({ t, x, y });
|
|
302
|
+
while (this.samples.length > 2 && t - this.samples[0].t > 100)
|
|
303
|
+
this.samples.shift();
|
|
304
|
+
}
|
|
305
|
+
reset() {
|
|
306
|
+
this.samples = [];
|
|
307
|
+
}
|
|
308
|
+
get velocity() {
|
|
309
|
+
let s = this.samples;
|
|
310
|
+
if (s.length < 2)
|
|
311
|
+
return [0, 0];
|
|
312
|
+
let a = s[0], b = s[s.length - 1], dt = (b.t - a.t) / 1000;
|
|
313
|
+
if (dt <= 0 || performance.now() - b.t > 60)
|
|
314
|
+
return [0, 0];
|
|
315
|
+
return [(b.x - a.x) / dt, (b.y - a.y) / dt];
|
|
316
|
+
}
|
|
317
|
+
}
|
|
318
|
+
var clamp = (x, a, b) => Math.min(b, Math.max(a, x));
|
|
319
|
+
|
|
320
|
+
class Roll {
|
|
321
|
+
from;
|
|
322
|
+
to = 0;
|
|
323
|
+
inherited = 0;
|
|
324
|
+
start = 0;
|
|
325
|
+
duration = 0;
|
|
326
|
+
constructor(position) {
|
|
327
|
+
this.from = this.to = position;
|
|
328
|
+
}
|
|
329
|
+
get target() {
|
|
330
|
+
return this.to;
|
|
331
|
+
}
|
|
332
|
+
sample(now) {
|
|
333
|
+
let t = now - this.start;
|
|
334
|
+
if (this.duration <= 0 || t >= this.duration)
|
|
335
|
+
return { position: this.to, velocity: 0 };
|
|
336
|
+
if (t <= 0)
|
|
337
|
+
return { position: this.from, velocity: 0 };
|
|
338
|
+
let seconds = this.duration / 1000, tau = t / this.duration, d = this.from - this.to, e = Math.exp(-10 * tau);
|
|
339
|
+
return {
|
|
340
|
+
position: this.to + (d + (this.inherited + 10 * d) * tau) * e,
|
|
341
|
+
velocity: e * (this.inherited - 10 * (this.inherited + 10 * d) * tau) / seconds
|
|
342
|
+
};
|
|
343
|
+
}
|
|
344
|
+
set(target, duration, now, delay = 0) {
|
|
345
|
+
let { position, velocity } = this.sample(now);
|
|
346
|
+
if (duration <= 0)
|
|
347
|
+
return this.snap(target);
|
|
348
|
+
let seconds = duration / 1000, distance = position - target, limit = Math.max(Math.abs(distance), 1) * 12 / seconds;
|
|
349
|
+
return this.inherited = clamp(delay > 0 ? 0 : velocity, -limit, limit) * seconds, this.from = position, this.to = target, this.start = now + delay, this.duration = duration, this;
|
|
350
|
+
}
|
|
351
|
+
snap(position) {
|
|
352
|
+
return this.from = this.to = position, this.duration = 0, this.inherited = 0, this;
|
|
353
|
+
}
|
|
354
|
+
resting(now) {
|
|
355
|
+
return this.duration <= 0 || now - this.start >= this.duration;
|
|
356
|
+
}
|
|
357
|
+
span(now) {
|
|
358
|
+
if (this.resting(now))
|
|
359
|
+
return [this.to, this.to];
|
|
360
|
+
return [Math.min(this.from, this.to), Math.max(this.from, this.to)];
|
|
361
|
+
}
|
|
362
|
+
}
|
|
363
|
+
function rollTarget(position, digit, trend) {
|
|
364
|
+
let target = Math.floor(position / 10) * 10 + digit;
|
|
365
|
+
if (trend > 0 && target < position - 0.001)
|
|
366
|
+
target += 10;
|
|
367
|
+
else if (trend < 0 && target > position + 0.001)
|
|
368
|
+
target -= 10;
|
|
369
|
+
else if (trend === 0)
|
|
370
|
+
target += Math.round((position - target) / 10) * 10;
|
|
371
|
+
return target;
|
|
372
|
+
}
|
|
373
|
+
var numeral = (position) => String((position % 10 + 10) % 10);
|
|
374
|
+
function entranceOffset(elapsed, duration, hold = 0.14) {
|
|
375
|
+
if (duration <= 0 || elapsed >= duration)
|
|
376
|
+
return 0;
|
|
377
|
+
let p = clamp((elapsed / duration - hold) / (1 - hold), 0, 1);
|
|
378
|
+
return (1 + 10 * p) * Math.exp(-10 * p);
|
|
379
|
+
}
|
|
380
|
+
|
|
381
|
+
// src/panel/flash.ts
|
|
382
|
+
var FLASH_MS = 320, flashes = /* @__PURE__ */ new WeakMap;
|
|
383
|
+
function flash(button, wake) {
|
|
384
|
+
if (!button || button.hidden)
|
|
385
|
+
return;
|
|
386
|
+
flashes.set(button, button.ownerDocument.defaultView?.performance.now() ?? performance.now()), button.dataset.kFlash = button.dataset.kFlash === "a" ? "b" : "a", wake?.();
|
|
387
|
+
}
|
|
388
|
+
function flashOf(button, now) {
|
|
389
|
+
let at = flashes.get(button);
|
|
390
|
+
if (at === void 0)
|
|
391
|
+
return 0;
|
|
392
|
+
let p = (now - at) / 320;
|
|
393
|
+
if (p >= 1 || p < 0)
|
|
394
|
+
return 0;
|
|
395
|
+
return (1 - p) ** 3;
|
|
396
|
+
}
|
|
397
|
+
|
|
398
|
+
// src/panel/feel.ts
|
|
399
|
+
var clamp2 = (x, a, b) => Math.min(b, Math.max(a, x)), smooth = (a, b, x) => {
|
|
400
|
+
let t = clamp2((x - a) / (b - a), 0, 1);
|
|
401
|
+
return t * t * (3 - 2 * t);
|
|
402
|
+
}, PRECISION = [
|
|
403
|
+
[24, 44],
|
|
404
|
+
[76, 96],
|
|
405
|
+
[126, 146]
|
|
406
|
+
], offRow = (y, top, bottom) => Math.max(0, top - y, y - bottom), precisionDepth = (off) => PRECISION.reduce((n, [a, b]) => n + smooth(a, b, Math.abs(off)), 0), precisionRate = (off) => 2 ** -precisionDepth(off), RULER = [
|
|
407
|
+
[20, 5, 0.2],
|
|
408
|
+
[40, 4, 0.17],
|
|
409
|
+
[80, 3, 0.14]
|
|
410
|
+
], RULER_NEAR = 36, RULER_FADE = 120, rulerLevels = (steps) => {
|
|
411
|
+
let n = 0;
|
|
412
|
+
while (n < 3 && 10 * 2 ** (n + 1) <= steps)
|
|
413
|
+
n++;
|
|
414
|
+
return n;
|
|
415
|
+
}, rulerAlpha = (depth, level) => clamp2(depth - (level - 1), 0, 1), rulerDepth = (rate, levels) => rate >= 1 ? 0 : clamp2(-Math.log2(rate), 0, levels), THIN = 1.7, THINNEST = 0.85, thinOf = (scale) => Math.max(0.85, 1 - 1.7 * Math.max(0, scale - 1));
|
|
416
|
+
var roundOf = (give) => clamp2(Math.abs(give) / 4, 0, 1), stretchOf = (give, length) => ({
|
|
417
|
+
scale: length > 0 ? 1 + Math.abs(give) / length : 1,
|
|
418
|
+
anchor: give < 0 ? 1 : 0
|
|
419
|
+
}), stretchAt = (at, give, length) => {
|
|
420
|
+
let { scale, anchor } = stretchOf(give, length), o = anchor * length;
|
|
421
|
+
return o + (at - o) * scale;
|
|
422
|
+
}, giveRoom = (room, most) => Math.max(0.5, Math.min(most, room)), DETENTS = {
|
|
423
|
+
default: { hold: 4, ramp: 6 },
|
|
424
|
+
base: { hold: 4, ramp: 6 },
|
|
425
|
+
round: { hold: 2.5, ramp: 4 }
|
|
426
|
+
}, detentScale = (rate) => clamp2(rate, 0, 1) ** 1.5, reach = (d, k = 1) => {
|
|
427
|
+
let { hold, ramp } = DETENTS[d.kind];
|
|
428
|
+
return { h: hold * k, r: ramp * k };
|
|
429
|
+
};
|
|
430
|
+
function detentsOf(width, min, max, o) {
|
|
431
|
+
let span = max - min;
|
|
432
|
+
if (!(span > 0) || !(width > 0))
|
|
433
|
+
return [];
|
|
434
|
+
let list = [], add = (value, kind) => {
|
|
435
|
+
if (value === void 0 || !Number.isFinite(value) || value < min || value > max)
|
|
436
|
+
return;
|
|
437
|
+
let at = (value - min) / span * width, room = (d) => {
|
|
438
|
+
let a = reach(d), b = DETENTS[kind];
|
|
439
|
+
return a.h + a.r + b.hold + b.ramp;
|
|
440
|
+
};
|
|
441
|
+
if (list.some((d) => Math.abs(d.at - at) < room(d)))
|
|
442
|
+
return;
|
|
443
|
+
list.push({ at, value, kind });
|
|
444
|
+
};
|
|
445
|
+
add(o.value, "default"), add(o.base, "base");
|
|
446
|
+
for (let v of o.round ?? [])
|
|
447
|
+
add(v, "round");
|
|
448
|
+
return list;
|
|
449
|
+
}
|
|
450
|
+
var roundValues = (min, max) => {
|
|
451
|
+
let mid = Number(((min + max) / 2).toPrecision(12));
|
|
452
|
+
return min < 0 && max > 0 && mid !== 0 ? [0, mid] : [mid];
|
|
453
|
+
};
|
|
454
|
+
function warp(x, list, k = 1) {
|
|
455
|
+
for (let d of list) {
|
|
456
|
+
let { h, r } = reach(d, k), u = x - d.at, a = Math.abs(u);
|
|
457
|
+
if (a >= h + r)
|
|
458
|
+
continue;
|
|
459
|
+
if (a <= h)
|
|
460
|
+
return d.at;
|
|
461
|
+
return d.at + Math.sign(u) * (a - h) * (h + r) / r;
|
|
462
|
+
}
|
|
463
|
+
return x;
|
|
464
|
+
}
|
|
465
|
+
function unwarp(shown, x, list, k = 1) {
|
|
466
|
+
for (let d of list) {
|
|
467
|
+
let { h, r } = reach(d, k), v = shown - d.at, a = Math.abs(v);
|
|
468
|
+
if (a >= h + r)
|
|
469
|
+
continue;
|
|
470
|
+
if (a === 0)
|
|
471
|
+
return d.at + clamp2(x - d.at, -h, h);
|
|
472
|
+
return d.at + Math.sign(v) * (h + a * r / (h + r));
|
|
473
|
+
}
|
|
474
|
+
return shown;
|
|
475
|
+
}
|
|
476
|
+
var REARM = 2;
|
|
477
|
+
|
|
478
|
+
class Notches {
|
|
479
|
+
list;
|
|
480
|
+
held = -1;
|
|
481
|
+
armed;
|
|
482
|
+
constructor(list, x) {
|
|
483
|
+
this.list = list;
|
|
484
|
+
this.armed = list.map((d) => Math.abs(x - d.at) > REARM), this.held = list.findIndex((d) => warp(x, [d]) === d.at);
|
|
485
|
+
}
|
|
486
|
+
move(x, k = 1) {
|
|
487
|
+
let shown = warp(x, this.list, k), held = this.list.findIndex((d) => d.at === shown), caught;
|
|
488
|
+
if (this.list.forEach((d, i) => {
|
|
489
|
+
let { h, r } = reach(d, k);
|
|
490
|
+
if (Math.abs(x - d.at) > Math.max(h + r, REARM))
|
|
491
|
+
this.armed[i] = !0;
|
|
492
|
+
}), held >= 0 && held !== this.held && this.armed[held])
|
|
493
|
+
caught = this.list[held], this.armed[held] = !1;
|
|
494
|
+
return this.held = held, caught ? { shown, caught } : { shown };
|
|
495
|
+
}
|
|
496
|
+
}
|
|
497
|
+
|
|
498
|
+
// src/panel/scope.ts
|
|
499
|
+
function escapeTo(level, inCard = !0) {
|
|
500
|
+
if (level === "row")
|
|
501
|
+
return inCard ? "card" : "panel";
|
|
502
|
+
if (level === "card")
|
|
503
|
+
return "panel";
|
|
504
|
+
return "none";
|
|
505
|
+
}
|
|
506
|
+
function commandScope(level, selection = !1) {
|
|
507
|
+
if (selection)
|
|
508
|
+
return "selection";
|
|
509
|
+
if (level === "row")
|
|
510
|
+
return "row";
|
|
511
|
+
if (level === "card")
|
|
512
|
+
return "group";
|
|
513
|
+
return "all";
|
|
514
|
+
}
|
|
515
|
+
function scopeLabel(verb, scope) {
|
|
516
|
+
return `${verb} ${scope === "selection" ? "selected" : scope === "row" ? "this" : scope === "group" ? "group" : "all"}`;
|
|
517
|
+
}
|
|
518
|
+
function crumbTrail(path) {
|
|
519
|
+
let out = [];
|
|
520
|
+
for (let level of ["card", "row", "editor"]) {
|
|
521
|
+
let label = path[level]?.trim();
|
|
522
|
+
if (label)
|
|
523
|
+
out.push({ level, label });
|
|
524
|
+
}
|
|
525
|
+
return out;
|
|
526
|
+
}
|
|
527
|
+
function escTarget(trail, title) {
|
|
528
|
+
if (!trail.length)
|
|
529
|
+
return;
|
|
530
|
+
return trail.at(-2)?.label ?? (title.trim() || "panel");
|
|
531
|
+
}
|
|
532
|
+
function fitCrumbs(widths, room, sep, more, hint = 0) {
|
|
533
|
+
let n = widths.length;
|
|
534
|
+
if (!n)
|
|
535
|
+
return { from: 0, hint: !1 };
|
|
536
|
+
let span = (from) => {
|
|
537
|
+
let w = 0;
|
|
538
|
+
for (let i = from;i < n; i++)
|
|
539
|
+
w += widths[i] + (i > from ? sep : 0);
|
|
540
|
+
return from > 0 ? w + more + sep : w;
|
|
541
|
+
}, from = 0;
|
|
542
|
+
while (from < n - 1 && span(from) > room)
|
|
543
|
+
from++;
|
|
544
|
+
let used = span(from);
|
|
545
|
+
return { from, hint: hint > 0 && used + sep + hint <= room };
|
|
546
|
+
}
|
|
547
|
+
function digitOption(key, count) {
|
|
548
|
+
if (!/^[1-9]$/.test(key))
|
|
549
|
+
return;
|
|
550
|
+
let i = Number(key) - 1;
|
|
551
|
+
return i < count ? i : void 0;
|
|
552
|
+
}
|
|
553
|
+
function digitHints(count, showing) {
|
|
554
|
+
let n = Math.min(9, count);
|
|
555
|
+
if (n <= 0)
|
|
556
|
+
return [];
|
|
557
|
+
if (showing)
|
|
558
|
+
return Array.from({ length: n }, (_, i) => String(i + 1));
|
|
559
|
+
return [n === 1 ? "1" : `1–${n}`];
|
|
560
|
+
}
|
|
561
|
+
|
|
562
|
+
// src/panel/crumbs.ts
|
|
563
|
+
var glide2 = springEasing(2 * Math.PI / 0.3, 1), CRUMB_GLIDE = `${glide2.ms}ms ${glide2.easing}`, SLIDE = 5, GAP = 6, HINT_GAP = 10, SEP = "›";
|
|
564
|
+
function createCrumbs(o) {
|
|
565
|
+
let doc = o.header.ownerDocument, element = doc.createElement("div");
|
|
566
|
+
element.className = "knobs-crumbs", element.setAttribute("aria-hidden", "true"), o.header.append(element);
|
|
567
|
+
let live = /* @__PURE__ */ new Map, trail = [], esc, hinting = !1, ctx, width = (text) => {
|
|
568
|
+
if (ctx === void 0)
|
|
569
|
+
ctx = doc.createElement("canvas").getContext("2d");
|
|
570
|
+
if (!ctx)
|
|
571
|
+
return text.length * 6;
|
|
572
|
+
let cs = getComputedStyle(element);
|
|
573
|
+
return ctx.font = `${cs.fontWeight} ${cs.fontSize} ${cs.fontFamily}`, ctx.measureText(text).width;
|
|
574
|
+
}, titleEnd = (head) => {
|
|
575
|
+
let t = o.title.getBoundingClientRect();
|
|
576
|
+
if (!o.title.textContent)
|
|
577
|
+
return { x: t.left - head.left, right: t.right - head.left, t };
|
|
578
|
+
let range = doc.createRange();
|
|
579
|
+
range.selectNodeContents(o.title);
|
|
580
|
+
let r = range.getBoundingClientRect();
|
|
581
|
+
return { x: Math.min(r.right, t.right) - head.left + GAP, right: t.right - head.left, t };
|
|
582
|
+
}, build = () => {
|
|
583
|
+
if (!trail.length)
|
|
584
|
+
return { pieces: [], xs: [] };
|
|
585
|
+
let head = o.header.getBoundingClientRect(), { x, right } = titleEnd(head), sepW = width(SEP) + GAP * 2, escText = esc ? `esc ↑ ${esc}` : "", fit = fitCrumbs(trail.map((c) => width(c.label)), Math.max(0, right - x - sepW), sepW, width("…"), escText ? width(escText) + HINT_GAP - sepW : 0), pieces = [{ key: "sep:lead", text: SEP, kind: "sep" }];
|
|
586
|
+
if (fit.from > 0)
|
|
587
|
+
pieces.push({ key: "more", text: "…", kind: "more" }, { key: "sep:more", text: SEP, kind: "sep" });
|
|
588
|
+
if (trail.forEach((c, i) => {
|
|
589
|
+
if (i < fit.from)
|
|
590
|
+
return;
|
|
591
|
+
if (i > fit.from)
|
|
592
|
+
pieces.push({ key: `sep:${c.level}`, text: SEP, kind: "sep" });
|
|
593
|
+
pieces.push({
|
|
594
|
+
key: `${c.level}:${c.label}`,
|
|
595
|
+
text: c.label,
|
|
596
|
+
kind: "crumb",
|
|
597
|
+
level: c.level,
|
|
598
|
+
deep: i === trail.length - 1
|
|
599
|
+
});
|
|
600
|
+
}), escText && fit.hint)
|
|
601
|
+
pieces.push({ key: `esc:${esc}`, text: escText, kind: "esc" });
|
|
602
|
+
let xs = [], at = x;
|
|
603
|
+
for (let p of pieces) {
|
|
604
|
+
if (p.kind === "esc")
|
|
605
|
+
at += HINT_GAP - GAP;
|
|
606
|
+
xs.push(at), at += width(p.text) + (p.kind === "sep" ? GAP : p.kind === "esc" ? 0 : GAP);
|
|
607
|
+
}
|
|
608
|
+
return { pieces, xs };
|
|
609
|
+
}, place = () => {
|
|
610
|
+
let head = o.header.getBoundingClientRect(), t = o.title.getBoundingClientRect();
|
|
611
|
+
element.style.top = `${(t.top - head.top + t.height / 2).toFixed(2)}px`;
|
|
612
|
+
}, leave = (key, node, x) => {
|
|
613
|
+
if (live.delete(key), node.dataset.leaving = "", node.style.opacity = "0", !o.reduced())
|
|
614
|
+
node.style.translate = `${(x - SLIDE).toFixed(2)}px -50%`;
|
|
615
|
+
setTimeout(() => node.remove(), 320);
|
|
616
|
+
}, render = () => {
|
|
617
|
+
place();
|
|
618
|
+
let { pieces, xs } = build(), keep = new Set(pieces.map((p) => p.key));
|
|
619
|
+
for (let [key, node] of live)
|
|
620
|
+
if (!keep.has(key))
|
|
621
|
+
leave(key, node, parseFloat(node.dataset.x ?? "0"));
|
|
622
|
+
let reduced = o.reduced();
|
|
623
|
+
pieces.forEach((p, i) => {
|
|
624
|
+
let x = xs[i], node = live.get(p.key);
|
|
625
|
+
if (!node) {
|
|
626
|
+
if (node = doc.createElement("span"), node.className = `knobs-crumb-${p.kind}`, node.textContent = p.text, p.level)
|
|
627
|
+
node.dataset.level = p.level;
|
|
628
|
+
node.style.opacity = "0", node.style.translate = `${(x + (reduced ? 0 : SLIDE)).toFixed(2)}px -50%`, element.append(node), live.set(p.key, node), node.getBoundingClientRect();
|
|
629
|
+
}
|
|
630
|
+
node.dataset.x = String(x), node.toggleAttribute("data-deep", Boolean(p.deep)), node.style.translate = `${x.toFixed(2)}px -50%`, node.style.opacity = p.kind === "esc" && !hinting ? "0" : "1";
|
|
631
|
+
}), o.wake();
|
|
632
|
+
}, onDown = (event) => {
|
|
633
|
+
if (!event.target.closest?.("[data-level]"))
|
|
634
|
+
return;
|
|
635
|
+
event.preventDefault(), event.stopPropagation();
|
|
636
|
+
}, onClick = (event) => {
|
|
637
|
+
let node = event.target.closest?.("[data-level]");
|
|
638
|
+
if (!node || "leaving" in node.dataset)
|
|
639
|
+
return;
|
|
640
|
+
event.stopPropagation(), o.jump(node.dataset.level);
|
|
641
|
+
};
|
|
642
|
+
element.addEventListener("pointerdown", onDown), element.addEventListener("mousedown", onDown), element.addEventListener("click", onClick);
|
|
643
|
+
let size = "", sizes = typeof ResizeObserver > "u" ? void 0 : new ResizeObserver(() => {
|
|
644
|
+
let r = o.title.getBoundingClientRect(), next = `${r.width}|${r.height}|${o.title.textContent}`;
|
|
645
|
+
if (next === size)
|
|
646
|
+
return;
|
|
647
|
+
if (size = next, trail.length)
|
|
648
|
+
render();
|
|
649
|
+
});
|
|
650
|
+
return sizes?.observe(o.header), sizes?.observe(o.title), {
|
|
651
|
+
element,
|
|
652
|
+
set(next, nextEsc) {
|
|
653
|
+
if (next.length === trail.length && next.every((c, i) => c.level === trail[i].level && c.label === trail[i].label) && nextEsc === esc)
|
|
654
|
+
return;
|
|
655
|
+
trail = next, esc = nextEsc, render();
|
|
656
|
+
},
|
|
657
|
+
hint(on) {
|
|
658
|
+
if (on === hinting)
|
|
659
|
+
return;
|
|
660
|
+
hinting = on;
|
|
661
|
+
for (let [key, node] of live)
|
|
662
|
+
if (key.startsWith("esc:"))
|
|
663
|
+
node.style.opacity = on ? "1" : "0";
|
|
664
|
+
o.wake();
|
|
665
|
+
},
|
|
666
|
+
layout() {
|
|
667
|
+
if (trail.length)
|
|
668
|
+
render();
|
|
669
|
+
},
|
|
670
|
+
dispose() {
|
|
671
|
+
sizes?.disconnect(), element.remove();
|
|
672
|
+
}
|
|
673
|
+
};
|
|
674
|
+
}
|
|
675
|
+
|
|
396
676
|
// src/panel/styles.ts
|
|
397
|
-
var lead = springEasing(LEAD.omega, LEAD.zeta), trail = springEasing(TRAIL.omega, TRAIL.zeta), fill = springEasing(FILL.omega, FILL.zeta), LEAD_T = `${lead.ms}ms var(--k-lead)`, TRAIL_T = `calc(${trail.ms}ms * (.6 + .4 * clamp(0, var(--k-blur), 2))) var(--k-trail)`, FILL_T = "calc(var(--k-fill-ms) * 1ms * clamp(var(--k-blur),.1,2)) var(--k-fill-ease)", SLIDE_T = "calc(var(--k-fill-ms) * 1ms) var(--k-fill-ease)", snappy = springEasing(2 * Math.PI / 0.25, 1), snappyOut = springEasing(2 * Math.PI / 0.2, 1), BLOOM_IN = `${snappy.ms}ms ${snappy.easing}`, BLOOM_OUT = `${snappyOut.ms}ms ${snappyOut.easing}`, DELAY = "var(--k-delay,0ms)", POINTER_ROW = ".knobs-panel[data-k-row-hover]:not([data-keyboard])", DELAYED = ".knobs-fill,.knobs-handle,.knobs-seg-pill,.knobs-toggle,.knobs-toggle i,.knobs-arm,.knobs-curve,.knobs-swatch,.knobs-strip-thumb,.knobs-pad>i,.knobs-ease-handle,.knobs-range-track,.knobs-range-fill,.knobs-range-thumb,.knobs-dial-hand,.knobs-grad,.knobs-grad-mini,.knobs-grad-mini>i,.knobs-grad-stop,.knobs-graph-pt", GRAD_N = GRADIENT_SAMPLES, gradStops = (at) => Array.from({ length: GRAD_N }, (_, i) => `var(--kgrad-${i}) ${at(i)}`).join(","), GRAD_STRIP = `linear-gradient(to right,${gradStops((i) => `calc(var(--kg-r) + (100% - var(--kg-r) * 2) * ${Number((i / (GRAD_N - 1)).toFixed(5))})`)})`, GRAD_MINI = `linear-gradient(to right,${gradStops((i) => `${Number((i / (GRAD_N - 1) * 100).toFixed(3))}%`)})`, GRAD_PROPS = Array.from({ length: GRAD_N }, (_, i) => `@property --kgrad-${i}{syntax:"<color>";inherits:false;initial-value:transparent}`).join(`
|
|
677
|
+
var lead = springEasing(LEAD.omega, LEAD.zeta), trail = springEasing(TRAIL.omega, TRAIL.zeta), fill = springEasing(FILL.omega, FILL.zeta), LEAD_T = `${lead.ms}ms var(--k-lead)`, TRAIL_T = `calc(${trail.ms}ms * (.6 + .4 * clamp(0, var(--k-blur), 2))) var(--k-trail)`, FILL_T = "calc(var(--k-fill-ms) * 1ms * clamp(var(--k-blur),.1,2)) var(--k-fill-ease)", SLIDE_T = "calc(var(--k-fill-ms) * 1ms) var(--k-fill-ease)", snappy = springEasing(2 * Math.PI / 0.25, 1), snappyOut = springEasing(2 * Math.PI / 0.2, 1), BLOOM_IN = `${snappy.ms}ms ${snappy.easing}`, BLOOM_OUT = `${snappyOut.ms}ms ${snappyOut.easing}`, DELAY = "var(--k-delay,0ms)", armed = springEasing(2 * Math.PI / 0.3, 1), ARMED_T = `${armed.ms}ms ${armed.easing}`, POINTER_ROW = ".knobs-panel[data-k-row-hover]:not([data-keyboard])", DELAYED = ".knobs-fill,.knobs-handle,.knobs-seg-pill,.knobs-toggle,.knobs-toggle i,.knobs-arm,.knobs-curve,.knobs-swatch,.knobs-strip-thumb,.knobs-pad>i,.knobs-ease-handle,.knobs-range-track,.knobs-range-fill,.knobs-range-thumb,.knobs-dial-hand,.knobs-grad,.knobs-grad-mini,.knobs-grad-mini>i,.knobs-grad-stop,.knobs-graph-pt", GRAD_N = GRADIENT_SAMPLES, gradStops = (at) => Array.from({ length: GRAD_N }, (_, i) => `var(--kgrad-${i}) ${at(i)}`).join(","), GRAD_STRIP = `linear-gradient(to right,${gradStops((i) => `calc(var(--kg-r) + (100% - var(--kg-r) * 2) * ${Number((i / (GRAD_N - 1)).toFixed(5))})`)})`, GRAD_MINI = `linear-gradient(to right,${gradStops((i) => `${Number((i / (GRAD_N - 1) * 100).toFixed(3))}%`)})`, GRAD_PROPS = Array.from({ length: GRAD_N }, (_, i) => `@property --kgrad-${i}{syntax:"<color>";inherits:false;initial-value:transparent}`).join(`
|
|
398
678
|
`), COLOR_TOKENS = [
|
|
399
679
|
"bg",
|
|
400
680
|
"surface",
|
|
@@ -433,6 +713,7 @@ ${register(["width"], "<length-percentage>", "288px")}
|
|
|
433
713
|
@property --k-head-s{syntax:"<number>";inherits:true;initial-value:0}
|
|
434
714
|
@property --k-head-v{syntax:"<number>";inherits:true;initial-value:0}
|
|
435
715
|
@property --kc-value-size{syntax:"<length>";inherits:true;initial-value:11.5px}
|
|
716
|
+
@property --k-armed{syntax:"<number>";inherits:false;initial-value:0}
|
|
436
717
|
@property --kr-lo{syntax:"<number>";inherits:true;initial-value:0}
|
|
437
718
|
@property --kpt-sel{syntax:"<number>";inherits:false;initial-value:1}
|
|
438
719
|
@property --kgs-sel{syntax:"<number>";inherits:false;initial-value:1}
|
|
@@ -454,6 +735,8 @@ ${GRAD_PROPS}
|
|
|
454
735
|
--k-font-weight:400;--k-title-weight:600;--k-thumb:#fff;--k-lift:1;--k-frame:card;
|
|
455
736
|
--k-row-gap:0px;--k-hover-in:0;--k-hover-out:120;--k-fill-base:var(--k-surface-hi);--k-fill-rest-mix:20%;--k-fill-hover-mix:var(--k-fill-rest-mix);--k-fill-active-mix:30%;--k-chrome:1;--k-handle:var(--k-accent);--k-handle-inset:0px;--k-handle-rest:0;--k-step-resist:0.25;--k-motion-blur:0;--k-range-blur:.5;--k-curve-blur:.5;--k-group-transform:uppercase;--k-value-font:var(--k-font);
|
|
456
737
|
--k-lead:${lead.easing};--k-trail:${trail.easing};--k-fill-ease:${fill.easing};--k-fill-ms:${fill.ms};
|
|
738
|
+
--k-lens-ms:180;--k-lens-lead:1;--k-lens-wash:.9;--k-lens-line:.65;--k-lens-panel-wash:.35;--k-lens-inset:0px;--k-lens-panel-inset:0px;
|
|
739
|
+
--k-armed-edge:.1;--k-armed-glow:0;--k-armed-glow-a:0;--k-title-rest:.6;--k-esc-hint-ms:1800;
|
|
457
740
|
}
|
|
458
741
|
/* Glass edges are hairlines: --k-rim-width is one device pixel at common pixel ratios. */
|
|
459
742
|
@media(min-resolution:1.5dppx){:where(:root,:host){--k-rim-width:.67px}}
|
|
@@ -519,7 +802,7 @@ ${GRAD_PROPS}
|
|
|
519
802
|
.knobs-panel,.knobs-chip,.knobs-menu{letter-spacing:normal;word-spacing:normal;text-transform:none;text-indent:0;text-shadow:none;text-decoration:none;font-style:normal;font-variant:normal;font-feature-settings:normal;font-kerning:auto;white-space:normal;direction:ltr;margin:0;float:none}
|
|
520
803
|
:where(.knobs-panel,.knobs-chip) :where(*,*::before,*::after){box-sizing:border-box}
|
|
521
804
|
.knobs-panel .rn-slot{box-sizing:content-box}
|
|
522
|
-
.knobs-panel [hidden],.knobs-chip[hidden]{display:none!important}
|
|
805
|
+
.knobs-panel [hidden],.knobs-panel[hidden],.knobs-chip[hidden]{display:none!important}
|
|
523
806
|
.knobs-mono{font-family:var(--k-mono);font-variant-numeric:tabular-nums}
|
|
524
807
|
|
|
525
808
|
/* The chip: a round cog button, or a capsule with a label beside the cog. */
|
|
@@ -546,13 +829,17 @@ ${GRAD_PROPS}
|
|
|
546
829
|
.knobs-panel,.knobs-chip{--k-token-t:calc(var(--k-fill-ms) * 1ms) var(--k-fill-ease)}
|
|
547
830
|
:root[data-knobs-scrub] .knobs-chip{--k-token-t:0s}
|
|
548
831
|
:root[data-knobs-scrub] .knobs-panel{transition:none}
|
|
549
|
-
@media(prefers-reduced-motion:reduce){.knobs-chip{--k-token-t:0s}.knobs-panel.knobs-panel{transition
|
|
832
|
+
@media(prefers-reduced-motion:reduce){.knobs-chip{--k-token-t:0s}.knobs-panel.knobs-panel{transition:--k-armed .16s linear}}
|
|
833
|
+
.knobs-panel[data-k-armed]{--k-armed:1}
|
|
550
834
|
/* Panel material: --k-glass (0 opaque … 1 clearest) thins the surface over a blurred, saturated
|
|
551
835
|
backdrop, toned toward the theme (--k-glass-tone dims it under dark panels) so text keeps its
|
|
552
836
|
contrast. Its edge is a hairline (--k-edge, --k-rim-width wide) and a hairline of light (--k-rim)
|
|
553
837
|
catches its top. */
|
|
554
|
-
.knobs-panel{-webkit-user-select:none;user-select:none;transition:${TOKEN_TRANSITIONS};position:fixed;z-index:2147480001;width:var(--k-width);max-width:calc(100vw - 16px);max-height:calc(100dvh - 16px);overflow:auto;padding:var(--k-pad);
|
|
555
|
-
background:color-mix(in srgb,var(--k-bg) calc(100% - var(--k-glass) * 20%),transparent);border:1px solid transparent;border-radius:var(--k-panel-radius);box-shadow:var(--k-shadow),0 0 0 var(--k-rim-width) var(--
|
|
838
|
+
.knobs-panel{-webkit-user-select:none;user-select:none;transition:${TOKEN_TRANSITIONS},--k-armed ${ARMED_T};position:fixed;z-index:2147480001;width:var(--k-width);max-width:calc(100vw - 16px);max-height:calc(100dvh - 16px);overflow:auto;padding:var(--k-pad);
|
|
839
|
+
background:color-mix(in srgb,var(--k-bg) calc(100% - var(--k-glass) * 20%),transparent);border:1px solid transparent;border-radius:var(--k-panel-radius);box-shadow:var(--k-shadow),0 0 0 var(--k-rim-width) var(--ka-edge),inset 0 var(--k-rim-width) 0 var(--k-rim),var(--ka-glow);scrollbar-width:thin;
|
|
840
|
+
/* Armed (focus inside: keys come here), the edge takes the accent and a faint glow lifts it. */
|
|
841
|
+
--ka-edge:color-mix(in srgb,var(--k-accent) calc(var(--k-armed) * var(--k-armed-edge) * 100%),var(--k-edge));
|
|
842
|
+
--ka-glow:0 0 calc(var(--k-armed-glow) * 1px) color-mix(in srgb,var(--k-accent) calc(var(--k-armed) * var(--k-armed-glow-a) * var(--k-chrome,1) * 100%),transparent);
|
|
556
843
|
--k-backdrop:blur(calc(var(--k-glass) * 24px)) saturate(calc(1 + var(--k-glass) * .8)) brightness(calc(1 + (var(--k-glass-tone) - 1) * var(--k-glass)));
|
|
557
844
|
-webkit-backdrop-filter:var(--k-backdrop);backdrop-filter:var(--k-backdrop);
|
|
558
845
|
animation:knobs-in .16s var(--k-ease);}
|
|
@@ -570,7 +857,7 @@ ${GRAD_PROPS}
|
|
|
570
857
|
.knobs-morph::after{box-shadow:var(--k-shadow);opacity:var(--p)}
|
|
571
858
|
/* Meanwhile the chip is just its label, fading on the shape. */
|
|
572
859
|
.knobs-chip.knobs-chip[data-morph]{visibility:visible;pointer-events:auto;transform:none;transition:none;background:transparent;border-color:transparent;box-shadow:none}
|
|
573
|
-
.knobs-panel.knobs-inline{position:relative;z-index:auto;left:auto!important;top:auto!important;max-width:100%;max-height:none;box-shadow:0 0 0 var(--k-rim-width) color-mix(in srgb,var(--
|
|
860
|
+
.knobs-panel.knobs-inline{position:relative;z-index:auto;left:auto!important;top:auto!important;max-width:100%;max-height:none;box-shadow:0 0 0 var(--k-rim-width) color-mix(in srgb,var(--ka-edge) calc(var(--k-chrome) * 100%),transparent),inset 0 var(--k-rim-width) 0 color-mix(in srgb,var(--k-rim) calc(var(--k-chrome) * 100%),transparent),var(--ka-glow);animation:none;
|
|
574
861
|
/* --k-chrome 0: an embedded panel sheds its card (background, border, rim) for the host's column,
|
|
575
862
|
border width too, so its rows line up with the column's edges. */
|
|
576
863
|
border-width:calc(var(--k-chrome) * 1px);
|
|
@@ -589,7 +876,7 @@ ${GRAD_PROPS}
|
|
|
589
876
|
:is(.knobs-panel,.knobs-menu) :is(:focus,:focus-visible):where(:not(.knobs-accessory *)){outline:none}
|
|
590
877
|
|
|
591
878
|
/* Header */
|
|
592
|
-
.knobs-panel header{display:flex;align-items:center;justify-content:space-between;gap:6px;cursor:grab;touch-action:none;padding:0 var(--k-inset) var(--k-section-gap);min-height:var(--k-icon-btn)}
|
|
879
|
+
.knobs-panel header{position:relative;display:flex;align-items:center;justify-content:space-between;gap:6px;cursor:grab;touch-action:none;padding:0 var(--k-inset) var(--k-section-gap);min-height:var(--k-icon-btn)}
|
|
593
880
|
.knobs-panel.knobs-inline header,.knobs-panel.knobs-inline header:active{cursor:default}
|
|
594
881
|
/* The title row stays pinned while the rows scroll under it, in the panel (it scrolls once it's
|
|
595
882
|
bounded) or, with .knobs-pin-ancestor (which also makes the panel overflow:visible), in the
|
|
@@ -616,7 +903,21 @@ ${GRAD_PROPS}
|
|
|
616
903
|
.knobs-panel[data-gpu-active="true"]:not(.knobs-page)>header::after{display:none}
|
|
617
904
|
}
|
|
618
905
|
.knobs-panel header:active{cursor:grabbing}
|
|
619
|
-
.knobs-panel header strong{flex:1;min-width:0;font-weight:var(--k-title-weight);font-size:var(--k-text-title);letter-spacing:-.01em;white-space:nowrap;overflow:hidden;text-overflow:ellipsis;transition:opacity .2s var(--k-ease)}
|
|
906
|
+
.knobs-panel header strong{flex:1;min-width:0;font-weight:var(--k-title-weight);font-size:var(--k-text-title);letter-spacing:-.01em;white-space:nowrap;overflow:hidden;text-overflow:ellipsis;transition:opacity .2s var(--k-ease),color ${ARMED_T};
|
|
907
|
+
color:color-mix(in srgb,var(--k-text) calc(var(--k-title-rest) * 100%),var(--k-label))}
|
|
908
|
+
/* At rest a panel's title is a little quiet; armed (keys come here) it's at full strength. */
|
|
909
|
+
.knobs-panel[data-k-armed] header strong{color:var(--k-text)}
|
|
910
|
+
/* The scope line beside the title (crumbs.ts): where keys go, and for a moment where Esc goes. */
|
|
911
|
+
.knobs-crumbs{position:absolute;left:0;top:50%;height:0;pointer-events:none;white-space:nowrap;font-size:var(--k-text-meta);font-weight:var(--k-label-weight);line-height:1.2;letter-spacing:0;text-transform:none}
|
|
912
|
+
.knobs-crumbs>*{position:absolute;left:0;top:0;translate:0 -50%;color:var(--k-dim);transition:opacity .2s var(--k-ease),translate ${CRUMB_GLIDE},color .2s var(--k-ease)}
|
|
913
|
+
.knobs-crumbs>[data-leaving]{transition:opacity .14s cubic-bezier(.4,0,1,1),translate .14s cubic-bezier(.4,0,1,1)}
|
|
914
|
+
.knobs-crumbs>.knobs-crumb-crumb{pointer-events:auto;cursor:pointer}
|
|
915
|
+
.knobs-crumbs>.knobs-crumb-crumb[data-deep]{color:var(--k-text)}
|
|
916
|
+
.knobs-crumbs>.knobs-crumb-crumb:not([data-deep]):hover{color:var(--k-label)}
|
|
917
|
+
.knobs-crumbs>.knobs-crumb-sep{color:color-mix(in srgb,var(--k-dim) 70%,transparent)}
|
|
918
|
+
.knobs-crumbs>.knobs-crumb-esc{font-family:var(--k-mono);font-size:var(--k-text-cap);transition:opacity .35s var(--k-ease),translate ${CRUMB_GLIDE}}
|
|
919
|
+
@media(prefers-reduced-motion:reduce){.knobs-panel .knobs-crumbs>*{transition:opacity .15s linear!important}}
|
|
920
|
+
.knobs-panel[data-gpu-active="true"] .knobs-crumbs>*{color:transparent!important}
|
|
620
921
|
/* Holding \\ shows the values from before the last change; the title dims meanwhile. */
|
|
621
922
|
.knobs-panel[data-comparing] header strong{opacity:.4}
|
|
622
923
|
.knobs-head-actions{display:flex;align-items:center;margin-right:calc((var(--k-icon-size) - var(--k-icon-btn)) / 2)}
|
|
@@ -714,6 +1015,23 @@ ${GRAD_PROPS}
|
|
|
714
1015
|
.knobs-card{position:relative;margin-bottom:var(--k-section-gap);border-radius:var(--k-radius);background:var(--k-surface);overflow:hidden}
|
|
715
1016
|
.knobs-card:last-child{margin-bottom:0}
|
|
716
1017
|
.knobs-card>*{position:relative;min-height:var(--k-row);transition:background var(--k-hover-t),opacity .2s}
|
|
1018
|
+
.knobs-panel [data-tab-opacity]{transition:none!important}
|
|
1019
|
+
/* Rubber-band ends (drawStretch in give.ts): past a limit a control stretches about its far end
|
|
1020
|
+
and thins a little, its stretching corners rounding off; a row draws past its card's edge
|
|
1021
|
+
meanwhile (keeping the card's corners where it has them), and its text stays put. */
|
|
1022
|
+
.knobs-stretching{scale:var(--k-sx) var(--k-sy);transform-origin:var(--k-sox) var(--k-soy)}
|
|
1023
|
+
.knobs-card[data-k-stretch]{overflow:visible}
|
|
1024
|
+
.knobs-card>:first-child{--k-rt:1}
|
|
1025
|
+
.knobs-card>:last-child{--k-rb:1}
|
|
1026
|
+
.knobs-card>.knobs-stretching{--k-r:var(--k-radius);border-radius:calc(var(--k-r) * var(--k-rt,0)) calc(var(--k-r) * var(--k-rt,0)) calc(var(--k-r) * var(--k-rb,0)) calc(var(--k-r) * var(--k-rb,0))}
|
|
1027
|
+
.knobs-card>.knobs-stretching[data-stretch="end"]{border-top-right-radius:calc(var(--k-r) * max(var(--k-rt,0),var(--k-sf)));border-bottom-right-radius:calc(var(--k-r) * max(var(--k-rb,0),var(--k-sf)))}
|
|
1028
|
+
.knobs-card>.knobs-stretching[data-stretch="start"]{border-top-left-radius:calc(var(--k-r) * max(var(--k-rt,0),var(--k-sf)));border-bottom-left-radius:calc(var(--k-r) * max(var(--k-rb,0),var(--k-sf)))}
|
|
1029
|
+
.knobs-stretching>:is(.knobs-track,.knobs-range-track){border-radius:inherit}
|
|
1030
|
+
.knobs-slider.knobs-stretching :is(.knobs-label,.knobs-value),.knobs-range.knobs-stretching .knobs-range-text{scale:calc(1 / var(--k-sx)) calc(1 / var(--k-sy))}
|
|
1031
|
+
.knobs-slider.knobs-stretching .knobs-label{transform-origin:calc(var(--k-ox) - var(--k-inset)) 0}
|
|
1032
|
+
.knobs-panel .knobs-slider.knobs-stretching .knobs-value{transform-origin:calc(var(--k-ox) - var(--k-w) + var(--k-inset) + 100%) 0}
|
|
1033
|
+
.knobs-range.knobs-stretching .knobs-range-text{transform-origin:var(--k-sox) 50%}
|
|
1034
|
+
.knobs-stretching>:is(.knobs-strip-thumb,.knobs-pad-puck,.knobs-pad-home){transform:scale(calc(1 / var(--k-sx)),calc(1 / var(--k-sy)))}
|
|
717
1035
|
.knobs-card>:hover,.knobs-card>.knobs-active{background:var(--k-row-hover)}
|
|
718
1036
|
.knobs-card>*+*::before{content:"";position:absolute;z-index:1;left:0;right:0;top:0;height:1px;transform:scaleY(.5);transform-origin:top;background:var(--k-line);pointer-events:none;transition:opacity .15s}
|
|
719
1037
|
.knobs-card>::after{content:"";position:absolute;z-index:2;inset:0;background:var(--k-surface-hi);opacity:0;pointer-events:none;transition:opacity .15s,box-shadow .15s}
|
|
@@ -726,6 +1044,13 @@ ${GRAD_PROPS}
|
|
|
726
1044
|
/* One wash at a time: while the pointer has a row (and keys haven't taken over since), a focus
|
|
727
1045
|
wash elsewhere steps aside. */
|
|
728
1046
|
${POINTER_ROW} :is(.knobs-card>*,.knobs-sub,.knobs-x-head):not(:hover)::after{opacity:0}
|
|
1047
|
+
/* The focus lens (lens.ts) marks the level keys act on, and replaces the washes above: one shape,
|
|
1048
|
+
a soft wash and a hairline edge, placed from script (--kl-w, --kl-l). */
|
|
1049
|
+
.knobs-panel[data-k-lens] :is(.knobs-card>*,.knobs-sub,.knobs-x-head,.knobs-card,.knobs-tile)::after{opacity:0!important}
|
|
1050
|
+
.knobs-lens{position:absolute;left:0;top:0;z-index:2;width:0;height:0;opacity:0;pointer-events:none;box-sizing:border-box;contain:strict;will-change:transform,opacity;
|
|
1051
|
+
background:color-mix(in srgb,var(--k-surface-hi) calc(var(--kl-w,0) * 100%),transparent);
|
|
1052
|
+
box-shadow:inset 0 0 0 var(--k-rim-width) color-mix(in srgb,var(--k-line-hi) calc(var(--kl-l,0) * 100%),transparent)}
|
|
1053
|
+
.knobs-panel[data-gpu-active="true"] .knobs-lens{visibility:hidden}
|
|
729
1054
|
.knobs-panel .knobs-slider{height:var(--k-row);contain:layout style}
|
|
730
1055
|
.knobs-panel .knobs-row:where(:not(.knobs-slider,.knobs-x,.knobs-spring)){display:flex;align-items:center;justify-content:space-between;gap:8px;height:var(--k-row);padding:0 var(--k-inset);contain:layout style}
|
|
731
1056
|
.knobs-panel .knobs-row>span:first-child{color:var(--k-label);font-weight:var(--k-label-weight);min-width:0;overflow:hidden;text-overflow:ellipsis;white-space:nowrap}
|
|
@@ -762,12 +1087,19 @@ ${POINTER_ROW} :is(.knobs-card>*,.knobs-sub,.knobs-x-head):not(:hover)::after{op
|
|
|
762
1087
|
.knobs-smear .knobs-fill{background:linear-gradient(to left,transparent,var(--k-fill-tint) calc(var(--k-smear) * 1px));transform:translateX(calc(var(--k-fill) - 100% + var(--k-smear) * .5px))}
|
|
763
1088
|
.knobs-smear .knobs-handle::before{scale:calc(1 + var(--k-smear) / 3) 1;opacity:calc(3 / (3 + var(--k-smear)))}
|
|
764
1089
|
.knobs-animate .knobs-fill,.knobs-animate .knobs-handle{transition:--k-fill ${SLIDE_T} ${DELAY},--k-fill-mix .15s,opacity .15s}
|
|
765
|
-
|
|
766
|
-
|
|
1090
|
+
/* The default: a short notch rising from the bottom edge, only while the row is in hand. The
|
|
1091
|
+
active preset's value hangs one from the top edge. */
|
|
767
1092
|
.knobs-marks>.knobs-notch{top:auto;bottom:0;width:1.5px;height:4px;border-radius:999px 999px 0 0;transform:translateX(-50%);background:var(--k-accent);opacity:0;transition:opacity .2s,height .15s var(--k-ease)}
|
|
1093
|
+
.knobs-marks>.knobs-base{top:0;bottom:auto;border-radius:0 0 999px 999px}
|
|
768
1094
|
.knobs-slider:is(:hover,.knobs-active,:has(:focus-visible)) .knobs-notch{opacity:.38}
|
|
769
|
-
.knobs-slider.knobs-modified:is(:hover,.knobs-active,:has(:focus-visible)) .knobs-notch{opacity:.55}
|
|
770
|
-
.knobs-magnet .knobs-notch{opacity:.95;height:6px}
|
|
1095
|
+
.knobs-slider.knobs-modified:is(:hover,.knobs-active,:has(:focus-visible)) .knobs-notch:not(.knobs-base){opacity:.55}
|
|
1096
|
+
.knobs-magnet .knobs-notch:not(.knobs-base),.knobs-held-base .knobs-base{opacity:.95;height:6px}
|
|
1097
|
+
/* Precision by distance: a fine ruler around the handle, a level for each halving of the rate,
|
|
1098
|
+
each finer, shorter and fainter than the last. */
|
|
1099
|
+
.knobs-ruler{position:absolute;inset:0;pointer-events:none;--k-ruler-w:linear-gradient(to right,transparent calc(var(--k-fill) - ${RULER_FADE}px),#000 calc(var(--k-fill) - ${RULER_NEAR}px),#000 calc(var(--k-fill) + ${RULER_NEAR}px),transparent calc(var(--k-fill) + ${RULER_FADE}px));-webkit-mask:var(--k-ruler-w);mask:var(--k-ruler-w)}
|
|
1100
|
+
.knobs-ruler>i{position:absolute;left:0;right:0;top:50%;opacity:0;translate:-.5px 0;transition:opacity .18s var(--k-ease)}
|
|
1101
|
+
${RULER.map(([n, h, a], i) => `.knobs-ruler>i:nth-child(${i + 1}){height:${h}px;margin-top:${-h / 2}px;background:repeating-linear-gradient(to right,color-mix(in srgb,var(--k-accent) ${a * 100}%,transparent) 0 1px,transparent 1px calc(100% / ${n}))}`).join(`
|
|
1102
|
+
`)}
|
|
771
1103
|
/* The handle is a full-width layer moved by the fill (translate's % is its own width, the track's). */
|
|
772
1104
|
.knobs-handle{position:absolute;inset:0;pointer-events:none;transform:translateX(var(--k-fill));opacity:var(--k-handle-rest);transition:opacity .15s}
|
|
773
1105
|
.knobs-handle::before{content:"";position:absolute;left:0;top:50%;width:3px;height:calc(var(--k-row) - 12px);margin-top:calc(6px - var(--k-row) / 2);translate:calc(-50% - var(--k-handle-inset)) 0;border-radius:var(--k-round);background:var(--k-handle);
|
|
@@ -977,7 +1309,7 @@ ${POINTER_ROW} .knobs-slider.knobs-modified:not(:hover,.knobs-active) .knobs-val
|
|
|
977
1309
|
/* Rows as separate bars (--k-row-gap above 0): each row carries its own surface, no hairlines.
|
|
978
1310
|
A spring stacks its curve over its two sliders. */
|
|
979
1311
|
.knobs-panel[data-rows] .knobs-card{display:flex;flex-direction:column;gap:var(--k-row-gap);background:transparent;overflow:visible}
|
|
980
|
-
.knobs-panel[data-rows] .knobs-card>*{background:var(--k-surface);border-radius:var(--k-radius);overflow:hidden}
|
|
1312
|
+
.knobs-panel[data-rows] .knobs-card>*{background:var(--k-surface);border-radius:var(--k-radius);overflow:hidden;--k-rt:1;--k-rb:1}
|
|
981
1313
|
.knobs-panel[data-rows] .knobs-card>:is(:hover,.knobs-active){background:color-mix(in srgb,var(--k-surface-hi) 50%,var(--k-surface))}
|
|
982
1314
|
.knobs-panel[data-rows] :is(.knobs-card>*,.knobs-sub)::before{display:none}
|
|
983
1315
|
.knobs-panel[data-rows] .knobs-card>.knobs-subhead{background:transparent}
|
|
@@ -1180,17 +1512,19 @@ ${POINTER_ROW} .knobs-slider.knobs-modified:not(:hover,.knobs-active) .knobs-val
|
|
|
1180
1512
|
/* Hint mode: stable short codes, zero layout shift */
|
|
1181
1513
|
.knobs-hinting .knobs-hint-miss{opacity:var(--k-disabled-opacity);transition:opacity .1s}
|
|
1182
1514
|
.knobs-hint-layer{position:absolute;left:0;top:0;width:0;height:0;pointer-events:none;z-index:5;animation:knobs-in .1s var(--k-ease)}
|
|
1183
|
-
|
|
1515
|
+
/* The badges are <kbd>s, so they outrank the scoped reset's (0,1,1). */
|
|
1516
|
+
.knobs-panel .knobs-hint{position:absolute;transform:translateY(-50%);display:inline-flex;align-items:center;height:15px;padding:0 4px;border-radius:var(--k-radius-sm);background:var(--k-accent);color:var(--k-bg);box-shadow:0 1px 3px rgba(0,0,0,calc(.35 * var(--k-lift)));
|
|
1184
1517
|
font:700 var(--k-text-cap)/1 var(--k-mono);letter-spacing:.04em;text-transform:uppercase;pointer-events:none}
|
|
1518
|
+
.knobs-panel .knobs-hint[hidden]{display:none}
|
|
1185
1519
|
.knobs-hint-typed{opacity:var(--k-hint-typed-opacity)}
|
|
1186
1520
|
.knobs-hint-rest{opacity:1}
|
|
1187
1521
|
|
|
1188
1522
|
/* A focused row's options: tiny digits in hint mode's look, once focus settles (optkeys.ts). */
|
|
1189
1523
|
.knobs-optkeys{position:absolute;left:0;top:0;width:0;height:0;pointer-events:none;z-index:5}
|
|
1190
|
-
.knobs-optkey{position:absolute;transform:translateY(-50%);display:inline-flex;align-items:center;height:12px;padding:0 3px;border-radius:var(--k-radius-sm);background:var(--k-accent);color:var(--k-bg);box-shadow:0 1px 3px rgba(0,0,0,calc(.35 * var(--k-lift)));
|
|
1524
|
+
.knobs-panel .knobs-optkey{position:absolute;transform:translateY(-50%);display:inline-flex;align-items:center;height:12px;padding:0 3px;border-radius:var(--k-radius-sm);background:var(--k-accent);color:var(--k-bg);box-shadow:0 1px 3px rgba(0,0,0,calc(.35 * var(--k-lift)));
|
|
1191
1525
|
font:700 calc(var(--k-text-cap) * .85)/1 var(--k-mono);letter-spacing:.04em;pointer-events:none;opacity:0;transition:opacity .12s var(--k-ease)}
|
|
1192
|
-
.knobs-optkey-range{transform:translate(-100%,-50%)}
|
|
1193
|
-
.knobs-optkey[data-shown]{opacity:.85;transition-duration:.15s}
|
|
1526
|
+
.knobs-panel .knobs-optkey-range{transform:translate(-100%,-50%)}
|
|
1527
|
+
.knobs-panel .knobs-optkey[data-shown]{opacity:.85;transition-duration:.15s}
|
|
1194
1528
|
|
|
1195
1529
|
/* Custom actions */
|
|
1196
1530
|
.knobs-panel .knobs-custom-actions{display:grid;grid-template-columns:repeat(auto-fit,minmax(96px,1fr));gap:var(--k-gap);margin-top:var(--k-section-gap)}
|
|
@@ -1432,4 +1766,1054 @@ function injectStyles(doc) {
|
|
|
1432
1766
|
style.dataset.knobsStyle = "", style.textContent = content, doc.head.append(style);
|
|
1433
1767
|
}
|
|
1434
1768
|
|
|
1435
|
-
|
|
1769
|
+
// src/panel/sound.ts
|
|
1770
|
+
var click = (f, p, d = 0.005, q = 4, at = 0) => ({
|
|
1771
|
+
f,
|
|
1772
|
+
q,
|
|
1773
|
+
a: 0.0008,
|
|
1774
|
+
d,
|
|
1775
|
+
p,
|
|
1776
|
+
at
|
|
1777
|
+
}), thud = (f, p, d, at = 0) => ({
|
|
1778
|
+
filter: "lowpass",
|
|
1779
|
+
f,
|
|
1780
|
+
q: 0.7,
|
|
1781
|
+
a: 0.0015,
|
|
1782
|
+
d,
|
|
1783
|
+
p,
|
|
1784
|
+
at
|
|
1785
|
+
}), air = (f, to, d, p, a = 0.012, at = 0) => ({
|
|
1786
|
+
f,
|
|
1787
|
+
to,
|
|
1788
|
+
glide: a + d * 0.6,
|
|
1789
|
+
q: 1.4,
|
|
1790
|
+
a,
|
|
1791
|
+
d,
|
|
1792
|
+
p,
|
|
1793
|
+
at
|
|
1794
|
+
}), knock = (f, d, p, at = 0) => ({
|
|
1795
|
+
wave: "sine",
|
|
1796
|
+
f: f * 1.6,
|
|
1797
|
+
to: f,
|
|
1798
|
+
glide: 0.018,
|
|
1799
|
+
a: 0.001,
|
|
1800
|
+
d,
|
|
1801
|
+
p,
|
|
1802
|
+
at
|
|
1803
|
+
}), glass = (f, d, p, at = 0) => [
|
|
1804
|
+
[1, 1],
|
|
1805
|
+
[2.76, 0.28],
|
|
1806
|
+
[5.4, 0.08]
|
|
1807
|
+
].filter(([ratio]) => f * ratio <= 5000).map(([ratio, level]) => ({
|
|
1808
|
+
wave: "sine",
|
|
1809
|
+
f: f * ratio,
|
|
1810
|
+
a: 0.002,
|
|
1811
|
+
d: Math.max(0.028, d / ratio ** 0.75),
|
|
1812
|
+
p: p * level,
|
|
1813
|
+
at
|
|
1814
|
+
})), clamp3 = (v, lo, hi) => Math.min(hi, Math.max(lo, v));
|
|
1815
|
+
var cadence = (sinceMs) => clamp3((240 - sinceMs) / 170, 0, 1);
|
|
1816
|
+
function recipe(cue, detail = {}, speed = 0, random = Math.random) {
|
|
1817
|
+
let dir = Math.sign(detail.direction ?? 0), light = 1 - 0.45 * speed, vary = (amount) => 1 + (random() * 2 - 1) * amount;
|
|
1818
|
+
switch (cue) {
|
|
1819
|
+
case "step": {
|
|
1820
|
+
let k = 2 ** (clamp3(detail.position ?? 0.5, 0, 1) * 0.9 - 0.35), even = light / k ** 0.75, layers = [
|
|
1821
|
+
click(3000 * k, 0.5 * even, 0.004, 5),
|
|
1822
|
+
click(1300 * k, 0.22 * even, 0.01, 2.5)
|
|
1823
|
+
];
|
|
1824
|
+
if (speed < 0.6)
|
|
1825
|
+
layers.push({ wave: "sine", f: 1480 * k, a: 0.001, d: 0.05, p: 0.008 * light });
|
|
1826
|
+
if (detail.strong)
|
|
1827
|
+
layers.push(knock(240, 0.03, 0.05), click(2100, 0.3, 0.006, 3));
|
|
1828
|
+
return { layers, gain: 1.8 };
|
|
1829
|
+
}
|
|
1830
|
+
case "limit":
|
|
1831
|
+
return { layers: [thud(380, 0.9, 0.05), knock(115, 0.06, 0.07)], gain: 0.9 };
|
|
1832
|
+
case "toggle":
|
|
1833
|
+
return dir < 0 ? {
|
|
1834
|
+
layers: [
|
|
1835
|
+
click(2800, 0.14, 0.003, 3),
|
|
1836
|
+
click(1450, 0.5, 0.013, 2, 0.012),
|
|
1837
|
+
knock(250, 0.034, 0.06, 0.012)
|
|
1838
|
+
],
|
|
1839
|
+
gain: 1.2
|
|
1840
|
+
} : {
|
|
1841
|
+
layers: [
|
|
1842
|
+
click(3300, 0.14, 0.003, 3),
|
|
1843
|
+
click(1850, 0.5, 0.012, 2, 0.013),
|
|
1844
|
+
knock(330, 0.03, 0.06, 0.013)
|
|
1845
|
+
],
|
|
1846
|
+
gain: 1.2
|
|
1847
|
+
};
|
|
1848
|
+
case "select": {
|
|
1849
|
+
let k = 1 + 0.06 * dir;
|
|
1850
|
+
return {
|
|
1851
|
+
layers: [click(2700 * k, 0.4 * light, 0.006, 3), knock(430 * k, 0.02, 0.045 * light)],
|
|
1852
|
+
gain: 1.5
|
|
1853
|
+
};
|
|
1854
|
+
}
|
|
1855
|
+
case "preset":
|
|
1856
|
+
return {
|
|
1857
|
+
layers: [
|
|
1858
|
+
click(2400, 0.3, 0.005, 3),
|
|
1859
|
+
thud(1800, 0.08, 0.004),
|
|
1860
|
+
...glass(1046.5, 0.24, 0.016, 0.012),
|
|
1861
|
+
...glass(1568, 0.2, 0.01, 0.012)
|
|
1862
|
+
],
|
|
1863
|
+
gain: 2.5,
|
|
1864
|
+
room: 0.6
|
|
1865
|
+
};
|
|
1866
|
+
case "random":
|
|
1867
|
+
case "mutate": {
|
|
1868
|
+
let n = cue === "random" ? 5 : 2, layers = [], at = 0, gap = 0.044;
|
|
1869
|
+
for (let i = 0;i < n; i++) {
|
|
1870
|
+
let level = 0.8 ** i;
|
|
1871
|
+
layers.push(click(1500 + 1900 * random(), 0.36 * level, 0.006, 3, at), knock(380 + 320 * random(), 0.018, 0.04 * level, at)), at += gap * vary(0.15), gap *= 0.78;
|
|
1872
|
+
}
|
|
1873
|
+
return { layers, gain: 1.6 };
|
|
1874
|
+
}
|
|
1875
|
+
case "reset":
|
|
1876
|
+
return {
|
|
1877
|
+
layers: [
|
|
1878
|
+
{ f: 2600, to: 750, glide: 0.09, q: 1.3, a: 0.07, d: 0.03, p: 0.18 },
|
|
1879
|
+
click(2200, 0.3, 0.004, 3, 0.082),
|
|
1880
|
+
knock(300, 0.02, 0.04, 0.082)
|
|
1881
|
+
],
|
|
1882
|
+
gain: 1.1
|
|
1883
|
+
};
|
|
1884
|
+
case "copy":
|
|
1885
|
+
return {
|
|
1886
|
+
layers: [
|
|
1887
|
+
thud(2200, 0.06, 0.003),
|
|
1888
|
+
...glass(1174.66, 0.2, 0.014),
|
|
1889
|
+
{ wave: "sine", f: 1178.5, a: 0.002, d: 0.18, p: 0.006 },
|
|
1890
|
+
...glass(1567.98, 0.24, 0.014, 0.06)
|
|
1891
|
+
],
|
|
1892
|
+
gain: 2.6,
|
|
1893
|
+
room: 1
|
|
1894
|
+
};
|
|
1895
|
+
case "undo":
|
|
1896
|
+
case "redo": {
|
|
1897
|
+
let back = cue === "undo";
|
|
1898
|
+
return {
|
|
1899
|
+
layers: [
|
|
1900
|
+
air(back ? 1700 : 850, back ? 750 : 1800, 0.04, 0.14),
|
|
1901
|
+
knock(back ? 330 : 420, 0.02, 0.04, 0.028)
|
|
1902
|
+
],
|
|
1903
|
+
gain: 1.4
|
|
1904
|
+
};
|
|
1905
|
+
}
|
|
1906
|
+
case "open":
|
|
1907
|
+
return { layers: [click(2600, 0.16, 0.004, 3), air(700, 1600, 0.045, 0.14, 0.02)], gain: 1.7 };
|
|
1908
|
+
case "close":
|
|
1909
|
+
return {
|
|
1910
|
+
layers: [
|
|
1911
|
+
air(1500, 650, 0.04, 0.13, 0.015),
|
|
1912
|
+
knock(240, 0.028, 0.06, 0.03),
|
|
1913
|
+
click(1700, 0.2, 0.006, 3, 0.03)
|
|
1914
|
+
],
|
|
1915
|
+
gain: 1.1
|
|
1916
|
+
};
|
|
1917
|
+
case "hint":
|
|
1918
|
+
return {
|
|
1919
|
+
layers: [air(1200, 2400, 0.05, 0.06, 0.02), ...glass(1568, 0.14, 0.01, 0.012)],
|
|
1920
|
+
gain: 2.3,
|
|
1921
|
+
room: 0.8
|
|
1922
|
+
};
|
|
1923
|
+
case "type": {
|
|
1924
|
+
let k = vary(0.06);
|
|
1925
|
+
return {
|
|
1926
|
+
layers: [click(3600 * k, 0.16 * light, 0.003, 1), click(1600 * k, 0.3 * light, 0.008, 3)],
|
|
1927
|
+
gain: 1.4
|
|
1928
|
+
};
|
|
1929
|
+
}
|
|
1930
|
+
case "press":
|
|
1931
|
+
return {
|
|
1932
|
+
layers: [click(2650, 0.4, 0.004, 1.5), knock(380, 0.02, 0.05), thud(220, 0.25, 0.022)],
|
|
1933
|
+
gain: 2
|
|
1934
|
+
};
|
|
1935
|
+
case "capture":
|
|
1936
|
+
return {
|
|
1937
|
+
layers: [
|
|
1938
|
+
click(3000, 0.3, 0.003, 3),
|
|
1939
|
+
click(2400, 0.22, 0.004, 3, 0.035),
|
|
1940
|
+
...glass(1318.5, 0.12, 0.012, 0.04)
|
|
1941
|
+
],
|
|
1942
|
+
gain: 2.5,
|
|
1943
|
+
room: 0.5
|
|
1944
|
+
};
|
|
1945
|
+
}
|
|
1946
|
+
}
|
|
1947
|
+
var themed = (layers, theme) => theme === "mech" ? layers.map((l) => l.wave ? { ...l, p: l.p * 0.5, d: l.d * 0.5 } : { ...l, d: l.d * 0.75, q: (l.q ?? 1) * 1.3, p: l.p * 1.1 }) : [...layers], GAPS = {
|
|
1948
|
+
step: 24,
|
|
1949
|
+
limit: 140,
|
|
1950
|
+
toggle: 40,
|
|
1951
|
+
select: 28,
|
|
1952
|
+
preset: 80,
|
|
1953
|
+
random: 120,
|
|
1954
|
+
mutate: 120,
|
|
1955
|
+
reset: 120,
|
|
1956
|
+
copy: 200,
|
|
1957
|
+
undo: 45,
|
|
1958
|
+
redo: 45,
|
|
1959
|
+
open: 60,
|
|
1960
|
+
close: 60,
|
|
1961
|
+
hint: 120,
|
|
1962
|
+
type: 18,
|
|
1963
|
+
press: 50,
|
|
1964
|
+
capture: 120
|
|
1965
|
+
};
|
|
1966
|
+
function createLimiter(gaps = GAPS) {
|
|
1967
|
+
let last = /* @__PURE__ */ new Map;
|
|
1968
|
+
return (cue, now) => {
|
|
1969
|
+
let since = now - (last.get(cue) ?? Number.NEGATIVE_INFINITY);
|
|
1970
|
+
if (since < (gaps[cue] ?? 0))
|
|
1971
|
+
return null;
|
|
1972
|
+
return last.set(cue, now), cadence(since);
|
|
1973
|
+
};
|
|
1974
|
+
}
|
|
1975
|
+
var FRESH_MS = 150, DETENTS2 = 24;
|
|
1976
|
+
function createDetents() {
|
|
1977
|
+
let last = /* @__PURE__ */ new Map;
|
|
1978
|
+
return (key, before, after, detents, now) => {
|
|
1979
|
+
let since = now - (last.get(key) ?? Number.NEGATIVE_INFINITY);
|
|
1980
|
+
if (last.set(key, now), since > FRESH_MS)
|
|
1981
|
+
return !0;
|
|
1982
|
+
let n = Math.max(1, Math.round(detents));
|
|
1983
|
+
return Math.round(clamp3(before, 0, 1) * n) !== Math.round(clamp3(after, 0, 1) * n);
|
|
1984
|
+
};
|
|
1985
|
+
}
|
|
1986
|
+
var numbers = (v) => typeof v === "number" ? [v] : v && typeof v === "object" ? Object.values(v).filter((n) => typeof n === "number") : [];
|
|
1987
|
+
function changeCue(field, before, after) {
|
|
1988
|
+
if (field.kind === "toggle")
|
|
1989
|
+
return { cue: "toggle", detail: { direction: after ? 1 : -1 }, detents: 1, at: 1, was: 0 };
|
|
1990
|
+
if (field.kind === "choice") {
|
|
1991
|
+
let options = field.options ?? [];
|
|
1992
|
+
return { cue: "select", detail: { direction: options.indexOf(after) - options.indexOf(before) }, detents: 1, at: 1, was: 0 };
|
|
1993
|
+
}
|
|
1994
|
+
if (field.kind === "keybind")
|
|
1995
|
+
return after ? { cue: "capture", detail: {}, detents: 1, at: 1, was: 0 } : { cue: "select", detail: { direction: -1 }, detents: 1, at: 1, was: 0 };
|
|
1996
|
+
let { min, max, step } = field, a = numbers(before), b = numbers(after);
|
|
1997
|
+
if (typeof min !== "number" || typeof max !== "number" || !(max > min) || !b.length)
|
|
1998
|
+
return { cue: "step", detail: {}, detents: 1, at: 0.5, was: 0.5 };
|
|
1999
|
+
let i = b.findIndex((n, j) => n !== a[j]);
|
|
2000
|
+
if (i < 0)
|
|
2001
|
+
i = 0;
|
|
2002
|
+
let pos = (n) => clamp3(((n ?? min) - min) / (max - min), 0, 1), steps = typeof step === "number" && step > 0 ? (max - min) / step : Number.POSITIVE_INFINITY;
|
|
2003
|
+
return {
|
|
2004
|
+
cue: "step",
|
|
2005
|
+
detail: { position: pos(b[i]), strong: same(after, field.value) && !same(before, field.value) },
|
|
2006
|
+
detents: steps <= 10 ? steps : DETENTS2,
|
|
2007
|
+
at: pos(b[i]),
|
|
2008
|
+
was: pos(a[i])
|
|
2009
|
+
};
|
|
2010
|
+
}
|
|
2011
|
+
var same = (x, y) => {
|
|
2012
|
+
let a = numbers(x), b = numbers(y);
|
|
2013
|
+
return a.length > 0 && a.length === b.length && a.every((n, i) => n === b[i]);
|
|
2014
|
+
};
|
|
2015
|
+
function resolveSound(option) {
|
|
2016
|
+
if (!option)
|
|
2017
|
+
return null;
|
|
2018
|
+
if (option === !0)
|
|
2019
|
+
return {};
|
|
2020
|
+
return {
|
|
2021
|
+
...option.volume !== void 0 ? { volume: clamp3(Number(option.volume) || 0, 0, 1) } : {},
|
|
2022
|
+
...option.theme ? { theme: option.theme } : {}
|
|
2023
|
+
};
|
|
2024
|
+
}
|
|
2025
|
+
var MAX_VOICES = 10, PAD = 0.04, FADE = 0.015, LEVEL = 0.5, browserEnv = () => {
|
|
2026
|
+
let win = typeof window > "u" ? void 0 : window;
|
|
2027
|
+
return {
|
|
2028
|
+
AudioContext: win?.AudioContext ?? win?.webkitAudioContext,
|
|
2029
|
+
activated: () => win?.navigator?.userActivation?.hasBeenActive !== !1,
|
|
2030
|
+
now: () => performance.now()
|
|
2031
|
+
};
|
|
2032
|
+
};
|
|
2033
|
+
function createSound(env = browserEnv()) {
|
|
2034
|
+
let enabled = !0, volume = 1, theme = "glass", context = null, bus = null, voices = [], listeners = /* @__PURE__ */ new Set, limit = createLimiter(), now = env.now ?? (() => Date.now()), random = env.random ?? Math.random, notify = () => {
|
|
2035
|
+
for (let f of listeners)
|
|
2036
|
+
f();
|
|
2037
|
+
}, ensure = () => {
|
|
2038
|
+
if (context)
|
|
2039
|
+
return context;
|
|
2040
|
+
if (!env.AudioContext || env.activated?.() === !1)
|
|
2041
|
+
return null;
|
|
2042
|
+
try {
|
|
2043
|
+
context = new env.AudioContext;
|
|
2044
|
+
} catch {
|
|
2045
|
+
return null;
|
|
2046
|
+
}
|
|
2047
|
+
return context;
|
|
2048
|
+
}, wake = (ctx) => {
|
|
2049
|
+
if (ctx.state === "suspended" && enabled)
|
|
2050
|
+
try {
|
|
2051
|
+
ctx.resume().catch(() => {});
|
|
2052
|
+
} catch {}
|
|
2053
|
+
}, build = (ctx) => {
|
|
2054
|
+
if (bus)
|
|
2055
|
+
return bus;
|
|
2056
|
+
let input = ctx.createGain();
|
|
2057
|
+
input.gain.value = enabled ? volume : 0;
|
|
2058
|
+
let subsonic = ctx.createBiquadFilter();
|
|
2059
|
+
subsonic.type = "highpass", subsonic.frequency.value = 45;
|
|
2060
|
+
let squeeze = ctx.createDynamicsCompressor();
|
|
2061
|
+
squeeze.threshold.value = -22, squeeze.knee.value = 12, squeeze.ratio.value = 3, squeeze.attack.value = 0.002, squeeze.release.value = 0.12, input.connect(subsonic).connect(squeeze).connect(ctx.destination);
|
|
2062
|
+
let rate = ctx.sampleRate, noise = ctx.createBuffer(1, rate, rate), data = noise.getChannelData(0), s = 7;
|
|
2063
|
+
for (let i = 0;i < data.length; i++)
|
|
2064
|
+
s = s * 1664525 + 1013904223 >>> 0, data[i] = s / 2147483648 - 1;
|
|
2065
|
+
let len = Math.floor(rate * 0.2), impulse = ctx.createBuffer(2, len, rate);
|
|
2066
|
+
for (let c = 0;c < 2; c++) {
|
|
2067
|
+
let ch = impulse.getChannelData(c);
|
|
2068
|
+
for (let i = Math.floor(rate * 0.006);i < len; i++)
|
|
2069
|
+
ch[i] = data[i * (c + 3) % data.length] * Math.exp(-6.9 * i / len) / 9;
|
|
2070
|
+
}
|
|
2071
|
+
let verb = ctx.createConvolver();
|
|
2072
|
+
verb.normalize = !1, verb.buffer = impulse;
|
|
2073
|
+
let walls = ctx.createBiquadFilter();
|
|
2074
|
+
return walls.type = "lowpass", walls.frequency.value = 3800, walls.connect(verb).connect(input), bus = { input, room: walls, noise }, bus;
|
|
2075
|
+
}, render = (ctx, layers, level, room) => {
|
|
2076
|
+
let b = build(ctx), t0 = ctx.currentTime + 0.002;
|
|
2077
|
+
for (let i = voices.length - 1;i >= 0; i--)
|
|
2078
|
+
if (voices[i].end < t0)
|
|
2079
|
+
voices.splice(i, 1);
|
|
2080
|
+
while (voices.length >= MAX_VOICES) {
|
|
2081
|
+
let old = voices.shift();
|
|
2082
|
+
old.gain.gain.cancelScheduledValues(t0), old.gain.gain.setValueAtTime(old.gain.gain.value, t0), old.gain.gain.linearRampToValueAtTime(0, t0 + FADE);
|
|
2083
|
+
}
|
|
2084
|
+
let out = ctx.createGain();
|
|
2085
|
+
out.gain.value = level, out.connect(b.input);
|
|
2086
|
+
let send;
|
|
2087
|
+
if (room > 0)
|
|
2088
|
+
send = ctx.createGain(), send.gain.value = 0.1 * room, out.connect(send).connect(b.room);
|
|
2089
|
+
let end = t0;
|
|
2090
|
+
for (let l of layers) {
|
|
2091
|
+
let t = t0 + (l.at ?? 0), stop = t + l.a + l.d + PAD;
|
|
2092
|
+
end = Math.max(end, stop);
|
|
2093
|
+
let g = ctx.createGain();
|
|
2094
|
+
g.gain.setValueAtTime(0, t), g.gain.linearRampToValueAtTime(l.p, t + l.a), g.gain.setTargetAtTime(0, t + l.a, l.d / 6.9);
|
|
2095
|
+
let source, param;
|
|
2096
|
+
if (l.wave) {
|
|
2097
|
+
let osc = ctx.createOscillator();
|
|
2098
|
+
osc.type = l.wave, param = osc.frequency, osc.connect(g), source = osc;
|
|
2099
|
+
} else {
|
|
2100
|
+
let src = ctx.createBufferSource();
|
|
2101
|
+
src.buffer = b.noise, src.loop = !0;
|
|
2102
|
+
let filter = ctx.createBiquadFilter();
|
|
2103
|
+
if (filter.type = l.filter ?? "bandpass", l.q !== void 0)
|
|
2104
|
+
filter.Q.value = l.q;
|
|
2105
|
+
param = filter.frequency, src.connect(filter).connect(g), source = src;
|
|
2106
|
+
}
|
|
2107
|
+
if (param.setValueAtTime(l.f, t), l.to !== void 0)
|
|
2108
|
+
param.exponentialRampToValueAtTime(l.to, t + (l.glide ?? l.a + l.d));
|
|
2109
|
+
if (g.connect(out), l.wave)
|
|
2110
|
+
source.start(t);
|
|
2111
|
+
else
|
|
2112
|
+
source.start(t, random() * 0.9);
|
|
2113
|
+
source.stop(stop);
|
|
2114
|
+
}
|
|
2115
|
+
voices.push({ gain: out, end }), setTimeout(() => {
|
|
2116
|
+
out.disconnect(), send?.disconnect();
|
|
2117
|
+
}, (end - ctx.currentTime + 0.1) * 1000);
|
|
2118
|
+
};
|
|
2119
|
+
return {
|
|
2120
|
+
get enabled() {
|
|
2121
|
+
return enabled;
|
|
2122
|
+
},
|
|
2123
|
+
enable(on = !0) {
|
|
2124
|
+
if (on === enabled)
|
|
2125
|
+
return;
|
|
2126
|
+
if (enabled = on, context && bus) {
|
|
2127
|
+
let t = context.currentTime, g = bus.input.gain;
|
|
2128
|
+
g.cancelScheduledValues(t), g.setValueAtTime(g.value, t), g.linearRampToValueAtTime(on ? volume : 0, t + FADE * 2);
|
|
2129
|
+
}
|
|
2130
|
+
if (on && context)
|
|
2131
|
+
wake(context);
|
|
2132
|
+
notify();
|
|
2133
|
+
},
|
|
2134
|
+
get volume() {
|
|
2135
|
+
return volume;
|
|
2136
|
+
},
|
|
2137
|
+
setVolume(next) {
|
|
2138
|
+
let v = clamp3(Number(next) || 0, 0, 1);
|
|
2139
|
+
if (v === volume)
|
|
2140
|
+
return;
|
|
2141
|
+
if (volume = v, context && bus && enabled)
|
|
2142
|
+
bus.input.gain.setTargetAtTime(v, context.currentTime, 0.02);
|
|
2143
|
+
notify();
|
|
2144
|
+
},
|
|
2145
|
+
get theme() {
|
|
2146
|
+
return theme;
|
|
2147
|
+
},
|
|
2148
|
+
setTheme(next) {
|
|
2149
|
+
if (next !== "glass" && next !== "mech")
|
|
2150
|
+
return;
|
|
2151
|
+
if (next === theme)
|
|
2152
|
+
return;
|
|
2153
|
+
theme = next, notify();
|
|
2154
|
+
},
|
|
2155
|
+
play(cue, detail = {}) {
|
|
2156
|
+
if (!enabled || volume === 0)
|
|
2157
|
+
return;
|
|
2158
|
+
let level = LEVEL * clamp3(detail.volume ?? 1, 0, 1);
|
|
2159
|
+
if (level === 0)
|
|
2160
|
+
return;
|
|
2161
|
+
let made;
|
|
2162
|
+
if (typeof cue === "string") {
|
|
2163
|
+
let speed = limit(cue, now());
|
|
2164
|
+
if (speed === null)
|
|
2165
|
+
return;
|
|
2166
|
+
made = recipe(cue, detail, speed, random);
|
|
2167
|
+
} else
|
|
2168
|
+
made = { layers: cue };
|
|
2169
|
+
let ctx = ensure();
|
|
2170
|
+
if (!ctx)
|
|
2171
|
+
return;
|
|
2172
|
+
let gain = level * (made.gain ?? 1), layers = themed(made.layers, detail.theme ?? theme), room = (detail.theme ?? theme) === "mech" ? 0 : made.room ?? 0;
|
|
2173
|
+
if (ctx.state === "running")
|
|
2174
|
+
render(ctx, layers, gain, room);
|
|
2175
|
+
else
|
|
2176
|
+
try {
|
|
2177
|
+
ctx.resume().then(() => {
|
|
2178
|
+
if (enabled && ctx.state === "running")
|
|
2179
|
+
render(ctx, layers, gain, room);
|
|
2180
|
+
}).catch(() => {});
|
|
2181
|
+
} catch {}
|
|
2182
|
+
},
|
|
2183
|
+
output() {
|
|
2184
|
+
let ctx = ensure();
|
|
2185
|
+
if (!ctx)
|
|
2186
|
+
return null;
|
|
2187
|
+
return wake(ctx), { context: ctx, destination: build(ctx).input };
|
|
2188
|
+
},
|
|
2189
|
+
prime(doc = typeof document > "u" ? void 0 : document) {
|
|
2190
|
+
let win = doc?.defaultView;
|
|
2191
|
+
if (!win || context)
|
|
2192
|
+
return;
|
|
2193
|
+
let kinds = ["pointerdown", "keydown", "touchend"], unlock = () => {
|
|
2194
|
+
if (!enabled)
|
|
2195
|
+
return;
|
|
2196
|
+
let ctx = ensure();
|
|
2197
|
+
if (!ctx)
|
|
2198
|
+
return;
|
|
2199
|
+
build(ctx), wake(ctx);
|
|
2200
|
+
for (let k of kinds)
|
|
2201
|
+
win.removeEventListener(k, unlock, !0);
|
|
2202
|
+
};
|
|
2203
|
+
for (let k of kinds)
|
|
2204
|
+
win.addEventListener(k, unlock, !0);
|
|
2205
|
+
},
|
|
2206
|
+
subscribe(listener) {
|
|
2207
|
+
return listeners.add(listener), () => {
|
|
2208
|
+
listeners.delete(listener);
|
|
2209
|
+
};
|
|
2210
|
+
}
|
|
2211
|
+
};
|
|
2212
|
+
}
|
|
2213
|
+
var sound = /* @__PURE__ */ createSound(), panels = /* @__PURE__ */ new WeakMap, SOUND_PREF = "knobs:sound:v1";
|
|
2214
|
+
function soundPanel(root, options) {
|
|
2215
|
+
panels.set(root, options), sound.prime(root.ownerDocument);
|
|
2216
|
+
}
|
|
2217
|
+
var sounds = (from) => {
|
|
2218
|
+
let root = from?.closest?.(".knobs-panel");
|
|
2219
|
+
return Boolean(root && panels.has(root));
|
|
2220
|
+
}, detents = createDetents();
|
|
2221
|
+
function changed(from, key, field, before, after) {
|
|
2222
|
+
if (!sounds(from))
|
|
2223
|
+
return;
|
|
2224
|
+
let c = changeCue(field, before, after);
|
|
2225
|
+
if (!c)
|
|
2226
|
+
return;
|
|
2227
|
+
if (c.cue === "step" && !detents(key, c.was, c.at, c.detents, performance.now()))
|
|
2228
|
+
return;
|
|
2229
|
+
cue(from, c.cue, c.detail);
|
|
2230
|
+
}
|
|
2231
|
+
function cue(from, name, detail = {}) {
|
|
2232
|
+
let root = from?.closest?.(".knobs-panel"), options = root && panels.get(root);
|
|
2233
|
+
if (!options)
|
|
2234
|
+
return;
|
|
2235
|
+
sound.play(name, {
|
|
2236
|
+
...detail,
|
|
2237
|
+
volume: (options.volume ?? 1) * (detail.volume ?? 1),
|
|
2238
|
+
...options.theme && !detail.theme ? { theme: options.theme } : {}
|
|
2239
|
+
});
|
|
2240
|
+
}
|
|
2241
|
+
|
|
2242
|
+
// src/panel/hints.ts
|
|
2243
|
+
function extractWords(text) {
|
|
2244
|
+
return text.toLowerCase().match(/[a-z]+/g) ?? [];
|
|
2245
|
+
}
|
|
2246
|
+
function candidatePool(words) {
|
|
2247
|
+
let flat = words.join(""), pool = [], push = (s) => {
|
|
2248
|
+
if (s && /^[a-z]{1,3}$/.test(s) && !pool.includes(s))
|
|
2249
|
+
pool.push(s);
|
|
2250
|
+
};
|
|
2251
|
+
if (words.length >= 2) {
|
|
2252
|
+
let w0 = words[0], w1 = words[1], base2 = w0[0] + w1[0];
|
|
2253
|
+
push(base2);
|
|
2254
|
+
for (let i = 1;i < w1.length; i++)
|
|
2255
|
+
push(w0[0] + w1[i]);
|
|
2256
|
+
for (let i = 1;i < w0.length; i++)
|
|
2257
|
+
push(w0[i] + w1[0]);
|
|
2258
|
+
if (words.length >= 3)
|
|
2259
|
+
push(base2 + words[2][0]);
|
|
2260
|
+
if (w1.length >= 2)
|
|
2261
|
+
push(base2 + w1[1]);
|
|
2262
|
+
if (w0.length >= 2)
|
|
2263
|
+
push(base2 + w0[1]);
|
|
2264
|
+
} else if (flat.length > 0) {
|
|
2265
|
+
for (let i = 1;i < flat.length; i++)
|
|
2266
|
+
push(flat[0] + flat[i]);
|
|
2267
|
+
if (flat.length >= 3)
|
|
2268
|
+
push(flat.slice(0, 3));
|
|
2269
|
+
}
|
|
2270
|
+
let f0 = flat[0] || "x";
|
|
2271
|
+
for (let c = 97;c <= 122; c++)
|
|
2272
|
+
push(f0 + String.fromCharCode(c));
|
|
2273
|
+
return pool;
|
|
2274
|
+
}
|
|
2275
|
+
function assignHintCodes(items) {
|
|
2276
|
+
let n = items.length;
|
|
2277
|
+
if (n === 0)
|
|
2278
|
+
return [];
|
|
2279
|
+
let displayCounts = /* @__PURE__ */ new Map;
|
|
2280
|
+
for (let item of items) {
|
|
2281
|
+
let d = item.display.toLowerCase();
|
|
2282
|
+
displayCounts.set(d, (displayCounts.get(d) ?? 0) + 1);
|
|
2283
|
+
}
|
|
2284
|
+
let itemWords = items.map((item) => {
|
|
2285
|
+
let d = item.display.toLowerCase(), useKey = (displayCounts.get(d) ?? 0) > 1 && d !== item.key.toLowerCase(), w = extractWords(useKey ? item.key : item.display);
|
|
2286
|
+
return w.length > 0 ? w : extractWords(item.key);
|
|
2287
|
+
}), pools = itemWords.map((w) => candidatePool(w)), base2Of = pools.map((p) => p[0] ?? "x"), firstLetterCounts = /* @__PURE__ */ new Map, base2Counts = /* @__PURE__ */ new Map;
|
|
2288
|
+
for (let i = 0;i < n; i++) {
|
|
2289
|
+
let b2 = base2Of[i], f1 = b2[0];
|
|
2290
|
+
firstLetterCounts.set(f1, (firstLetterCounts.get(f1) ?? 0) + 1), base2Counts.set(b2, (base2Counts.get(b2) ?? 0) + 1);
|
|
2291
|
+
}
|
|
2292
|
+
let codes = Array(n).fill(""), used = /* @__PURE__ */ new Set;
|
|
2293
|
+
for (let i = 0;i < n; i++) {
|
|
2294
|
+
let words = itemWords[i], b2 = base2Of[i], f1 = b2[0];
|
|
2295
|
+
if (words.length === 1 && firstLetterCounts.get(f1) === 1 && !used.has(f1))
|
|
2296
|
+
codes[i] = f1, used.add(f1);
|
|
2297
|
+
else if (base2Counts.get(b2) === 1 && !used.has(b2))
|
|
2298
|
+
codes[i] = b2, used.add(b2);
|
|
2299
|
+
}
|
|
2300
|
+
for (let i = 0;i < n; i++) {
|
|
2301
|
+
if (codes[i])
|
|
2302
|
+
continue;
|
|
2303
|
+
let pool = pools[i], chosen = pool.find((cand) => cand.length === 2 && !used.has(cand) && !used.has(cand[0])) ?? pool.find((cand) => !used.has(cand) && !used.has(cand[0])) ?? `x${i}`;
|
|
2304
|
+
codes[i] = chosen, used.add(chosen);
|
|
2305
|
+
}
|
|
2306
|
+
return codes;
|
|
2307
|
+
}
|
|
2308
|
+
function resolveHintCodes(codes, rawQuery = "") {
|
|
2309
|
+
let q = rawQuery.toLowerCase().replace(/[^a-z]/g, ""), matchIndices = [], badges = codes.map((code, i) => {
|
|
2310
|
+
let match = q.length === 0 || code.startsWith(q);
|
|
2311
|
+
if (match)
|
|
2312
|
+
matchIndices.push(i);
|
|
2313
|
+
let typedLen = match ? Math.min(code.length, q.length) : 0;
|
|
2314
|
+
return {
|
|
2315
|
+
match,
|
|
2316
|
+
prefix: code,
|
|
2317
|
+
typed: code.slice(0, typedLen),
|
|
2318
|
+
rest: code.slice(typedLen)
|
|
2319
|
+
};
|
|
2320
|
+
}), uniqueMatchIndex = matchIndices.length === 1 && q.length > 0 ? matchIndices[0] : -1;
|
|
2321
|
+
return {
|
|
2322
|
+
badges,
|
|
2323
|
+
matchIndices,
|
|
2324
|
+
uniqueMatchIndex
|
|
2325
|
+
};
|
|
2326
|
+
}
|
|
2327
|
+
function createHintController(doc, root, wakeGpu) {
|
|
2328
|
+
let layer = doc.createElement("div");
|
|
2329
|
+
layer.className = "knobs-hint-layer", layer.setAttribute("aria-hidden", "true");
|
|
2330
|
+
let active = !1, local = !1, query = "", spots = [], targets = [], codes = [], badgeNodes = [];
|
|
2331
|
+
function stop() {
|
|
2332
|
+
if (!active)
|
|
2333
|
+
return;
|
|
2334
|
+
active = !1, local = !1, query = "", root.classList.remove("knobs-hinting");
|
|
2335
|
+
for (let t of spots)
|
|
2336
|
+
t.el.classList.remove("knobs-hint-match", "knobs-hint-miss");
|
|
2337
|
+
spots = [], targets = [], codes = [], badgeNodes = [], layer.replaceChildren(), layer.remove(), wakeGpu();
|
|
2338
|
+
}
|
|
2339
|
+
function paint() {
|
|
2340
|
+
if (!active)
|
|
2341
|
+
return;
|
|
2342
|
+
let resolution = resolveHintCodes(codes, query);
|
|
2343
|
+
if (local && resolution.uniqueMatchIndex >= 0) {
|
|
2344
|
+
let hit = targets[resolution.uniqueMatchIndex];
|
|
2345
|
+
stop(), hit.activate();
|
|
2346
|
+
return;
|
|
2347
|
+
}
|
|
2348
|
+
for (let i = 0;i < spots.length; i++) {
|
|
2349
|
+
let t = spots[i], b = resolution.badges[i], node = badgeNodes[i];
|
|
2350
|
+
if (t.el.classList.toggle("knobs-hint-match", b.match), t.el.classList.toggle("knobs-hint-miss", !b.match), !node)
|
|
2351
|
+
continue;
|
|
2352
|
+
if (node.tag.hidden = !b.match, b.match)
|
|
2353
|
+
node.typedSpan.textContent = b.typed, node.restSpan.textContent = b.rest;
|
|
2354
|
+
}
|
|
2355
|
+
wakeGpu();
|
|
2356
|
+
}
|
|
2357
|
+
function show(nextSpots, nextCodes) {
|
|
2358
|
+
if (stop(), nextSpots.length === 0)
|
|
2359
|
+
return;
|
|
2360
|
+
if (active = !0, query = "", spots = nextSpots, codes = nextCodes, root.classList.add("knobs-hinting"), !layer.isConnected)
|
|
2361
|
+
root.append(layer);
|
|
2362
|
+
cue(root, "hint");
|
|
2363
|
+
let rootRect = root.getBoundingClientRect();
|
|
2364
|
+
badgeNodes = spots.map((t) => {
|
|
2365
|
+
let tag = doc.createElement("kbd");
|
|
2366
|
+
tag.className = "knobs-hint";
|
|
2367
|
+
let typedSpan = doc.createElement("span");
|
|
2368
|
+
typedSpan.className = "knobs-hint-typed";
|
|
2369
|
+
let restSpan = doc.createElement("span");
|
|
2370
|
+
restSpan.className = "knobs-hint-rest", tag.append(typedSpan, restSpan);
|
|
2371
|
+
let rect = t.el.getBoundingClientRect();
|
|
2372
|
+
if (t.labelEl) {
|
|
2373
|
+
let lRect = t.labelEl.getBoundingClientRect(), left = lRect.right - rootRect.left - root.clientLeft + root.scrollLeft + 6, top = lRect.top - rootRect.top - root.clientTop + root.scrollTop + lRect.height * 0.5;
|
|
2374
|
+
tag.style.left = `${Math.round(left)}px`, tag.style.top = `${Math.round(top)}px`;
|
|
2375
|
+
} else {
|
|
2376
|
+
let left = rect.left - rootRect.left - root.clientLeft + root.scrollLeft + 1, top = rect.top - rootRect.top - root.clientTop + root.scrollTop + 1;
|
|
2377
|
+
tag.style.left = `${Math.round(left)}px`, tag.style.top = `${Math.round(top)}px`;
|
|
2378
|
+
}
|
|
2379
|
+
return layer.append(tag), { tag, typedSpan, restSpan };
|
|
2380
|
+
}), paint();
|
|
2381
|
+
}
|
|
2382
|
+
function start(nextTargets) {
|
|
2383
|
+
if (show(nextTargets, assignHintCodes(nextTargets)), !active)
|
|
2384
|
+
return;
|
|
2385
|
+
local = !0, targets = nextTargets;
|
|
2386
|
+
}
|
|
2387
|
+
function narrow(next) {
|
|
2388
|
+
if (next.length > query.length)
|
|
2389
|
+
cue(root, "type");
|
|
2390
|
+
query = next, paint();
|
|
2391
|
+
}
|
|
2392
|
+
function handleKey(event) {
|
|
2393
|
+
if (!active || !local)
|
|
2394
|
+
return !1;
|
|
2395
|
+
if (event.preventDefault(), event.stopPropagation(), event.key === "Escape")
|
|
2396
|
+
stop();
|
|
2397
|
+
else if (event.key === "Backspace")
|
|
2398
|
+
query = query.slice(0, -1), paint();
|
|
2399
|
+
else if (event.key === "Enter") {
|
|
2400
|
+
let resolution = resolveHintCodes(codes, query), first = targets[resolution.matchIndices[0] ?? -1];
|
|
2401
|
+
stop(), first?.activate();
|
|
2402
|
+
} else if (/^[a-zA-Z]$/.test(event.key))
|
|
2403
|
+
cue(root, "type"), query += event.key.toLowerCase(), paint();
|
|
2404
|
+
return !0;
|
|
2405
|
+
}
|
|
2406
|
+
return {
|
|
2407
|
+
get isActive() {
|
|
2408
|
+
return active;
|
|
2409
|
+
},
|
|
2410
|
+
start,
|
|
2411
|
+
show,
|
|
2412
|
+
narrow,
|
|
2413
|
+
stop,
|
|
2414
|
+
handleKey
|
|
2415
|
+
};
|
|
2416
|
+
}
|
|
2417
|
+
|
|
2418
|
+
// src/panel/page-hints.ts
|
|
2419
|
+
var HINT_LETTERS = "asdfghjkleruiowtyqpcvnmxzb", HOME_ROW = 9;
|
|
2420
|
+
function assignPageCodes(counts) {
|
|
2421
|
+
let n = HINT_LETTERS.length, result = counts.map((c) => Array(c).fill("")), need = counts.map((c) => c > 0 ? Math.ceil(c / n) : 0), used = need.reduce((a, b) => a + b, 0);
|
|
2422
|
+
if (used <= n) {
|
|
2423
|
+
while (used < n) {
|
|
2424
|
+
let best = -1, most = HOME_ROW;
|
|
2425
|
+
if (counts.forEach((c, i) => {
|
|
2426
|
+
let per = need[i] ? Math.ceil(c / need[i]) : 0;
|
|
2427
|
+
if (per > most)
|
|
2428
|
+
best = i, most = per;
|
|
2429
|
+
}), best < 0)
|
|
2430
|
+
break;
|
|
2431
|
+
need[best]++, used++;
|
|
2432
|
+
}
|
|
2433
|
+
let letter = 0;
|
|
2434
|
+
return counts.forEach((c, i) => {
|
|
2435
|
+
let k = need[i], at = 0;
|
|
2436
|
+
for (let chunk = 0;chunk < k; chunk++) {
|
|
2437
|
+
let size = Math.floor(c / k) + (chunk < c % k ? 1 : 0), first = HINT_LETTERS[letter++];
|
|
2438
|
+
for (let j = 0;j < size; j++)
|
|
2439
|
+
result[i][at + j] = size === 1 ? first : first + HINT_LETTERS[j];
|
|
2440
|
+
at += size;
|
|
2441
|
+
}
|
|
2442
|
+
}), result;
|
|
2443
|
+
}
|
|
2444
|
+
let slot = 0;
|
|
2445
|
+
return counts.forEach((c, i) => {
|
|
2446
|
+
for (let j = 0;j < c; j++, slot++) {
|
|
2447
|
+
let first = Math.floor(slot / n);
|
|
2448
|
+
if (first < n)
|
|
2449
|
+
result[i][j] = HINT_LETTERS[first] + HINT_LETTERS[slot % n];
|
|
2450
|
+
}
|
|
2451
|
+
}), result;
|
|
2452
|
+
}
|
|
2453
|
+
var COLUMN = 200;
|
|
2454
|
+
function orderHintGroups(groups) {
|
|
2455
|
+
return groups.map((g, i) => ({ g, i })).sort((a, b) => a.g.rank - b.g.rank || Math.floor(a.g.left / COLUMN) - Math.floor(b.g.left / COLUMN) || a.g.top - b.g.top || a.g.left - b.g.left || a.i - b.i).map((x) => x.i);
|
|
2456
|
+
}
|
|
2457
|
+
var PAGE_HINT_KEY = "s", KEY = Symbol.for("knobs.pageHints.v1");
|
|
2458
|
+
function pageHints(doc = document) {
|
|
2459
|
+
let holder = doc, registry = holder[KEY] ?? createRegistry(doc);
|
|
2460
|
+
return holder[KEY] = registry, registry;
|
|
2461
|
+
}
|
|
2462
|
+
var isTyping = isTypingTarget, PRESSABLE = "button,a[href],summary,[role=button],[role=tab],[role=radio],[role=switch],[role=checkbox],[role=menuitem],[role=option]";
|
|
2463
|
+
function activateElement(el) {
|
|
2464
|
+
let pressable = el.matches(PRESSABLE) || el.tabIndex < 0 && !el.matches("input,select,textarea");
|
|
2465
|
+
if (el.tabIndex >= 0 || el.matches("input,select,textarea"))
|
|
2466
|
+
el.focus({ preventScroll: !0, focusVisible: !0 });
|
|
2467
|
+
if (pressable)
|
|
2468
|
+
el.click();
|
|
2469
|
+
}
|
|
2470
|
+
var labelOf = (el) => (el.dataset.hint || el.getAttribute("aria-label") || el.textContent || "").trim();
|
|
2471
|
+
function createRegistry(doc) {
|
|
2472
|
+
let win = doc.defaultView, sources = [], session = null, listening = !1, onDocKey = (e) => {
|
|
2473
|
+
if (session || e.defaultPrevented || e.repeat || e.isComposing)
|
|
2474
|
+
return;
|
|
2475
|
+
if (e.metaKey || e.ctrlKey || e.altKey || e.key !== PAGE_HINT_KEY)
|
|
2476
|
+
return;
|
|
2477
|
+
if (isTyping(e.target) || !wide())
|
|
2478
|
+
return;
|
|
2479
|
+
if (registry.start())
|
|
2480
|
+
e.preventDefault();
|
|
2481
|
+
}, wide = () => listening && (sources.length > 1 || [...doc.querySelectorAll("[data-hint]")].some((el) => !el.closest(".knobs-panel"))), relisten = () => {
|
|
2482
|
+
let next = sources.some((s) => s.listen);
|
|
2483
|
+
if (next === listening)
|
|
2484
|
+
return;
|
|
2485
|
+
if (listening = next, next)
|
|
2486
|
+
doc.addEventListener("keydown", onDocKey);
|
|
2487
|
+
else
|
|
2488
|
+
doc.removeEventListener("keydown", onDocKey);
|
|
2489
|
+
}, shown = (el) => {
|
|
2490
|
+
if (!el.isConnected || el.disabled)
|
|
2491
|
+
return !1;
|
|
2492
|
+
if (el.closest("[hidden],[inert],[data-leaving]"))
|
|
2493
|
+
return !1;
|
|
2494
|
+
if (el.getClientRects().length === 0)
|
|
2495
|
+
return !1;
|
|
2496
|
+
let check = el.checkVisibility;
|
|
2497
|
+
return check ? check.call(el, { visibilityProperty: !0 }) : !0;
|
|
2498
|
+
}, collect = () => {
|
|
2499
|
+
let seen = /* @__PURE__ */ new Set, take = (item) => {
|
|
2500
|
+
let t = typeof item.nodeType === "number" ? { el: item } : item, el = t.el;
|
|
2501
|
+
if (seen.has(el) || !shown(el))
|
|
2502
|
+
return null;
|
|
2503
|
+
let label = (t.label ?? labelOf(el)).trim();
|
|
2504
|
+
return seen.add(el), {
|
|
2505
|
+
el,
|
|
2506
|
+
label: label || "x",
|
|
2507
|
+
...t.labelEl ? { labelEl: t.labelEl } : {},
|
|
2508
|
+
activate: t.activate ?? (() => activateElement(el))
|
|
2509
|
+
};
|
|
2510
|
+
}, groups = [];
|
|
2511
|
+
for (let source of [...sources].sort((a, b) => a.rank - b.rank)) {
|
|
2512
|
+
let targets = [];
|
|
2513
|
+
for (let item of source.targets()) {
|
|
2514
|
+
let t = take(item);
|
|
2515
|
+
if (t)
|
|
2516
|
+
targets.push(t);
|
|
2517
|
+
}
|
|
2518
|
+
if (targets.length)
|
|
2519
|
+
groups.push({ source, targets });
|
|
2520
|
+
}
|
|
2521
|
+
let byGroup = /* @__PURE__ */ new Map;
|
|
2522
|
+
for (let el of doc.querySelectorAll("[data-hint]")) {
|
|
2523
|
+
if (el.closest(".knobs-panel"))
|
|
2524
|
+
continue;
|
|
2525
|
+
let t = take(el);
|
|
2526
|
+
if (!t)
|
|
2527
|
+
continue;
|
|
2528
|
+
let key = el.closest("[data-hint-group]"), list = byGroup.get(key);
|
|
2529
|
+
if (list)
|
|
2530
|
+
list.push(t);
|
|
2531
|
+
else
|
|
2532
|
+
byGroup.set(key, [t]);
|
|
2533
|
+
}
|
|
2534
|
+
for (let targets of byGroup.values())
|
|
2535
|
+
groups.push({ targets });
|
|
2536
|
+
return groups;
|
|
2537
|
+
}, inView = (el, clips) => {
|
|
2538
|
+
if (!win)
|
|
2539
|
+
return !0;
|
|
2540
|
+
let r = el.getBoundingClientRect(), y = r.top + Math.min(r.height / 2, 10), top = 0, left = 0, { innerWidth: right, innerHeight: bottom } = win;
|
|
2541
|
+
for (let p = el.parentElement;p && p !== doc.body; p = p.parentElement) {
|
|
2542
|
+
let clip = clips.get(p);
|
|
2543
|
+
if (clip === void 0) {
|
|
2544
|
+
let cs = win.getComputedStyle(p);
|
|
2545
|
+
clip = cs.overflowX !== "visible" || cs.overflowY !== "visible" ? p.getBoundingClientRect() : null, clips.set(p, clip);
|
|
2546
|
+
}
|
|
2547
|
+
if (!clip)
|
|
2548
|
+
continue;
|
|
2549
|
+
top = Math.max(top, clip.top), left = Math.max(left, clip.left), right = Math.min(right, clip.right), bottom = Math.min(bottom, clip.bottom);
|
|
2550
|
+
}
|
|
2551
|
+
return y >= top && y <= bottom && r.right > left + 2 && r.left < right - 2;
|
|
2552
|
+
}, registry = {
|
|
2553
|
+
register(root, targets, options = {}) {
|
|
2554
|
+
let source = {
|
|
2555
|
+
root,
|
|
2556
|
+
targets,
|
|
2557
|
+
listen: options.listen !== !1,
|
|
2558
|
+
rank: options.rank ?? 2,
|
|
2559
|
+
...options.paint ? { paint: options.paint } : {}
|
|
2560
|
+
};
|
|
2561
|
+
return sources.push(source), relisten(), () => {
|
|
2562
|
+
let i = sources.indexOf(source);
|
|
2563
|
+
if (i < 0)
|
|
2564
|
+
return;
|
|
2565
|
+
if (sources.splice(i, 1), session?.has(source))
|
|
2566
|
+
registry.stop();
|
|
2567
|
+
relisten();
|
|
2568
|
+
};
|
|
2569
|
+
},
|
|
2570
|
+
start() {
|
|
2571
|
+
registry.stop();
|
|
2572
|
+
let groups = collect();
|
|
2573
|
+
if (groups.length === 0)
|
|
2574
|
+
return !1;
|
|
2575
|
+
let codes;
|
|
2576
|
+
if (groups.length === 1) {
|
|
2577
|
+
let only = groups[0].targets;
|
|
2578
|
+
codes = [assignHintCodes(only.map((t) => ({ key: t.label, display: t.label })))];
|
|
2579
|
+
} else {
|
|
2580
|
+
let clips = /* @__PURE__ */ new Map;
|
|
2581
|
+
if (groups = groups.map((g) => ({ ...g, targets: g.targets.filter((t) => inView(t.el, clips)) })).filter((g) => g.targets.length > 0), groups.length === 0)
|
|
2582
|
+
return !1;
|
|
2583
|
+
groups = orderHintGroups(groups.map((g) => {
|
|
2584
|
+
let left = 1 / 0, top = 1 / 0;
|
|
2585
|
+
for (let t of g.targets) {
|
|
2586
|
+
let r = t.el.getBoundingClientRect();
|
|
2587
|
+
left = Math.min(left, r.left), top = Math.min(top, r.top);
|
|
2588
|
+
}
|
|
2589
|
+
return { rank: g.source?.rank ?? 2, left, top };
|
|
2590
|
+
})).map((i) => groups[i]), codes = groups.length === 1 ? [assignHintCodes(groups[0].targets.map((t) => ({ key: t.label, display: t.label })))] : assignPageCodes(groups.map((g) => g.targets.length));
|
|
2591
|
+
}
|
|
2592
|
+
return session = createSession(doc, groups, codes, () => {
|
|
2593
|
+
session = null;
|
|
2594
|
+
}), !0;
|
|
2595
|
+
},
|
|
2596
|
+
stop() {
|
|
2597
|
+
session?.stop(), session = null;
|
|
2598
|
+
},
|
|
2599
|
+
get active() {
|
|
2600
|
+
return session !== null;
|
|
2601
|
+
},
|
|
2602
|
+
get listening() {
|
|
2603
|
+
return wide();
|
|
2604
|
+
}
|
|
2605
|
+
};
|
|
2606
|
+
return registry;
|
|
2607
|
+
}
|
|
2608
|
+
function createSession(doc, groups, codes, ended) {
|
|
2609
|
+
let win = doc.defaultView, entries = [], painters = [], shared = { spots: [], codes: [] };
|
|
2610
|
+
if (groups.forEach((g, i) => {
|
|
2611
|
+
let spots = [], mine = [];
|
|
2612
|
+
if (g.targets.forEach((t, j) => {
|
|
2613
|
+
let code = codes[i][j];
|
|
2614
|
+
if (!code)
|
|
2615
|
+
return;
|
|
2616
|
+
entries.push({ target: t, code }), spots.push(t), mine.push(code);
|
|
2617
|
+
}), !spots.length)
|
|
2618
|
+
return;
|
|
2619
|
+
if (g.source?.paint)
|
|
2620
|
+
painters.push(g.source.paint), g.source.paint.show(spots, mine);
|
|
2621
|
+
else
|
|
2622
|
+
shared.spots.push(...spots), shared.codes.push(...mine);
|
|
2623
|
+
}), shared.spots.length) {
|
|
2624
|
+
let layer = layerPainter(doc, groups);
|
|
2625
|
+
painters.push(layer), layer.show(shared.spots, shared.codes);
|
|
2626
|
+
}
|
|
2627
|
+
let allCodes = entries.map((e) => e.code), query = "", over = !1, stop = () => {
|
|
2628
|
+
if (over)
|
|
2629
|
+
return;
|
|
2630
|
+
over = !0;
|
|
2631
|
+
for (let p of painters)
|
|
2632
|
+
p.clear();
|
|
2633
|
+
win?.removeEventListener("keydown", onKey, !0), doc.removeEventListener("scroll", onScroll, !0), doc.removeEventListener("pointerdown", onPointer, !0), win?.removeEventListener("resize", stop), win?.removeEventListener("blur", stop), ended();
|
|
2634
|
+
}, finish = (i) => {
|
|
2635
|
+
let hit = i === void 0 ? void 0 : entries[i];
|
|
2636
|
+
stop(), hit?.target.activate();
|
|
2637
|
+
}, narrow = () => {
|
|
2638
|
+
let r = resolveHintCodes(allCodes, query);
|
|
2639
|
+
if (r.uniqueMatchIndex >= 0)
|
|
2640
|
+
return finish(r.uniqueMatchIndex);
|
|
2641
|
+
for (let p of painters)
|
|
2642
|
+
p.narrow(query);
|
|
2643
|
+
}, onKey = (e) => {
|
|
2644
|
+
if (e.isComposing || ["Shift", "Meta", "Control", "Alt", "CapsLock"].includes(e.key))
|
|
2645
|
+
return;
|
|
2646
|
+
if (e.preventDefault(), e.stopImmediatePropagation(), e.metaKey || e.ctrlKey)
|
|
2647
|
+
return stop();
|
|
2648
|
+
if (e.key === "Escape")
|
|
2649
|
+
stop();
|
|
2650
|
+
else if (e.key === "Backspace")
|
|
2651
|
+
query = query.slice(0, -1), narrow();
|
|
2652
|
+
else if (e.key === "Enter")
|
|
2653
|
+
finish(resolveHintCodes(allCodes, query).matchIndices[0]);
|
|
2654
|
+
else if (/^[a-zA-Z]$/.test(e.key))
|
|
2655
|
+
query += e.key.toLowerCase(), narrow();
|
|
2656
|
+
}, scrolled = /* @__PURE__ */ new Map, offset = (t) => t === doc ? (win?.scrollY ?? 0) + (win?.scrollX ?? 0) : t.scrollTop + t.scrollLeft, onScroll = (e) => {
|
|
2657
|
+
let t = e.target;
|
|
2658
|
+
if (!t)
|
|
2659
|
+
return;
|
|
2660
|
+
let at = offset(t), from = scrolled.get(t);
|
|
2661
|
+
if (from === void 0)
|
|
2662
|
+
scrolled.set(t, at);
|
|
2663
|
+
else if (Math.abs(at - from) > 24)
|
|
2664
|
+
stop();
|
|
2665
|
+
}, onPointer = () => stop();
|
|
2666
|
+
return win?.addEventListener("keydown", onKey, !0), doc.addEventListener("scroll", onScroll, !0), doc.addEventListener("pointerdown", onPointer, !0), win?.addEventListener("resize", stop), win?.addEventListener("blur", stop), {
|
|
2667
|
+
has: (source) => groups.some((g) => g.source === source),
|
|
2668
|
+
stop
|
|
2669
|
+
};
|
|
2670
|
+
}
|
|
2671
|
+
var STYLE_ID = "knobs-page-hints-css", CSS = `
|
|
2672
|
+
.knobs-page-hints{position:fixed;left:0;top:0;width:0;height:0;pointer-events:none;z-index:2147483000}
|
|
2673
|
+
.knobs-page-hint{position:fixed;transform:translateY(-50%);display:inline-flex;align-items:center;height:15px;padding:0 4px;border-radius:var(--kh-radius);background:var(--kh-accent);color:var(--kh-bg);
|
|
2674
|
+
box-shadow:0 1px 3px rgba(0,0,0,calc(.35 * var(--kh-lift)));font:700 var(--kh-size)/1 var(--kh-mono);letter-spacing:.04em;text-transform:uppercase;white-space:nowrap;pointer-events:none;
|
|
2675
|
+
animation:knobs-page-hint-in .1s var(--kh-ease)}
|
|
2676
|
+
.knobs-page-hint[hidden]{display:none}
|
|
2677
|
+
.knobs-page-hint>.knobs-hint-typed{opacity:var(--kh-typed)}
|
|
2678
|
+
[data-knobs-hint-miss]{opacity:.32;transition:opacity .1s}
|
|
2679
|
+
@keyframes knobs-page-hint-in{from{opacity:0;transform:translateY(calc(4px - 50%)) scale(.985)}to{opacity:1;transform:translateY(-50%)}}
|
|
2680
|
+
@media(prefers-reduced-motion:reduce){@keyframes knobs-page-hint-in{from{opacity:0}to{opacity:1}}}
|
|
2681
|
+
`;
|
|
2682
|
+
function hintTokens(doc, groups) {
|
|
2683
|
+
let win = doc.defaultView, read = (el, names) => {
|
|
2684
|
+
if (!el || !win)
|
|
2685
|
+
return "";
|
|
2686
|
+
let cs = win.getComputedStyle(el);
|
|
2687
|
+
for (let name of names) {
|
|
2688
|
+
let v = cs.getPropertyValue(name).trim();
|
|
2689
|
+
if (v)
|
|
2690
|
+
return v;
|
|
2691
|
+
}
|
|
2692
|
+
return "";
|
|
2693
|
+
}, panel = doc.querySelector(".knobs-panel"), piece = groups.find((g) => g.source && !g.source.paint)?.source?.root, pick = (k, fallback) => read(panel, [`--k-${k}`]) || read(piece, [`--ku-${k}`, `--k-${k}`]) || fallback;
|
|
2694
|
+
return {
|
|
2695
|
+
"--kh-accent": pick("accent", "#fff"),
|
|
2696
|
+
"--kh-bg": pick("bg", "#1c1c1e"),
|
|
2697
|
+
"--kh-mono": pick("mono", "ui-monospace,Menlo,monospace"),
|
|
2698
|
+
"--kh-radius": read(panel, ["--k-radius-sm"]) || "4px",
|
|
2699
|
+
"--kh-size": read(panel, ["--k-text-cap"]) || "10px",
|
|
2700
|
+
"--kh-typed": read(panel, ["--k-hint-typed-opacity"]) || ".42",
|
|
2701
|
+
"--kh-lift": read(panel, ["--k-lift"]) || "1",
|
|
2702
|
+
"--kh-ease": read(panel, ["--k-ease"]) || "cubic-bezier(.2,.8,.2,1)"
|
|
2703
|
+
};
|
|
2704
|
+
}
|
|
2705
|
+
function layerPainter(doc, groups) {
|
|
2706
|
+
let layer = doc.createElement("div");
|
|
2707
|
+
layer.className = "knobs-page-hints", layer.setAttribute("aria-hidden", "true");
|
|
2708
|
+
let spots = [], codes = [], nodes = [];
|
|
2709
|
+
return {
|
|
2710
|
+
show(next, nextCodes) {
|
|
2711
|
+
if (!doc.getElementById(STYLE_ID)) {
|
|
2712
|
+
let style = doc.createElement("style");
|
|
2713
|
+
style.id = STYLE_ID, style.textContent = CSS, doc.head.append(style);
|
|
2714
|
+
}
|
|
2715
|
+
spots = next, codes = nextCodes;
|
|
2716
|
+
for (let [k, v] of Object.entries(hintTokens(doc, groups)))
|
|
2717
|
+
layer.style.setProperty(k, v);
|
|
2718
|
+
nodes = spots.map((t, i) => {
|
|
2719
|
+
let tag = doc.createElement("kbd");
|
|
2720
|
+
tag.className = "knobs-page-hint";
|
|
2721
|
+
let typed = doc.createElement("span");
|
|
2722
|
+
typed.className = "knobs-hint-typed";
|
|
2723
|
+
let rest = doc.createElement("span");
|
|
2724
|
+
rest.textContent = codes[i] ?? "", tag.append(typed, rest);
|
|
2725
|
+
let r = (t.labelEl ?? t.el).getBoundingClientRect(), left = t.labelEl ? r.right + 6 : r.left + 1;
|
|
2726
|
+
return tag.style.left = `${Math.round(left)}px`, tag.style.top = `${Math.round(r.top + r.height / 2)}px`, layer.append(tag), { tag, typed, rest };
|
|
2727
|
+
}), doc.body.append(layer);
|
|
2728
|
+
},
|
|
2729
|
+
narrow(query) {
|
|
2730
|
+
let r = resolveHintCodes(codes, query);
|
|
2731
|
+
nodes.forEach((n, i) => {
|
|
2732
|
+
let b = r.badges[i];
|
|
2733
|
+
n.tag.hidden = !b.match, n.typed.textContent = b.typed, n.rest.textContent = b.rest, spots[i].el.toggleAttribute("data-knobs-hint-miss", !b.match);
|
|
2734
|
+
});
|
|
2735
|
+
},
|
|
2736
|
+
clear() {
|
|
2737
|
+
for (let t of spots)
|
|
2738
|
+
t.el.removeAttribute("data-knobs-hint-miss");
|
|
2739
|
+
layer.remove(), layer.replaceChildren(), spots = [], nodes = [];
|
|
2740
|
+
}
|
|
2741
|
+
};
|
|
2742
|
+
}
|
|
2743
|
+
|
|
2744
|
+
// src/panel/toggle.ts
|
|
2745
|
+
function isToggleKey(event, key) {
|
|
2746
|
+
if (!key || event.defaultPrevented || event.repeat || event.isComposing)
|
|
2747
|
+
return !1;
|
|
2748
|
+
if (event.ctrlKey || event.metaKey || event.altKey)
|
|
2749
|
+
return !1;
|
|
2750
|
+
if (isTypingTarget(event.target))
|
|
2751
|
+
return !1;
|
|
2752
|
+
if (event.key === key)
|
|
2753
|
+
return !0;
|
|
2754
|
+
return key === "`" && event.code === "Backquote" && !event.shiftKey;
|
|
2755
|
+
}
|
|
2756
|
+
var KEY2 = Symbol.for("knobs.toggle.v1"), memoryKey = (id) => storageKey(id, "shown");
|
|
2757
|
+
function toggleLayer(doc) {
|
|
2758
|
+
let holder = doc;
|
|
2759
|
+
return holder[KEY2] ??= createLayer(doc);
|
|
2760
|
+
}
|
|
2761
|
+
function createLayer(doc) {
|
|
2762
|
+
let win = doc.defaultView, surfaces = /* @__PURE__ */ new Set, state = /* @__PURE__ */ new Map, pressed = /* @__PURE__ */ new Map, lastFocus, isShown = (s) => {
|
|
2763
|
+
let on = state.get(s.id) ?? pressed.get(s.key);
|
|
2764
|
+
if (on === void 0)
|
|
2765
|
+
try {
|
|
2766
|
+
on = s.storage()?.getItem(memoryKey(s.id)) !== "0";
|
|
2767
|
+
} catch {
|
|
2768
|
+
on = !0;
|
|
2769
|
+
}
|
|
2770
|
+
return state.set(s.id, on), on;
|
|
2771
|
+
}, toggle = (key) => {
|
|
2772
|
+
let mine = [...surfaces].filter((s) => s.key === key);
|
|
2773
|
+
if (!mine.length)
|
|
2774
|
+
return !1;
|
|
2775
|
+
let next = !mine.some(isShown);
|
|
2776
|
+
if (pressed.set(key, next), !next) {
|
|
2777
|
+
let active = doc.activeElement;
|
|
2778
|
+
if (active && mine.some((s) => s.root.contains(active)))
|
|
2779
|
+
lastFocus = active, active.blur();
|
|
2780
|
+
pageHints(doc).stop();
|
|
2781
|
+
}
|
|
2782
|
+
for (let s of mine) {
|
|
2783
|
+
state.set(s.id, next);
|
|
2784
|
+
try {
|
|
2785
|
+
s.storage()?.setItem(memoryKey(s.id), next ? "1" : "0");
|
|
2786
|
+
} catch {}
|
|
2787
|
+
}
|
|
2788
|
+
for (let s of mine)
|
|
2789
|
+
s.show(next, !0);
|
|
2790
|
+
if (next) {
|
|
2791
|
+
let active = doc.activeElement;
|
|
2792
|
+
if (lastFocus?.isConnected && (!active || active === doc.body))
|
|
2793
|
+
lastFocus.focus({ preventScroll: !0 });
|
|
2794
|
+
lastFocus = void 0;
|
|
2795
|
+
}
|
|
2796
|
+
return !0;
|
|
2797
|
+
}, onKey = (event) => {
|
|
2798
|
+
let keys = new Set([...surfaces].map((s) => s.key));
|
|
2799
|
+
for (let key of keys)
|
|
2800
|
+
if (isToggleKey(event, key))
|
|
2801
|
+
return void toggle(key);
|
|
2802
|
+
};
|
|
2803
|
+
return {
|
|
2804
|
+
register(surface) {
|
|
2805
|
+
if (!surfaces.size)
|
|
2806
|
+
win.addEventListener("keydown", onKey, !0);
|
|
2807
|
+
return surfaces.add(surface), {
|
|
2808
|
+
shown: isShown(surface),
|
|
2809
|
+
dispose() {
|
|
2810
|
+
if (surfaces.delete(surface), !surfaces.size)
|
|
2811
|
+
win.removeEventListener("keydown", onKey, !0);
|
|
2812
|
+
}
|
|
2813
|
+
};
|
|
2814
|
+
},
|
|
2815
|
+
toggle
|
|
2816
|
+
};
|
|
2817
|
+
}
|
|
2818
|
+
|
|
2819
|
+
export { CHECK_POINTS, CHECK_PATH, iconPath, iconSvg, readSession, writeSession, rolling_css_default, presetCss, FILL, LEAD, trailScale, Edges, POP_MS, popScale, settleScale, VelocityTracker, clamp, Roll, rollTarget, numeral, entranceOffset, FLASH_MS, flash, flashOf, offRow, precisionRate, RULER, RULER_NEAR, RULER_FADE, rulerLevels, rulerAlpha, rulerDepth, THIN, THINNEST, thinOf, roundOf, stretchOf, stretchAt, giveRoom, detentScale, detentsOf, roundValues, unwarp, Notches, escapeTo, commandScope, scopeLabel, crumbTrail, escTarget, digitOption, digitHints, createCrumbs, DELAYED, injectPreset, injectStyles, resolveSound, sound, SOUND_PREF, soundPanel, changed, cue, createHintController, pageHints, toggleLayer };
|