@kitlangton/knobs 0.1.0 → 0.3.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 +303 -41
- package/dist/dev/{chunk-qtt698hp.js → chunk-7xgkerd2.js} +34 -11
- package/dist/dev/{chunk-ktsn42p7.js → chunk-jx1ktap3.js} +13762 -11794
- package/dist/dev/{chunk-kdewv0mt.js → chunk-pmkvb9ep.js} +1692 -207
- package/dist/dev/{chunk-e13znr7c.js → chunk-v8ssbcbk.js} +18 -7
- package/dist/dev/index.js +557 -130
- package/dist/index.js +589 -141
- package/dist/panel.js +21615 -17048
- package/dist/types/core/keys.d.ts +2 -0
- package/dist/types/core/random.d.ts +61 -1
- package/dist/types/core/remote.d.ts +5 -1
- package/dist/types/core/schema.d.ts +35 -6
- package/dist/types/core/store.d.ts +97 -8
- package/dist/types/core/unlock.d.ts +77 -0
- package/dist/types/core/variants.d.ts +10 -2
- package/dist/types/index.d.ts +4 -2
- 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 +20 -0
- package/dist/types/panel/feel.d.ts +103 -0
- package/dist/types/panel/flash.d.ts +13 -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 +110 -6
- package/dist/types/panel/gpu/shader.wgsl.d.ts +1 -1
- package/dist/types/panel/haptics.d.ts +6 -2
- package/dist/types/panel/has.d.ts +0 -1
- package/dist/types/panel/index.d.ts +3 -1
- package/dist/types/panel/inspector.d.ts +33 -3
- package/dist/types/panel/lens.d.ts +113 -0
- package/dist/types/panel/motion.d.ts +2 -0
- package/dist/types/panel/optkeys.d.ts +7 -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/presets.d.ts +17 -5
- package/dist/types/panel/ramp-morph.d.ts +117 -0
- package/dist/types/panel/ramps.d.ts +1 -1
- package/dist/types/panel/reel.d.ts +43 -0
- package/dist/types/panel/scope.d.ts +48 -0
- package/dist/types/panel/slider.d.ts +22 -5
- package/dist/types/panel/sound.d.ts +137 -0
- package/dist/types/panel/styles.d.ts +1 -1
- 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/panel/toolbar-grid.d.ts +6 -0
- package/dist/types/ui/activity-css.d.ts +1 -0
- package/dist/types/ui/activity-model.d.ts +122 -0
- package/dist/types/ui/activity.d.ts +67 -0
- package/dist/types/ui/follow.d.ts +17 -0
- package/dist/types/ui/index.d.ts +5 -1
- package/dist/types/ui/look.d.ts +9 -0
- package/dist/types/ui/picker-css.d.ts +1 -0
- package/dist/types/ui/picker-model.d.ts +53 -0
- package/dist/types/ui/picker.d.ts +67 -0
- package/dist/types/ui/react.d.ts +27 -0
- package/dist/types/ui/scrollbar.d.ts +12 -0
- package/dist/types/ui/solid.d.ts +23 -0
- package/dist/types/ui/split-model.d.ts +55 -0
- package/dist/types/ui/split.d.ts +22 -3
- package/dist/ui-react.js +3462 -690
- package/dist/ui-solid.js +3440 -689
- package/dist/ui.js +2963 -264
- package/package.json +1 -1
package/dist/ui-solid.js
CHANGED
|
@@ -1,5 +1,738 @@
|
|
|
1
1
|
// src/ui/solid.ts
|
|
2
|
-
import {
|
|
2
|
+
import {
|
|
3
|
+
children,
|
|
4
|
+
createEffect,
|
|
5
|
+
createRoot,
|
|
6
|
+
getOwner,
|
|
7
|
+
onCleanup,
|
|
8
|
+
untrack
|
|
9
|
+
} from "solid-js";
|
|
10
|
+
import { insert } from "solid-js/web";
|
|
11
|
+
|
|
12
|
+
// src/ui/follow.ts
|
|
13
|
+
function propChanges(initial, same = Object.is) {
|
|
14
|
+
let last = { ...initial };
|
|
15
|
+
return (next) => {
|
|
16
|
+
const changed = {};
|
|
17
|
+
for (const key of Object.keys(next))
|
|
18
|
+
if (!same(next[key], last[key]))
|
|
19
|
+
changed[key] = next[key];
|
|
20
|
+
last = { ...next };
|
|
21
|
+
return changed;
|
|
22
|
+
};
|
|
23
|
+
}
|
|
24
|
+
var anyChanged = (changed) => Object.keys(changed).length > 0;
|
|
25
|
+
function timelineChanges(now, changed) {
|
|
26
|
+
const next = {};
|
|
27
|
+
for (const key of Object.keys(changed)) {
|
|
28
|
+
const value = changed[key];
|
|
29
|
+
if (value === undefined)
|
|
30
|
+
continue;
|
|
31
|
+
if (key === "time" && Math.abs(value - now.time) < 0.000001)
|
|
32
|
+
continue;
|
|
33
|
+
if (key === "playing" && value === now.playing)
|
|
34
|
+
continue;
|
|
35
|
+
if ((key === "markers" || key === "ranges") && value === now[key])
|
|
36
|
+
continue;
|
|
37
|
+
next[key] = value;
|
|
38
|
+
}
|
|
39
|
+
return next;
|
|
40
|
+
}
|
|
41
|
+
|
|
42
|
+
// node_modules/@kitlangton/rolling-number/dist/chunk-vm1v7ncb.js
|
|
43
|
+
var z = new Map;
|
|
44
|
+
var le = /[\u0590-\u08ff\u200e\u200f\u202a-\u202e\u2066-\u2069\ufb1d-\ufeff]/u;
|
|
45
|
+
var U = " ABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789:.-/&+'";
|
|
46
|
+
var V = new Map;
|
|
47
|
+
function Re(e) {
|
|
48
|
+
let t = V.get(e);
|
|
49
|
+
if (!t) {
|
|
50
|
+
if (t = [...new Set(ue(e))], V.size >= 16)
|
|
51
|
+
V.delete(V.keys().next().value);
|
|
52
|
+
V.set(e, t);
|
|
53
|
+
}
|
|
54
|
+
return t;
|
|
55
|
+
}
|
|
56
|
+
function ue(e) {
|
|
57
|
+
if (typeof Intl.Segmenter === "function")
|
|
58
|
+
return [...new Intl.Segmenter(undefined, { granularity: "grapheme" }).segment(e)].map((t) => t.segment);
|
|
59
|
+
return [...e];
|
|
60
|
+
}
|
|
61
|
+
function pe(e, t = {}) {
|
|
62
|
+
let n = t.charset ?? U, i = !le.test(e);
|
|
63
|
+
if (!i)
|
|
64
|
+
return { text: e, tokens: [], rollable: i, signature: "text", magnitude: "" };
|
|
65
|
+
let r = ue(e).map((s, o) => {
|
|
66
|
+
if (t.transition === "direct")
|
|
67
|
+
return { key: `char:${o}`, identity: `char:${o}`, text: s, wheel: [s], index: 0 };
|
|
68
|
+
let h = Re(typeof n === "string" ? n : n[o] ?? n.at(-1) ?? U), c = `char:${o}`, m = h.indexOf(s);
|
|
69
|
+
return m >= 0 ? { key: c, identity: c, text: s, wheel: h, index: m } : { key: `${c}:${s}`, identity: c, text: s };
|
|
70
|
+
});
|
|
71
|
+
return { text: e, tokens: r, rollable: i, signature: `text:${t.transition ?? "wheel"}`, magnitude: "" };
|
|
72
|
+
}
|
|
73
|
+
function me(e, t, n = 0.14) {
|
|
74
|
+
return { target: 0, duration: t, points: Array.from({ length: 49 }, (i, r) => {
|
|
75
|
+
if (r === 48)
|
|
76
|
+
return 0;
|
|
77
|
+
let s = Math.max(0, Math.min(1, (r / 48 - n) / (1 - n)));
|
|
78
|
+
return e * (1 + 10 * s) * Math.exp(-10 * s);
|
|
79
|
+
}) };
|
|
80
|
+
}
|
|
81
|
+
function W(e, t) {
|
|
82
|
+
if (t <= 0 || e.duration <= 0)
|
|
83
|
+
return e;
|
|
84
|
+
let n = e.duration + t, i = Math.round((e.points.length - 1) * n / e.duration) + 1, r = e.points[0] ?? e.target;
|
|
85
|
+
return { target: e.target, duration: n, points: Array.from({ length: i }, (s, o) => {
|
|
86
|
+
if (o === i - 1)
|
|
87
|
+
return e.target;
|
|
88
|
+
let h = o / (i - 1) * n - t;
|
|
89
|
+
return h <= 0 ? r : J(e, h).position;
|
|
90
|
+
}) };
|
|
91
|
+
}
|
|
92
|
+
function L(e, t, n, i) {
|
|
93
|
+
if (i <= 0)
|
|
94
|
+
return { points: [t, t], duration: 0, target: t };
|
|
95
|
+
let r = i / 1000, s = e - t, o = Math.max(Math.abs(s), 1) * 12 / r, h = Math.max(-o, Math.min(o, n)) * r;
|
|
96
|
+
return { points: Array.from({ length: 49 }, (m, f) => {
|
|
97
|
+
if (f === 48)
|
|
98
|
+
return t;
|
|
99
|
+
let y = f / 48;
|
|
100
|
+
return t + (s + (h + 10 * s) * y) * Math.exp(-10 * y);
|
|
101
|
+
}), duration: i, target: t };
|
|
102
|
+
}
|
|
103
|
+
function fe(e, t = 0, n = 24) {
|
|
104
|
+
if (e.duration <= 0)
|
|
105
|
+
return { points: [0, 0], duration: 0, target: 0 };
|
|
106
|
+
let i = e.duration / (e.points.length - 1) / 1000;
|
|
107
|
+
return { duration: e.duration, target: 0, points: e.points.map((r, s, o) => {
|
|
108
|
+
if (s === 0)
|
|
109
|
+
return Math.max(0, Math.min(1, t));
|
|
110
|
+
if (s === o.length - 1)
|
|
111
|
+
return 0;
|
|
112
|
+
let h = Math.abs((o[s + 1] - o[s - 1]) / (2 * i)), c = n / 6;
|
|
113
|
+
return Math.max(0, Math.min(1, (h - c) / (n - c)));
|
|
114
|
+
}) };
|
|
115
|
+
}
|
|
116
|
+
function J(e, t) {
|
|
117
|
+
if (t >= e.duration || e.duration === 0)
|
|
118
|
+
return { position: e.target, velocity: 0 };
|
|
119
|
+
let n = Math.max(0, t) / e.duration * (e.points.length - 1), i = Math.min(Math.floor(n), e.points.length - 2), r = e.points[i] ?? e.target, s = e.points[i + 1] ?? e.target;
|
|
120
|
+
return { position: r + (s - r) * (n - i), velocity: (s - r) * (e.points.length - 1) * 1000 / e.duration };
|
|
121
|
+
}
|
|
122
|
+
function Q(e, t, n, i = 10) {
|
|
123
|
+
let r = Math.floor(e / i) * i + t;
|
|
124
|
+
if (n > 0 && r < e - 0.001)
|
|
125
|
+
r += i;
|
|
126
|
+
else if (n < 0 && r > e + 0.001)
|
|
127
|
+
r -= i;
|
|
128
|
+
else if (n === 0)
|
|
129
|
+
r += Math.round((e - r) / i) * i;
|
|
130
|
+
return r;
|
|
131
|
+
}
|
|
132
|
+
var P = (e, t) => e[(t % e.length + e.length) % e.length];
|
|
133
|
+
function ge(e, t, n) {
|
|
134
|
+
let i = Math.floor(t), r = t - i, s = [P(e, i)];
|
|
135
|
+
if (r > 0.00001)
|
|
136
|
+
s.push(P(e, i + 1));
|
|
137
|
+
if (s.at(-1) !== n)
|
|
138
|
+
s.push(n);
|
|
139
|
+
return { wheel: s, from: r, target: s.length - 1 };
|
|
140
|
+
}
|
|
141
|
+
function ye(e, t = "outward") {
|
|
142
|
+
if (t !== "outward") {
|
|
143
|
+
let r = e.map((o, h) => o ? -1 : h).filter((o) => o >= 0);
|
|
144
|
+
if (t === "end")
|
|
145
|
+
r.reverse();
|
|
146
|
+
let s = e.map((o) => o ? 0 : 1);
|
|
147
|
+
if (t !== "none")
|
|
148
|
+
r.forEach((o, h) => {
|
|
149
|
+
s[o] = h + 1;
|
|
150
|
+
});
|
|
151
|
+
return s;
|
|
152
|
+
}
|
|
153
|
+
let n = e.map((r, s) => r ? 0 : s + 1);
|
|
154
|
+
if (!e.includes(true))
|
|
155
|
+
return n;
|
|
156
|
+
let i = -1 / 0;
|
|
157
|
+
for (let r = 0;r < e.length; r++)
|
|
158
|
+
if (e[r])
|
|
159
|
+
i = r;
|
|
160
|
+
else
|
|
161
|
+
n[r] = r - i;
|
|
162
|
+
i = 1 / 0;
|
|
163
|
+
for (let r = e.length - 1;r >= 0; r--)
|
|
164
|
+
if (e[r])
|
|
165
|
+
i = r;
|
|
166
|
+
else
|
|
167
|
+
n[r] = Math.min(n[r], i - r);
|
|
168
|
+
return n;
|
|
169
|
+
}
|
|
170
|
+
function Z(e, t) {
|
|
171
|
+
let n = new Map, i = [], r = 0;
|
|
172
|
+
for (let s of e) {
|
|
173
|
+
let o = t.get(s);
|
|
174
|
+
if (!o) {
|
|
175
|
+
i.push(s);
|
|
176
|
+
continue;
|
|
177
|
+
}
|
|
178
|
+
for (let h of i)
|
|
179
|
+
n.set(h, o.x);
|
|
180
|
+
i.length = 0, r = o.x + o.width;
|
|
181
|
+
}
|
|
182
|
+
for (let s of i)
|
|
183
|
+
n.set(s, r);
|
|
184
|
+
return n;
|
|
185
|
+
}
|
|
186
|
+
var ee = new WeakMap;
|
|
187
|
+
|
|
188
|
+
class Y {
|
|
189
|
+
view;
|
|
190
|
+
media;
|
|
191
|
+
members = new Set;
|
|
192
|
+
pending = new Set;
|
|
193
|
+
sizes = new WeakMap;
|
|
194
|
+
intersections = new WeakMap;
|
|
195
|
+
resize;
|
|
196
|
+
intersection;
|
|
197
|
+
frame = 0;
|
|
198
|
+
static for(e) {
|
|
199
|
+
let t = ee.get(e);
|
|
200
|
+
if (!t)
|
|
201
|
+
t = new Y(e), ee.set(e, t);
|
|
202
|
+
return t;
|
|
203
|
+
}
|
|
204
|
+
constructor(e) {
|
|
205
|
+
this.view = e;
|
|
206
|
+
if (this.media = e.matchMedia("(prefers-reduced-motion: reduce)"), this.media.addEventListener("change", this.refresh), e.document.addEventListener("visibilitychange", this.refresh), e.document.fonts?.addEventListener("loadingdone", this.refresh), e.document.fonts?.ready.then(this.refresh), e.ResizeObserver)
|
|
207
|
+
this.resize = new e.ResizeObserver((t) => {
|
|
208
|
+
for (let n of t) {
|
|
209
|
+
let i = this.sizes.get(n.target);
|
|
210
|
+
if (i?.sizeChanged(n.target, n.contentRect.width, n.contentRect.height))
|
|
211
|
+
i.refresh();
|
|
212
|
+
}
|
|
213
|
+
});
|
|
214
|
+
if (e.IntersectionObserver)
|
|
215
|
+
this.intersection = new e.IntersectionObserver((t) => {
|
|
216
|
+
for (let n of t)
|
|
217
|
+
this.intersections.get(n.target)?.visibility(n.isIntersecting);
|
|
218
|
+
}, { rootMargin: "64px" });
|
|
219
|
+
}
|
|
220
|
+
refresh = () => {
|
|
221
|
+
for (let e of this.members)
|
|
222
|
+
e.refresh();
|
|
223
|
+
};
|
|
224
|
+
add(e, t) {
|
|
225
|
+
this.members.add(e), this.intersections.set(t, e), this.intersection?.observe(t);
|
|
226
|
+
}
|
|
227
|
+
watch(e, t) {
|
|
228
|
+
this.sizes.set(e, t), this.resize?.observe(e);
|
|
229
|
+
}
|
|
230
|
+
unwatch(e) {
|
|
231
|
+
this.resize?.unobserve(e), this.sizes.delete(e);
|
|
232
|
+
}
|
|
233
|
+
enqueue(e) {
|
|
234
|
+
if (this.pending.add(e), !this.frame)
|
|
235
|
+
this.frame = this.view.requestAnimationFrame(() => {
|
|
236
|
+
this.frame = 0;
|
|
237
|
+
let t = [...this.pending];
|
|
238
|
+
this.pending.clear();
|
|
239
|
+
let n = t.map((r) => r.stage());
|
|
240
|
+
for (let r of n)
|
|
241
|
+
r?.();
|
|
242
|
+
let i = t.map((r) => r.measure());
|
|
243
|
+
for (let r of i)
|
|
244
|
+
r?.();
|
|
245
|
+
});
|
|
246
|
+
}
|
|
247
|
+
remove(e, t) {
|
|
248
|
+
if (this.pending.delete(e), this.members.delete(e), this.intersection?.unobserve(t), this.intersections.delete(t), this.members.size)
|
|
249
|
+
return;
|
|
250
|
+
this.view.cancelAnimationFrame(this.frame), this.resize?.disconnect(), this.intersection?.disconnect(), this.media.removeEventListener("change", this.refresh), this.view.document.removeEventListener("visibilitychange", this.refresh), this.view.document.fonts?.removeEventListener("loadingdone", this.refresh), ee.delete(this.view);
|
|
251
|
+
}
|
|
252
|
+
}
|
|
253
|
+
var be = new WeakMap;
|
|
254
|
+
function F(e, t, n) {
|
|
255
|
+
let i = e.animate(t, n), r = e.ownerDocument.timeline?.currentTime;
|
|
256
|
+
if (typeof r === "number" && i.playState === "running")
|
|
257
|
+
i.startTime = r;
|
|
258
|
+
return i;
|
|
259
|
+
}
|
|
260
|
+
function ve(e) {
|
|
261
|
+
let t = e.ownerDocument.defaultView;
|
|
262
|
+
if (!t)
|
|
263
|
+
return false;
|
|
264
|
+
let n = be.get(t);
|
|
265
|
+
if (n === undefined)
|
|
266
|
+
n = t.CSS?.supports("animation-timing-function", "linear(0, 1)") ?? false, be.set(t, n);
|
|
267
|
+
return n;
|
|
268
|
+
}
|
|
269
|
+
|
|
270
|
+
class N {
|
|
271
|
+
element;
|
|
272
|
+
property;
|
|
273
|
+
animation;
|
|
274
|
+
motion;
|
|
275
|
+
value = 0;
|
|
276
|
+
constructor(e, t) {
|
|
277
|
+
this.element = e;
|
|
278
|
+
this.property = t;
|
|
279
|
+
}
|
|
280
|
+
read() {
|
|
281
|
+
let e = this.animation?.currentTime;
|
|
282
|
+
return this.animation && this.motion ? J(this.motion, typeof e === "number" ? e : 0) : { position: this.value, velocity: 0 };
|
|
283
|
+
}
|
|
284
|
+
set(e, t) {
|
|
285
|
+
this.cancel(), this.value = e, this.element.style.setProperty(this.property, t(e));
|
|
286
|
+
}
|
|
287
|
+
play(e, t, n) {
|
|
288
|
+
if (this.cancel(), this.value = e.target, this.element.style.setProperty(this.property, t(e.target)), !e.duration || e.points.every((f) => f === e.target)) {
|
|
289
|
+
n?.();
|
|
290
|
+
return;
|
|
291
|
+
}
|
|
292
|
+
let i = e.points[0] ?? e.target, r = e.target - i, s = this.property === "opacity" && ve(this.element), o = this.property === "transform" && Math.abs(r) > 0.00001 && ve(this.element), h = s ? [{ opacity: 0 }, { opacity: 1 }] : o ? [{ [this.property]: t(i) }, { [this.property]: t(e.target) }] : e.points.map((f) => ({ [this.property]: t(f) })), c = s ? `linear(${e.points.map(t).join(",")})` : o ? `linear(${e.points.map((f) => Number(((f - i) / r).toFixed(6))).join(",")})` : "linear", m = F(this.element, h, { duration: e.duration, easing: c });
|
|
293
|
+
this.animation = m, this.motion = e, m.onfinish = () => {
|
|
294
|
+
if (this.animation !== m)
|
|
295
|
+
return;
|
|
296
|
+
this.animation = undefined, this.motion = undefined, m.onfinish = null, m.cancel(), n?.();
|
|
297
|
+
};
|
|
298
|
+
}
|
|
299
|
+
cancel() {
|
|
300
|
+
if (this.animation)
|
|
301
|
+
this.animation.onfinish = null, this.animation.cancel(), this.animation = undefined;
|
|
302
|
+
this.motion = undefined;
|
|
303
|
+
}
|
|
304
|
+
}
|
|
305
|
+
var te = "http://www.w3.org/2000/svg";
|
|
306
|
+
var $e = 0;
|
|
307
|
+
|
|
308
|
+
class G {
|
|
309
|
+
host;
|
|
310
|
+
layers = new Map;
|
|
311
|
+
filter;
|
|
312
|
+
intensity = 1;
|
|
313
|
+
constructor(e) {
|
|
314
|
+
this.host = e;
|
|
315
|
+
}
|
|
316
|
+
filterUrl(e) {
|
|
317
|
+
if (!this.filter) {
|
|
318
|
+
let n = this.host.ownerDocument, i = n.createElementNS(te, "svg");
|
|
319
|
+
i.classList.add("rn-blur-defs"), i.setAttribute("aria-hidden", "true"), i.setAttribute("focusable", "false");
|
|
320
|
+
let r = n.createElementNS(te, "filter"), s;
|
|
321
|
+
do
|
|
322
|
+
s = `rn-vertical-blur-${++$e}`;
|
|
323
|
+
while (n.getElementById(s));
|
|
324
|
+
r.id = s, r.setAttribute("x", "-15%"), r.setAttribute("width", "130%"), r.setAttribute("color-interpolation-filters", "sRGB");
|
|
325
|
+
let o = n.createElementNS(te, "feGaussianBlur");
|
|
326
|
+
r.append(o), i.append(r), this.host.append(i), this.filter = { svg: i, blur: o, id: s, height: 0 };
|
|
327
|
+
}
|
|
328
|
+
let t = e * 0.035 * this.intensity;
|
|
329
|
+
if (this.filter.height !== t)
|
|
330
|
+
this.filter.blur.setAttribute("stdDeviation", `0 ${t}`), this.filter.height = t;
|
|
331
|
+
return `url("#${this.filter.id}")`;
|
|
332
|
+
}
|
|
333
|
+
apply(e, t, n, i, r = "roll") {
|
|
334
|
+
let s = fe(t, i, r === "entry" ? 6 : 24);
|
|
335
|
+
if (s.points.every((f) => f === 0))
|
|
336
|
+
return false;
|
|
337
|
+
let o = this.host.ownerDocument.createElement("span");
|
|
338
|
+
o.className = "rn-sharp", o.append(...e.childNodes);
|
|
339
|
+
let h = o.cloneNode(true);
|
|
340
|
+
h.className = "rn-smear", h.style.filter = this.filterUrl(n), e.append(o, h);
|
|
341
|
+
let c = new N(o, "opacity"), m = new N(h, "opacity");
|
|
342
|
+
return this.layers.set(e, { sharp: o, sharpOpacity: c, smearOpacity: m }), c.play(s, (f) => String(1 - f)), m.play(s, String), true;
|
|
343
|
+
}
|
|
344
|
+
remove(e) {
|
|
345
|
+
let t = this.layers.get(e);
|
|
346
|
+
if (!t)
|
|
347
|
+
return 0;
|
|
348
|
+
let n = t.smearOpacity.read().position;
|
|
349
|
+
return t.sharpOpacity.cancel(), t.smearOpacity.cancel(), e.replaceChildren(...t.sharp.childNodes), this.layers.delete(e), n;
|
|
350
|
+
}
|
|
351
|
+
destroy() {
|
|
352
|
+
for (let e of this.layers.keys())
|
|
353
|
+
this.remove(e);
|
|
354
|
+
this.filter?.svg.remove(), this.filter = undefined;
|
|
355
|
+
}
|
|
356
|
+
}
|
|
357
|
+
function xe(e) {
|
|
358
|
+
return Math.max(45, Math.min(110, e / 7));
|
|
359
|
+
}
|
|
360
|
+
function we(e, t, n) {
|
|
361
|
+
let i = Math.abs(t - e);
|
|
362
|
+
return { points: [e, t], target: t, duration: i * n };
|
|
363
|
+
}
|
|
364
|
+
function Ae(e) {
|
|
365
|
+
let i = Array.from({ length: e + 1 }, (o, h) => ({ "--rn-flap-step": String(h), offset: h / e, easing: "steps(1, end)" })), r = [], s = [];
|
|
366
|
+
for (let o = 0;o < e; o++) {
|
|
367
|
+
let h = o / e, c = (o + 0.5) / e, m = (o + 1) / e;
|
|
368
|
+
r.push({ transform: "perspective(5em) rotateX(0deg)", filter: "brightness(1)", offset: h, easing: "cubic-bezier(.6, 0, 1, .5)" }, { transform: "perspective(5em) rotateX(-90deg)", filter: "brightness(.45)", offset: c }, { transform: "perspective(5em) rotateX(-90deg)", filter: "brightness(.45)", offset: m }), s.push({ transform: "perspective(5em) rotateX(90deg)", filter: "brightness(.45)", offset: h }, { transform: "perspective(5em) rotateX(90deg)", filter: "brightness(.45)", offset: c, easing: "linear(0, 0.58, 0.9, 1, 1.045 78%, 1)" }, { transform: "perspective(5em) rotateX(0deg)", filter: "brightness(1)", offset: m });
|
|
369
|
+
}
|
|
370
|
+
return { index: i, falls: r, lands: s };
|
|
371
|
+
}
|
|
372
|
+
function Me(e) {
|
|
373
|
+
for (let t of e.querySelectorAll(".rn-flap-smear")) {
|
|
374
|
+
for (let n of t.getAnimations())
|
|
375
|
+
n.cancel();
|
|
376
|
+
t.remove();
|
|
377
|
+
}
|
|
378
|
+
for (let t of e.querySelectorAll(".rn-flap-sharp")) {
|
|
379
|
+
for (let n of t.getAnimations())
|
|
380
|
+
n.cancel();
|
|
381
|
+
t.classList.remove("rn-flap-sharp");
|
|
382
|
+
}
|
|
383
|
+
}
|
|
384
|
+
function ke(e, t, n, i, r, s, o, h) {
|
|
385
|
+
let c = e.ownerDocument, m = Math.abs(i - n), f = i >= n ? 1 : -1;
|
|
386
|
+
if (!m) {
|
|
387
|
+
e.replaceChildren();
|
|
388
|
+
return;
|
|
389
|
+
}
|
|
390
|
+
let y = Ae(m), M = Array.from({ length: m + 1 }, (p, w) => P(t, n + w * f)), S = [...M.slice(1), M.at(-1)], u = M.some((p) => /[\r\n\f\u2028\u2029]/u.test(p)), g = c.createElement("span");
|
|
391
|
+
g.style.cssText = `display:block;position:relative;height:${r}px`, F(g, y.index, { delay: o, duration: m * s, fill: "both" });
|
|
392
|
+
let T = (p, w) => {
|
|
393
|
+
let k = c.createElement("span");
|
|
394
|
+
k.className = `rn-face rn-flap rn-flap-${p}`, k.style.height = `${r}px`, k.style.overflow = "hidden";
|
|
395
|
+
let v = c.createElement("span");
|
|
396
|
+
v.style.cssText = `display:block;white-space:pre;line-height:${r}px`;
|
|
397
|
+
let l = w ? S : M;
|
|
398
|
+
if (u)
|
|
399
|
+
for (let b of l) {
|
|
400
|
+
let O = c.createElement("span");
|
|
401
|
+
O.style.cssText = `display:block;height:${r}px`, O.textContent = b, v.append(O);
|
|
402
|
+
}
|
|
403
|
+
else
|
|
404
|
+
v.textContent = l.join(`
|
|
405
|
+
`);
|
|
406
|
+
return k.append(v), v.style.transform = `translateY(calc(var(--rn-flap-step) * ${-r}px))`, k;
|
|
407
|
+
}, H = T("bottom", false), j = T("top", true), d = T("top", false), a = T("bottom", true);
|
|
408
|
+
if (h) {
|
|
409
|
+
let p = (w, k) => {
|
|
410
|
+
let v = w.firstElementChild, l = v.cloneNode(true);
|
|
411
|
+
v.classList.add("rn-flap-sharp");
|
|
412
|
+
let b = c.createElement("span");
|
|
413
|
+
b.className = "rn-flap-smear", b.style.cssText = "display:block;position:absolute;inset:0;overflow:hidden", b.style.filter = h, b.append(l), w.append(b);
|
|
414
|
+
let O = k.map((C) => ({ offset: C.offset, easing: C.easing ?? "linear", opacity: C.filter === "brightness(1)" ? 0 : 1 })), A = { delay: o, duration: m * s, fill: "both" };
|
|
415
|
+
F(b, O, A), F(v, O.map((C) => ({ ...C, opacity: 1 - C.opacity })), A);
|
|
416
|
+
};
|
|
417
|
+
p(d, y.falls), p(a, y.lands);
|
|
418
|
+
}
|
|
419
|
+
d.style.transform = "perspective(5em) rotateX(-90deg)", F(d, y.falls, { delay: o, duration: m * s, fill: "backwards" }), a.style.transform = "perspective(5em) rotateX(90deg)", F(a, y.lands, { delay: o, duration: m * s, fill: "forwards" }), e.style.height = `${r}px`, g.append(H, j, a, d), e.replaceChildren(g);
|
|
420
|
+
}
|
|
421
|
+
var Ee = new WeakMap;
|
|
422
|
+
var _ = new WeakSet;
|
|
423
|
+
var ne = (e) => `translateX(${e}px)`;
|
|
424
|
+
var ie = (e) => `scale(${e})`;
|
|
425
|
+
var re = (e) => String(Math.max(0, Math.min(1, e)));
|
|
426
|
+
var se = (e) => ("transition" in e) && e.transition === "direct";
|
|
427
|
+
function Te(e) {
|
|
428
|
+
if (e.duration !== undefined && (!Number.isFinite(e.duration) || e.duration < 0 || e.duration > 1e4))
|
|
429
|
+
throw RangeError("duration must be between 0 and 10000 milliseconds");
|
|
430
|
+
if (e.flipDuration !== undefined && (!Number.isFinite(e.flipDuration) || e.flipDuration < 1 || e.flipDuration > 1e4))
|
|
431
|
+
throw RangeError("flipDuration must be between 1 and 10000 milliseconds");
|
|
432
|
+
}
|
|
433
|
+
var Fe = { validate(e) {
|
|
434
|
+
if (typeof e.text !== "string")
|
|
435
|
+
throw TypeError("text must be a string");
|
|
436
|
+
if (e.transition !== undefined && e.transition !== "direct" && e.transition !== "wheel")
|
|
437
|
+
throw RangeError("transition must be direct or wheel");
|
|
438
|
+
if (e.transition === "direct" && e.mode === "flap")
|
|
439
|
+
throw RangeError("Direct text transitions require roll mode");
|
|
440
|
+
Te(e);
|
|
441
|
+
}, model: (e) => pe(e.text, e), direction: () => 1 };
|
|
442
|
+
|
|
443
|
+
class oe {
|
|
444
|
+
host;
|
|
445
|
+
source;
|
|
446
|
+
options;
|
|
447
|
+
target;
|
|
448
|
+
displayed;
|
|
449
|
+
semantic;
|
|
450
|
+
measurement;
|
|
451
|
+
visual;
|
|
452
|
+
measures = new Map;
|
|
453
|
+
columns = new Map;
|
|
454
|
+
sizes = new Map;
|
|
455
|
+
scheduler;
|
|
456
|
+
enhanced = false;
|
|
457
|
+
destroyed = false;
|
|
458
|
+
visible = true;
|
|
459
|
+
reset = true;
|
|
460
|
+
measurementPending = false;
|
|
461
|
+
hadClass;
|
|
462
|
+
previousLeft;
|
|
463
|
+
blur;
|
|
464
|
+
blurIntensity = 1;
|
|
465
|
+
constructor(e, t, n) {
|
|
466
|
+
this.host = e;
|
|
467
|
+
this.source = n;
|
|
468
|
+
n.validate(t), this.options = { ...t }, this.target = this.displayed = n.model(t);
|
|
469
|
+
let i = e.ownerDocument, r = (o) => {
|
|
470
|
+
let h = i.createElement("span");
|
|
471
|
+
return h.className = o, h;
|
|
472
|
+
};
|
|
473
|
+
this.semantic = r("rn-value"), this.measurement = r("rn-measure"), this.visual = r("rn-visual"), this.measurement.setAttribute("aria-hidden", "true"), this.visual.setAttribute("aria-hidden", "true"), this.semantic.textContent = this.target.text, this.hadClass = e.classList.contains("rn-root"), e.classList.add("rn-root"), e.replaceChildren(this.semantic, this.measurement, this.visual);
|
|
474
|
+
let s = i.defaultView;
|
|
475
|
+
if (s && typeof s.matchMedia === "function" && typeof s.requestAnimationFrame === "function" && typeof e.animate === "function")
|
|
476
|
+
this.scheduler = Y.for(s), this.scheduler.add(this, e), this.scheduler.watch(this.measurement, this);
|
|
477
|
+
this.prepare();
|
|
478
|
+
}
|
|
479
|
+
canAnimate() {
|
|
480
|
+
return !!this.scheduler && this.options.animated !== false && (this.options.duration ?? 500) > 0 && !this.scheduler.media.matches && !this.host.ownerDocument.hidden && (this.visible || this.options.pauseOffscreen === false) && this.target.rollable && this.host.isConnected;
|
|
481
|
+
}
|
|
482
|
+
update(e) {
|
|
483
|
+
if (this.destroyed)
|
|
484
|
+
return;
|
|
485
|
+
let t = { ...this.options, ...e };
|
|
486
|
+
this.source.validate(t);
|
|
487
|
+
let n = this.source.model(t), i = n.text === this.target.text && n.signature === this.target.signature;
|
|
488
|
+
if (this.options.motionBlur && !t.motionBlur)
|
|
489
|
+
this.blur?.destroy(), this.blur = undefined, Me(this.visual);
|
|
490
|
+
if (se(this.options) !== se(t))
|
|
491
|
+
this.reset = true;
|
|
492
|
+
if (this.options = t, this.target = n, !this.canAnimate()) {
|
|
493
|
+
this.finish();
|
|
494
|
+
return;
|
|
495
|
+
}
|
|
496
|
+
if (i && this.enhanced && !this.reset)
|
|
497
|
+
return;
|
|
498
|
+
this.semantic.textContent = n.text, this.prepare();
|
|
499
|
+
}
|
|
500
|
+
prepare() {
|
|
501
|
+
if (!this.canAnimate()) {
|
|
502
|
+
this.finish();
|
|
503
|
+
return;
|
|
504
|
+
}
|
|
505
|
+
this.measurementPending = true, this.scheduler?.enqueue(this);
|
|
506
|
+
}
|
|
507
|
+
stage() {
|
|
508
|
+
if (this.destroyed)
|
|
509
|
+
return;
|
|
510
|
+
if (!this.canAnimate())
|
|
511
|
+
return () => this.finish();
|
|
512
|
+
return this.previousLeft = this.enhanced && !this.reset ? this.measurement.getBoundingClientRect().left : undefined, () => this.stageMeasurement();
|
|
513
|
+
}
|
|
514
|
+
stageMeasurement() {
|
|
515
|
+
let e = new Set(this.target.tokens.map((n) => n.key));
|
|
516
|
+
for (let [n, i] of this.measures) {
|
|
517
|
+
if (e.has(n))
|
|
518
|
+
continue;
|
|
519
|
+
this.scheduler?.unwatch(i), this.sizes.delete(i), i.remove(), this.measures.delete(n);
|
|
520
|
+
}
|
|
521
|
+
let t = null;
|
|
522
|
+
for (let n of this.target.tokens) {
|
|
523
|
+
let i = this.measures.get(n.key);
|
|
524
|
+
if (!i)
|
|
525
|
+
i = this.host.ownerDocument.createElement("span"), i.className = "rn-token", this.measures.set(n.key, i), this.scheduler?.watch(i, this);
|
|
526
|
+
if (i.textContent !== n.text)
|
|
527
|
+
i.textContent = n.text;
|
|
528
|
+
let r = t ? t.nextSibling : this.measurement.firstChild;
|
|
529
|
+
if (i !== r)
|
|
530
|
+
this.measurement.insertBefore(i, r);
|
|
531
|
+
t = i;
|
|
532
|
+
}
|
|
533
|
+
this.host.dataset.rnMeasuring = "";
|
|
534
|
+
}
|
|
535
|
+
measure() {
|
|
536
|
+
if (this.destroyed)
|
|
537
|
+
return;
|
|
538
|
+
if (!this.canAnimate())
|
|
539
|
+
return () => this.finish();
|
|
540
|
+
let e = this.measurement.getBoundingClientRect(), t = this.host.ownerDocument.defaultView;
|
|
541
|
+
if (!t)
|
|
542
|
+
return () => this.finish();
|
|
543
|
+
let n = t.getComputedStyle(this.measurement);
|
|
544
|
+
if (n.direction === "rtl")
|
|
545
|
+
return () => this.finish();
|
|
546
|
+
let i = parseFloat(n.width), r = parseFloat(n.height);
|
|
547
|
+
if (!i || !r || !e.width || !e.height)
|
|
548
|
+
return () => this.finish();
|
|
549
|
+
let s = e.width / i, o = e.height / r, h = parseFloat(n.getPropertyValue("--rn-blur"));
|
|
550
|
+
this.blurIntensity = Number.isFinite(h) ? Math.max(0, h) : 1, this.sizes.set(this.measurement, { width: i, height: r });
|
|
551
|
+
let c = new Map;
|
|
552
|
+
for (let [f, y] of this.measures) {
|
|
553
|
+
let M = y.getBoundingClientRect(), S = { width: M.width / s, height: M.height / o };
|
|
554
|
+
this.sizes.set(y, S), c.set(f, { ...S, x: (M.left - e.left) / s, y: (M.top - e.top) / o });
|
|
555
|
+
}
|
|
556
|
+
let m = this.previousLeft === undefined ? 0 : (this.previousLeft - e.left) / s;
|
|
557
|
+
return () => this.commit(c, m);
|
|
558
|
+
}
|
|
559
|
+
makeColumn(e) {
|
|
560
|
+
let t = this.host.ownerDocument.createElement("span");
|
|
561
|
+
if (t.className = "rn-slot", t.dataset.rnKey = e.key, e.index !== undefined)
|
|
562
|
+
t.dataset.rnWheel = "";
|
|
563
|
+
if (this.options.mode === "flap")
|
|
564
|
+
t.dataset.rnFlap = "";
|
|
565
|
+
let n = this.host.ownerDocument.createElement("span");
|
|
566
|
+
return n.className = "rn-reel", t.append(n), this.visual.append(t), { token: e, element: t, reel: n, x: new N(t, "transform"), opacity: new N(t, "opacity"), roll: new N(n, "transform"), exiting: false, height: 0, width: 0 };
|
|
567
|
+
}
|
|
568
|
+
face(e, t) {
|
|
569
|
+
let n = this.host.ownerDocument.createElement("span");
|
|
570
|
+
n.className = "rn-face", n.textContent = t, n.style.height = `${e.height}px`;
|
|
571
|
+
let i = e.reel.children.length;
|
|
572
|
+
n.style.position = "absolute", n.style.top = "0", n.style.left = "0", n.style.width = "100%", n.style.transform = `translateY(${i * e.height}px)`, e.reel.style.height = `${(i + 1) * e.height}px`, e.reel.append(n);
|
|
573
|
+
}
|
|
574
|
+
rest(e) {
|
|
575
|
+
if (this.blur?.remove(e.reel), e.reel.replaceChildren(), e.reel.style.removeProperty("height"), this.face(e, e.token.text), this.wrapInk(e), e.token.index === undefined)
|
|
576
|
+
e.roll.set(1, ie);
|
|
577
|
+
else
|
|
578
|
+
e.roll.set(e.token.index, () => "translateY(0px)");
|
|
579
|
+
}
|
|
580
|
+
wrapInk(e) {
|
|
581
|
+
if (this.options.mode === "flap")
|
|
582
|
+
return;
|
|
583
|
+
let t = this.host.ownerDocument.createElement("span");
|
|
584
|
+
t.className = "rn-ink", t.append(...e.reel.childNodes), e.reel.append(t);
|
|
585
|
+
}
|
|
586
|
+
finishEntry(e) {
|
|
587
|
+
if (!e.entry)
|
|
588
|
+
return;
|
|
589
|
+
if (e.entry.blurred)
|
|
590
|
+
this.blur?.remove(e.reel);
|
|
591
|
+
e.entry.track.cancel(), e.entry.element.replaceWith(e.reel), e.entry = undefined;
|
|
592
|
+
}
|
|
593
|
+
enter(e, t, n, i) {
|
|
594
|
+
let r = this.host.ownerDocument.createElement("span");
|
|
595
|
+
r.className = "rn-enter", e.reel.replaceWith(r), r.append(e.reel);
|
|
596
|
+
let s = new N(r, "transform");
|
|
597
|
+
e.entry = { element: r, track: s, blurred: false };
|
|
598
|
+
let o = W(me(e.height * (i?.entryDistance ?? 1), i?.entryDuration ?? t, i?.entryHold), n);
|
|
599
|
+
if (this.options.motionBlur && e.token.text.trim()) {
|
|
600
|
+
this.blur ??= new G(this.host), this.blur.intensity = this.blurIntensity;
|
|
601
|
+
let h = { ...o, points: o.points.map((c) => c / e.height) };
|
|
602
|
+
e.entry.blurred = this.blur.apply(e.reel, h, e.height, 0, "entry");
|
|
603
|
+
}
|
|
604
|
+
s.play(o, (h) => `translateY(${h}px)`, () => this.finishEntry(e));
|
|
605
|
+
}
|
|
606
|
+
commit(e, t) {
|
|
607
|
+
if (this.destroyed)
|
|
608
|
+
return;
|
|
609
|
+
this.measurementPending = false;
|
|
610
|
+
let n = this.enhanced && !this.reset, i = n ? this.options.duration ?? 500 : 0, r = Ee.get(this.host), s = i ? r?.widthDuration ?? i : 0, o = this.options.mode === "flap", h = this.options.direction === "up" ? 1 : this.options.direction === "down" ? -1 : this.source.direction(this.displayed, this.target);
|
|
611
|
+
if (this.target.text !== this.displayed.text)
|
|
612
|
+
this.host.dataset.rnTrend = h > 0 ? "up" : h < 0 ? "down" : "none";
|
|
613
|
+
let c = new Map([...this.columns].map(([d, a]) => {
|
|
614
|
+
let p = a.x.read();
|
|
615
|
+
return [d, { ...p, x: p.position, width: a.width }];
|
|
616
|
+
})), m = Z(this.target.tokens.map((d) => d.key), c), f = [...c.keys()].sort((d, a) => c.get(d).x - c.get(a).x), y = Z(f, e), M = new Map(this.displayed.tokens.filter((d) => d.index === undefined).map((d) => [d.identity, d.key])), S = new Map(this.target.tokens.filter((d) => d.index === undefined).map((d) => [d.identity, d.key])), u = this.options.stagger === "start" || this.options.stagger === "end", g = this.target.tokens.map((d) => {
|
|
617
|
+
let a = this.columns.get(d.key);
|
|
618
|
+
return !a || a.exiting || u && d.index !== undefined && a.token.text !== d.text;
|
|
619
|
+
}), T = ye(this.target.tokens.map((d, a) => d.index !== undefined && !g[a]), this.options.stagger), H = Math.max(0, ...this.target.tokens.map((d, a) => g[a] ? T[a] - 1 : 0)), j = Math.min(i * 0.045, i * 0.3 / Math.max(1, H));
|
|
620
|
+
for (let [d, a] of this.target.tokens.entries()) {
|
|
621
|
+
let p = e.get(a.key);
|
|
622
|
+
if (!p)
|
|
623
|
+
continue;
|
|
624
|
+
let w = Math.max(0, T[d] - 1) * j, k = M.get(a.identity), v = k !== undefined && k !== a.key ? c.get(k) : undefined, l = this.columns.get(a.key), b = !l;
|
|
625
|
+
if (!l) {
|
|
626
|
+
l = this.makeColumn(a), this.columns.set(a.key, l);
|
|
627
|
+
let x = (v?.x ?? m.get(a.key) ?? p.x) + t;
|
|
628
|
+
l.x.set(n ? x + (p.x - x) * (v ? 0 : r?.entryOrigin ?? 0) : p.x, ne), l.opacity.set(n ? 0 : 1, re);
|
|
629
|
+
}
|
|
630
|
+
let O = l.token.text !== a.text, A = Math.abs(l.height - p.height) > 0.1, C = l.exiting;
|
|
631
|
+
l.exiting = false, l.element.style.width = `${p.width}px`, l.element.style.height = `${p.height}px`, l.element.style.top = `${p.y}px`;
|
|
632
|
+
let q = c.get(a.key);
|
|
633
|
+
if (l.x.play(L(q ? q.position + t : l.x.read().position, p.x, q?.velocity ?? 0, s), ne), b || C || !n) {
|
|
634
|
+
let x = l.opacity.read(), E = L(x.position, 1, x.velocity, a.index === undefined ? Math.min(i, 180) : i ? r?.fadeDuration ?? i : 0), R = !o && a.identity.startsWith("group:") && !v ? (r?.entryDuration ?? i) * (r?.entryHold ?? 0.14) : 0;
|
|
635
|
+
l.opacity.play(b ? W(E, w + R) : E, re);
|
|
636
|
+
}
|
|
637
|
+
if (l.height = p.height, l.width = p.width, !n || A)
|
|
638
|
+
this.finishEntry(l);
|
|
639
|
+
if (b && o && a.wheel && i)
|
|
640
|
+
l.roll.set(Math.max(0, a.wheel.indexOf(" ")), () => "translateY(0px)");
|
|
641
|
+
if (o && !A && (O || b) && a.index !== undefined && a.wheel && i && l.roll.read().position !== a.index) {
|
|
642
|
+
let x = l.roll.read(), E = Math.round(x.position), R = Q(E, a.index, h, a.wheel.length), D = this.options.flipDuration ?? xe(i);
|
|
643
|
+
this.blur?.remove(l.reel);
|
|
644
|
+
let I;
|
|
645
|
+
if (this.options.motionBlur && this.blurIntensity > 0)
|
|
646
|
+
this.blur ??= new G(this.host), this.blur.intensity = this.blurIntensity, I = this.blur.filterUrl(p.height);
|
|
647
|
+
ke(l.reel, a.wheel, E, R, p.height, D, w, I), l.token = a;
|
|
648
|
+
let X = l;
|
|
649
|
+
l.roll.play(W(we(E, R, D), w), () => "translateY(0px)", () => this.rest(X));
|
|
650
|
+
} else if (!b && !A && O && a.index !== undefined && a.wheel && l.token.index !== undefined && i) {
|
|
651
|
+
let x = l.roll.read(), E = se(this.options) ? ge(l.token.wheel, x.position, a.text) : undefined, R = E?.from ?? x.position, D = E?.target ?? Q(x.position, a.index, h, a.wheel.length), I = E?.wheel ?? a.wheel, X = E ? Math.min(Math.max(0, x.velocity), (D - R) * 1e4 / i) : x.velocity, K = u ? W(L(R, D, X, i), w) : L(R, D, X, i), ae = Math.floor(Math.min(...K.points)), Oe = Math.ceil(Math.max(...K.points));
|
|
652
|
+
if (l.entry)
|
|
653
|
+
l.entry.blurred = false;
|
|
654
|
+
let Se = this.blur?.remove(l.reel) ?? 0;
|
|
655
|
+
l.reel.replaceChildren();
|
|
656
|
+
for (let B = ae;B <= Oe; B++)
|
|
657
|
+
this.face(l, P(I, B));
|
|
658
|
+
if (this.wrapInk(l), this.options.motionBlur)
|
|
659
|
+
this.blur ??= new G(this.host), this.blur.intensity = this.blurIntensity, this.blur.apply(l.reel, K, p.height, Se);
|
|
660
|
+
l.token = E ? { ...a, wheel: I, index: D } : a;
|
|
661
|
+
let Ce = l;
|
|
662
|
+
l.roll.play(K, (B) => `translateY(${(ae - B) * p.height}px)`, () => this.rest(Ce));
|
|
663
|
+
} else if (b || A || O || !n)
|
|
664
|
+
l.token = a, this.rest(l);
|
|
665
|
+
if (b && i && a.index !== undefined && !o)
|
|
666
|
+
this.enter(l, i, w, r);
|
|
667
|
+
if (v && i && (b || C)) {
|
|
668
|
+
let x = l.roll.read(), E = l;
|
|
669
|
+
l.roll.play(L(b ? 0.96 : x.position, 1, x.velocity, Math.min(i, 180)), ie, () => this.rest(E));
|
|
670
|
+
}
|
|
671
|
+
}
|
|
672
|
+
for (let [d, a] of this.columns) {
|
|
673
|
+
if (e.has(d))
|
|
674
|
+
continue;
|
|
675
|
+
let p = c.get(d), w = S.get(a.token.identity), k = w ? e.get(w) : undefined;
|
|
676
|
+
if (a.x.play(L(p.position + t, k?.x ?? y.get(d) ?? p.position, p.velocity, s), ne), a.exiting)
|
|
677
|
+
continue;
|
|
678
|
+
if (a.exiting = true, k && i) {
|
|
679
|
+
let l = a.roll.read();
|
|
680
|
+
a.roll.play(L(l.position, 1.04, l.velocity, Math.min(i, 180)), ie);
|
|
681
|
+
}
|
|
682
|
+
let v = a.opacity.read();
|
|
683
|
+
a.opacity.play(L(v.position, 0, v.velocity, a.token.index === undefined ? Math.min(i, 180) : i * 0.65), re, () => {
|
|
684
|
+
if (!a.exiting)
|
|
685
|
+
return;
|
|
686
|
+
this.removeColumn(a), this.columns.delete(d);
|
|
687
|
+
});
|
|
688
|
+
}
|
|
689
|
+
this.enhanced = true, this.reset = false, this.displayed = this.target, this.host.dataset.rnReady = "";
|
|
690
|
+
}
|
|
691
|
+
removeColumn(e) {
|
|
692
|
+
this.blur?.remove(e.reel), this.finishEntry(e), e.x.cancel(), e.roll.cancel(), e.opacity.cancel(), e.element.remove();
|
|
693
|
+
}
|
|
694
|
+
refresh() {
|
|
695
|
+
if (this.destroyed)
|
|
696
|
+
return;
|
|
697
|
+
this.reset = true, this.prepare();
|
|
698
|
+
}
|
|
699
|
+
sizeChanged(e, t, n) {
|
|
700
|
+
if (this.measurementPending || !this.host.hasAttribute("data-rn-measuring"))
|
|
701
|
+
return false;
|
|
702
|
+
let i = this.sizes.get(e);
|
|
703
|
+
return !i || Math.abs(i.width - t) > 0.2 || Math.abs(i.height - n) > 0.2;
|
|
704
|
+
}
|
|
705
|
+
visibility(e) {
|
|
706
|
+
if (this.visible === e)
|
|
707
|
+
return;
|
|
708
|
+
if (this.visible = e, e || this.options.pauseOffscreen !== false)
|
|
709
|
+
this.refresh();
|
|
710
|
+
}
|
|
711
|
+
finish() {
|
|
712
|
+
if (this.destroyed)
|
|
713
|
+
return;
|
|
714
|
+
this.measurementPending = false;
|
|
715
|
+
for (let e of this.columns.values())
|
|
716
|
+
this.removeColumn(e);
|
|
717
|
+
this.columns.clear(), this.blur?.destroy(), this.blur = undefined, this.semantic.textContent = this.target.text, delete this.host.dataset.rnReady, delete this.host.dataset.rnMeasuring, delete this.host.dataset.rnTrend, this.enhanced = false, this.reset = true, this.displayed = this.target;
|
|
718
|
+
}
|
|
719
|
+
destroy() {
|
|
720
|
+
if (this.destroyed)
|
|
721
|
+
return;
|
|
722
|
+
this.finish(), this.destroyed = true;
|
|
723
|
+
for (let e of this.measures.values())
|
|
724
|
+
this.scheduler?.unwatch(e);
|
|
725
|
+
if (this.scheduler?.unwatch(this.measurement), this.scheduler?.remove(this, this.host), this.host.replaceChildren(this.host.ownerDocument.createTextNode(this.target.text)), !this.hadClass)
|
|
726
|
+
this.host.classList.remove("rn-root");
|
|
727
|
+
_.delete(this.host);
|
|
728
|
+
}
|
|
729
|
+
}
|
|
730
|
+
function rt(e, t) {
|
|
731
|
+
if (_.has(e))
|
|
732
|
+
throw Error("A rolling number is already mounted on this element");
|
|
733
|
+
let n = new oe(e, t, Fe);
|
|
734
|
+
return _.add(e), n;
|
|
735
|
+
}
|
|
3
736
|
|
|
4
737
|
// src/core/spring.ts
|
|
5
738
|
class Spring {
|
|
@@ -83,323 +816,247 @@ function springCss(value) {
|
|
|
83
816
|
return { easing, duration: ms };
|
|
84
817
|
}
|
|
85
818
|
|
|
86
|
-
// src/ui/
|
|
87
|
-
|
|
88
|
-
var clamp = (x, a, b) => Math.min(b, Math.max(a, x));
|
|
89
|
-
var sum = (xs) => xs.reduce((s, x) => s + x, 0);
|
|
90
|
-
var rowTarget = (width, height) => clamp(Math.round(Math.min(height / 3, width / 2.6)), 120, 420);
|
|
91
|
-
function justify(aspects, start, end, width, target, gap, tol) {
|
|
819
|
+
// src/ui/activity-model.ts
|
|
820
|
+
function flattenActivity(events, expanded) {
|
|
92
821
|
const rows = [];
|
|
93
|
-
|
|
94
|
-
|
|
95
|
-
|
|
96
|
-
|
|
97
|
-
|
|
98
|
-
|
|
99
|
-
|
|
100
|
-
|
|
101
|
-
|
|
102
|
-
|
|
103
|
-
|
|
104
|
-
|
|
105
|
-
|
|
106
|
-
|
|
107
|
-
}
|
|
108
|
-
closed = true;
|
|
109
|
-
break;
|
|
110
|
-
}
|
|
111
|
-
total += aspects[j];
|
|
112
|
-
h = next;
|
|
113
|
-
j++;
|
|
114
|
-
}
|
|
115
|
-
const slice = aspects.slice(i, j);
|
|
116
|
-
if (closed || h <= target * 1.25) {
|
|
117
|
-
rows.push({ start: i, end: j, h, ws: slice.map((a) => a * h) });
|
|
118
|
-
} else {
|
|
119
|
-
const free = width - (j - i - 1) * gap;
|
|
120
|
-
const f = free / (target * total);
|
|
121
|
-
const ws = slice.map((a) => a * target * (f - 1 <= tol ? f : 1));
|
|
122
|
-
rows.push({ start: i, end: j, h: target, ws });
|
|
123
|
-
}
|
|
124
|
-
i = j;
|
|
822
|
+
for (const event of events) {
|
|
823
|
+
const kids = event.children ?? [];
|
|
824
|
+
const group = kids.length > 0;
|
|
825
|
+
const open = group && expanded.has(event.id);
|
|
826
|
+
rows.push({ id: event.id, event, depth: 0, parent: null, group, expanded: open });
|
|
827
|
+
if (open)
|
|
828
|
+
for (const child of flatChildren(kids))
|
|
829
|
+
rows.push({
|
|
830
|
+
id: child.id,
|
|
831
|
+
event: child,
|
|
832
|
+
depth: 1,
|
|
833
|
+
parent: event.id,
|
|
834
|
+
group: false,
|
|
835
|
+
expanded: false
|
|
836
|
+
});
|
|
125
837
|
}
|
|
126
838
|
return rows;
|
|
127
839
|
}
|
|
128
|
-
function
|
|
129
|
-
const
|
|
130
|
-
|
|
131
|
-
|
|
132
|
-
|
|
133
|
-
|
|
134
|
-
const cost = Array.from({ length: k + 1 }, () => new Float64Array(n + 1).fill(Infinity));
|
|
135
|
-
const cut = Array.from({ length: k + 1 }, () => new Int32Array(n + 1));
|
|
136
|
-
cost[0][0] = 0;
|
|
137
|
-
for (let j = 1;j <= k; j++)
|
|
138
|
-
for (let i = j;i <= n; i++)
|
|
139
|
-
for (let m = j - 1;m < i; m++) {
|
|
140
|
-
const c = Math.max(cost[j - 1][m], prefix[i] - prefix[m]);
|
|
141
|
-
if (c < cost[j][i]) {
|
|
142
|
-
cost[j][i] = c;
|
|
143
|
-
cut[j][i] = m;
|
|
144
|
-
}
|
|
145
|
-
}
|
|
146
|
-
const counts = [];
|
|
147
|
-
for (let j = k, i = n;j > 0; j--) {
|
|
148
|
-
const m = cut[j][i];
|
|
149
|
-
counts.unshift(i - m);
|
|
150
|
-
i = m;
|
|
840
|
+
function flatChildren(events) {
|
|
841
|
+
const out = [];
|
|
842
|
+
for (const e of events) {
|
|
843
|
+
out.push(e);
|
|
844
|
+
if (e.children?.length)
|
|
845
|
+
out.push(...flatChildren(e.children));
|
|
151
846
|
}
|
|
152
|
-
return
|
|
847
|
+
return out;
|
|
153
848
|
}
|
|
154
|
-
function
|
|
155
|
-
const
|
|
156
|
-
|
|
849
|
+
function indexActivity(events) {
|
|
850
|
+
const index = new Map;
|
|
851
|
+
for (const event of events) {
|
|
852
|
+
index.set(event.id, { event, parent: null });
|
|
853
|
+
for (const child of flatChildren(event.children ?? []))
|
|
854
|
+
index.set(child.id, { event: child, parent: event.id });
|
|
855
|
+
}
|
|
856
|
+
return index;
|
|
857
|
+
}
|
|
858
|
+
var isActivityKey = (key) => key === "ArrowUp" || key === "ArrowDown" || key === "ArrowLeft" || key === "ArrowRight" || key === "Home" || key === "End";
|
|
859
|
+
function navigateActivity(rows, selected, key) {
|
|
860
|
+
if (!rows.length)
|
|
861
|
+
return {};
|
|
862
|
+
const i = selected === null ? -1 : rows.findIndex((r) => r.id === selected);
|
|
863
|
+
const row = i >= 0 ? rows[i] : undefined;
|
|
864
|
+
switch (key) {
|
|
865
|
+
case "ArrowDown":
|
|
866
|
+
return { select: rows[i < 0 ? 0 : Math.min(rows.length - 1, i + 1)].id };
|
|
867
|
+
case "ArrowUp":
|
|
868
|
+
return { select: rows[i < 0 ? rows.length - 1 : Math.max(0, i - 1)].id };
|
|
869
|
+
case "Home":
|
|
870
|
+
return { select: rows[0].id };
|
|
871
|
+
case "End":
|
|
872
|
+
return { select: rows[rows.length - 1].id };
|
|
873
|
+
case "ArrowRight":
|
|
874
|
+
if (!row?.group)
|
|
875
|
+
return {};
|
|
876
|
+
if (!row.expanded)
|
|
877
|
+
return { toggle: { id: row.id, expanded: true } };
|
|
878
|
+
return rows[i + 1]?.parent === row.id ? { select: rows[i + 1].id } : {};
|
|
879
|
+
case "ArrowLeft":
|
|
880
|
+
if (!row)
|
|
881
|
+
return {};
|
|
882
|
+
if (row.group && row.expanded)
|
|
883
|
+
return { toggle: { id: row.id, expanded: false } };
|
|
884
|
+
return row.parent !== null ? { select: row.parent } : {};
|
|
885
|
+
}
|
|
886
|
+
}
|
|
887
|
+
function takesActivityKey(key, result, rows) {
|
|
888
|
+
if (result.select !== undefined || result.toggle !== undefined)
|
|
889
|
+
return true;
|
|
890
|
+
return rows > 0 && key !== "ArrowLeft" && key !== "ArrowRight";
|
|
891
|
+
}
|
|
892
|
+
function keepSelection(rows, selected, parentOf) {
|
|
893
|
+
if (selected === null)
|
|
157
894
|
return null;
|
|
158
|
-
|
|
159
|
-
|
|
160
|
-
|
|
161
|
-
|
|
162
|
-
let at = 0;
|
|
163
|
-
const groups = counts.map((c) => aspects.slice(at, at += c));
|
|
164
|
-
const heights = groups.map((g) => (w - (g.length - 1) * gap) / sum(g));
|
|
165
|
-
const budget = h - (r - 1) * gap;
|
|
166
|
-
if (budget <= 0 || heights.some((x) => x <= 0))
|
|
167
|
-
break;
|
|
168
|
-
const s = Math.min(1, budget / sum(heights));
|
|
169
|
-
const fill = s < 1 && 1 / s - 1 <= tol ? 1 / s : 1;
|
|
170
|
-
const rects = [];
|
|
171
|
-
let y = 0;
|
|
172
|
-
let blockW = 0;
|
|
173
|
-
groups.forEach((g, k) => {
|
|
174
|
-
const rh = heights[k] * s;
|
|
175
|
-
const rowW = sum(g.map((a) => a * rh * fill)) + (g.length - 1) * gap;
|
|
176
|
-
blockW = Math.max(blockW, rowW);
|
|
177
|
-
let x = 0;
|
|
178
|
-
for (const a of g) {
|
|
179
|
-
rects.push({ x, y, w: a * rh * fill, h: rh });
|
|
180
|
-
x += a * rh * fill + gap;
|
|
181
|
-
}
|
|
182
|
-
y += rh + gap;
|
|
183
|
-
});
|
|
184
|
-
let i = 0;
|
|
185
|
-
groups.forEach((g, k) => {
|
|
186
|
-
const rowW = sum(g.map((a) => a * heights[k] * s * fill)) + (g.length - 1) * gap;
|
|
187
|
-
const dx = (blockW - rowW) / 2;
|
|
188
|
-
for (let m = 0;m < g.length; m++, i++) {
|
|
189
|
-
const q = rects[i];
|
|
190
|
-
rects[i] = { ...q, x: q.x + dx };
|
|
191
|
-
}
|
|
192
|
-
});
|
|
193
|
-
const area = sum(rects.map((q) => q.w * q.h));
|
|
194
|
-
const block = {
|
|
195
|
-
rects,
|
|
196
|
-
w: blockW,
|
|
197
|
-
h: y - gap,
|
|
198
|
-
area,
|
|
199
|
-
crop: fill - 1,
|
|
200
|
-
minH: Math.min(...rects.map((q) => q.h))
|
|
201
|
-
};
|
|
202
|
-
if (!best || block.area > best.area * 1.0001)
|
|
203
|
-
best = block;
|
|
204
|
-
}
|
|
205
|
-
return best;
|
|
895
|
+
if (rows.some((r) => r.id === selected))
|
|
896
|
+
return selected;
|
|
897
|
+
const parent = parentOf(selected);
|
|
898
|
+
return parent && rows.some((r) => r.id === parent) ? parent : null;
|
|
206
899
|
}
|
|
207
|
-
|
|
208
|
-
|
|
209
|
-
|
|
210
|
-
|
|
211
|
-
|
|
212
|
-
|
|
213
|
-
|
|
214
|
-
|
|
215
|
-
if (!restBlock)
|
|
216
|
-
return;
|
|
217
|
-
const [rx, ry] = restAt(restBlock);
|
|
218
|
-
const others = offset(restBlock.rects, rx, ry);
|
|
219
|
-
const rects = [...others.slice(0, hero), heroRect, ...others.slice(hero)];
|
|
220
|
-
const area = heroRect.w * heroRect.h + restBlock.area;
|
|
221
|
-
if (!best || area > best.area * 1.0001)
|
|
222
|
-
best = { rects, w: bw, h: bh, area, crop: restBlock.crop, minH: restBlock.minH };
|
|
223
|
-
};
|
|
224
|
-
for (let f = 0.4;f <= 0.761; f += 0.04) {
|
|
225
|
-
{
|
|
226
|
-
const hw = Math.min(w * f, h * a);
|
|
227
|
-
const hh = hw / a;
|
|
228
|
-
const block = rowsBlock(rest, w - hw - gap, h, gap, tol);
|
|
229
|
-
if (block) {
|
|
230
|
-
const bw = hw + gap + block.w;
|
|
231
|
-
const bh = Math.max(hh, block.h);
|
|
232
|
-
place({ x: 0, y: (bh - hh) / 2, w: hw, h: hh }, block, (b) => [hw + gap, (bh - b.h) / 2], bw, bh);
|
|
233
|
-
}
|
|
234
|
-
}
|
|
235
|
-
{
|
|
236
|
-
const hh = Math.min(h * f, w / a);
|
|
237
|
-
const hw = hh * a;
|
|
238
|
-
const block = rowsBlock(rest, w, h - hh - gap, gap, tol);
|
|
239
|
-
if (block) {
|
|
240
|
-
const bw = Math.max(hw, block.w);
|
|
241
|
-
place({ x: (bw - hw) / 2, y: 0, w: hw, h: hh }, block, (b) => [(bw - b.w) / 2, hh + gap], bw, hh + gap + block.h);
|
|
242
|
-
}
|
|
243
|
-
}
|
|
244
|
-
}
|
|
245
|
-
return best;
|
|
900
|
+
function showsSelection(showing, input, keys = "focus") {
|
|
901
|
+
if (input === "key")
|
|
902
|
+
return true;
|
|
903
|
+
if (input === "pointer")
|
|
904
|
+
return false;
|
|
905
|
+
if (input === "blur")
|
|
906
|
+
return keys === "page" ? showing : false;
|
|
907
|
+
return showing;
|
|
246
908
|
}
|
|
247
|
-
function
|
|
248
|
-
const
|
|
249
|
-
const
|
|
250
|
-
|
|
251
|
-
|
|
252
|
-
|
|
253
|
-
|
|
254
|
-
const h = Math.max(1, vh - pad * 2);
|
|
255
|
-
const as = aspects.map((a) => Number.isFinite(a) && a > 0 ? clamp(a, 0.05, 20) : 1);
|
|
256
|
-
const empty = {
|
|
257
|
-
mode: "rows",
|
|
258
|
-
rects: [],
|
|
259
|
-
width: vw,
|
|
260
|
-
height: 0,
|
|
261
|
-
coverage: 0,
|
|
262
|
-
crop: 0,
|
|
263
|
-
headers: []
|
|
909
|
+
function diffActivity(before, after) {
|
|
910
|
+
const was = new Set(before);
|
|
911
|
+
const now = new Set(after);
|
|
912
|
+
return {
|
|
913
|
+
entered: after.filter((id) => !was.has(id)),
|
|
914
|
+
left: before.filter((id) => !now.has(id)),
|
|
915
|
+
kept: after.filter((id) => was.has(id))
|
|
264
916
|
};
|
|
265
|
-
if (!as.length || vw <= 0)
|
|
266
|
-
return empty;
|
|
267
|
-
const grouped = layout.groups?.some((g) => g !== undefined) ?? false;
|
|
268
|
-
const minTile = layout.minTile ?? Math.max(80, vh / 6);
|
|
269
|
-
if ((layout.fit ?? true) && !grouped && as.length <= FIT_MAX && vh > 0) {
|
|
270
|
-
const rows = rowsBlock(as, w, h, gap, tol);
|
|
271
|
-
let pick = rows && rows.minH >= minTile ? { mode: "fit", block: rows } : null;
|
|
272
|
-
const asked = layout.hero !== undefined && layout.hero >= 0 && layout.hero < as.length;
|
|
273
|
-
const hero = as.length > 2 ? heroBlock(as, asked ? layout.hero : 0, w, h, gap, tol) : null;
|
|
274
|
-
if (hero && hero.minH >= minTile && (asked || !pick || hero.area > pick.block.area * 1.04))
|
|
275
|
-
pick = { mode: "hero", block: hero };
|
|
276
|
-
if (pick) {
|
|
277
|
-
const { block } = pick;
|
|
278
|
-
const rects = offset(block.rects, pad + (w - block.w) / 2, pad + (h - block.h) / 2);
|
|
279
|
-
return {
|
|
280
|
-
mode: pick.mode,
|
|
281
|
-
rects,
|
|
282
|
-
width: vw,
|
|
283
|
-
height: vh,
|
|
284
|
-
coverage: block.area / (vw * vh),
|
|
285
|
-
crop: block.crop,
|
|
286
|
-
headers: []
|
|
287
|
-
};
|
|
288
|
-
}
|
|
289
|
-
}
|
|
290
|
-
const target = layout.rowHeight ?? rowTarget(vw, vh);
|
|
291
|
-
const header = layout.header ?? 28;
|
|
292
|
-
const rects = new Array(as.length);
|
|
293
|
-
const headers = [];
|
|
294
|
-
let y = pad;
|
|
295
|
-
let crop = 0;
|
|
296
|
-
let area = 0;
|
|
297
|
-
let i = 0;
|
|
298
|
-
while (i < as.length) {
|
|
299
|
-
const group = layout.groups?.[i];
|
|
300
|
-
let end = i + 1;
|
|
301
|
-
while (end < as.length && layout.groups?.[end] === group)
|
|
302
|
-
end++;
|
|
303
|
-
if (group !== undefined) {
|
|
304
|
-
if (i > 0)
|
|
305
|
-
y += header / 2;
|
|
306
|
-
headers.push({ group, y, index: i });
|
|
307
|
-
y += header;
|
|
308
|
-
}
|
|
309
|
-
for (const row of justify(as, i, end, w, target, gap, tol)) {
|
|
310
|
-
let x = pad;
|
|
311
|
-
row.ws.forEach((rw, k) => {
|
|
312
|
-
rects[row.start + k] = { x, y, w: rw, h: row.h };
|
|
313
|
-
crop = Math.max(crop, rw / (as[row.start + k] * row.h) - 1);
|
|
314
|
-
area += rw * row.h;
|
|
315
|
-
x += rw + gap;
|
|
316
|
-
});
|
|
317
|
-
y += row.h + gap;
|
|
318
|
-
}
|
|
319
|
-
i = end;
|
|
320
|
-
}
|
|
321
|
-
const height = y - gap + pad;
|
|
322
|
-
return { mode: "rows", rects, width: vw, height, coverage: area / (vw * height), crop, headers };
|
|
323
917
|
}
|
|
324
|
-
function
|
|
325
|
-
const
|
|
326
|
-
|
|
327
|
-
|
|
328
|
-
|
|
329
|
-
|
|
330
|
-
|
|
331
|
-
|
|
332
|
-
|
|
333
|
-
|
|
334
|
-
|
|
335
|
-
hi = mid;
|
|
336
|
-
}
|
|
918
|
+
function stackRows(heights, top = 0, gap = 0) {
|
|
919
|
+
const y = [];
|
|
920
|
+
let at = top;
|
|
921
|
+
heights.forEach((h, i) => {
|
|
922
|
+
y.push(at);
|
|
923
|
+
at += h + (i < heights.length - 1 ? gap : 0);
|
|
924
|
+
});
|
|
925
|
+
return { y, bottom: at };
|
|
926
|
+
}
|
|
927
|
+
function railSegments(nodes, from, to) {
|
|
928
|
+
const sorted = [...nodes].sort((a, b) => a.y - b.y);
|
|
337
929
|
const out = [];
|
|
338
|
-
for (let i =
|
|
339
|
-
|
|
930
|
+
for (let i = 0;i < sorted.length - 1; i++) {
|
|
931
|
+
const a = sorted[i];
|
|
932
|
+
const b = sorted[i + 1];
|
|
933
|
+
const top = Math.max(from, a.y + a.r);
|
|
934
|
+
const bottom = Math.min(to, b.y - b.r);
|
|
935
|
+
if (bottom - top > 0.25)
|
|
936
|
+
out.push([top, bottom]);
|
|
937
|
+
}
|
|
340
938
|
return out;
|
|
341
939
|
}
|
|
342
|
-
|
|
343
|
-
|
|
344
|
-
|
|
345
|
-
|
|
346
|
-
|
|
347
|
-
const
|
|
348
|
-
const
|
|
349
|
-
|
|
350
|
-
const right = dir === "right";
|
|
351
|
-
let best = -1;
|
|
352
|
-
let bestD = Number.POSITIVE_INFINITY;
|
|
353
|
-
rects.forEach((r, i) => {
|
|
354
|
-
if (i === from || !overlapY(f, r))
|
|
355
|
-
return;
|
|
356
|
-
const d = (r.x + r.w / 2 - fx) * (right ? 1 : -1);
|
|
357
|
-
if (d > 1 && d < bestD) {
|
|
358
|
-
bestD = d;
|
|
359
|
-
best = i;
|
|
360
|
-
}
|
|
361
|
-
});
|
|
362
|
-
if (best >= 0)
|
|
363
|
-
return best;
|
|
364
|
-
return clamp(from + (right ? 1 : -1), 0, rects.length - 1);
|
|
365
|
-
}
|
|
366
|
-
const down = dir === "down";
|
|
367
|
-
let edge = Number.POSITIVE_INFINITY;
|
|
368
|
-
for (const r of rects) {
|
|
369
|
-
const d = (r.y + r.h / 2 - fy) * (down ? 1 : -1);
|
|
370
|
-
if (d > f.h * 0.25 && !overlapY(f, r))
|
|
371
|
-
edge = Math.min(edge, down ? r.y : -(r.y + r.h));
|
|
372
|
-
}
|
|
373
|
-
if (!Number.isFinite(edge))
|
|
374
|
-
return from;
|
|
375
|
-
let best = from;
|
|
376
|
-
let bestD = Number.POSITIVE_INFINITY;
|
|
377
|
-
rects.forEach((r, i) => {
|
|
378
|
-
const d = (r.y + r.h / 2 - fy) * (down ? 1 : -1);
|
|
379
|
-
if (i === from || d <= f.h * 0.25 || overlapY(f, r))
|
|
380
|
-
return;
|
|
381
|
-
const near = down ? r.y - edge : -(r.y + r.h) - edge;
|
|
382
|
-
if (near > Math.min(r.h, f.h) * 0.5)
|
|
383
|
-
return;
|
|
384
|
-
const dx = Math.abs(r.x + r.w / 2 - fx);
|
|
385
|
-
if (dx < bestD) {
|
|
386
|
-
bestD = dx;
|
|
387
|
-
best = i;
|
|
388
|
-
}
|
|
389
|
-
});
|
|
390
|
-
return best;
|
|
940
|
+
function holdScroll(hold, height, view) {
|
|
941
|
+
if (hold.from <= 0)
|
|
942
|
+
return { scroll: 0, space: height };
|
|
943
|
+
const end = Math.min(hold.from, Math.max(0, hold.h1 - view));
|
|
944
|
+
const span = hold.h1 - hold.h0;
|
|
945
|
+
const p = Math.abs(span) < 0.5 ? 1 : Math.min(1, Math.max(0, (height - hold.h0) / span));
|
|
946
|
+
const scroll = hold.from + (end - hold.from) * p;
|
|
947
|
+
return { scroll, space: Math.max(height, scroll + view) };
|
|
391
948
|
}
|
|
392
|
-
function
|
|
393
|
-
|
|
394
|
-
|
|
395
|
-
const
|
|
396
|
-
const
|
|
397
|
-
|
|
949
|
+
function formatDuration(duration) {
|
|
950
|
+
if (typeof duration === "string")
|
|
951
|
+
return duration;
|
|
952
|
+
const s = Math.max(0, Math.round(duration));
|
|
953
|
+
const h = Math.floor(s / 3600);
|
|
954
|
+
const m = Math.floor(s % 3600 / 60);
|
|
955
|
+
const sec = String(s % 60).padStart(2, "0");
|
|
956
|
+
return h ? `${h}:${String(m).padStart(2, "0")}:${sec}` : `${m}:${sec}`;
|
|
398
957
|
}
|
|
399
|
-
function
|
|
400
|
-
|
|
401
|
-
|
|
958
|
+
function groupTitle(count) {
|
|
959
|
+
return `${count} update${count === 1 ? "" : "s"}`;
|
|
960
|
+
}
|
|
961
|
+
|
|
962
|
+
// src/panel/rolling-css.ts
|
|
963
|
+
var rolling_css_default = `.rn-root {
|
|
964
|
+
display: inline-block;
|
|
965
|
+
position: relative;
|
|
966
|
+
white-space: pre;
|
|
967
|
+
unicode-bidi: isolate;
|
|
968
|
+
vertical-align: baseline;
|
|
969
|
+
}
|
|
970
|
+
.rn-root > .rn-measure,
|
|
971
|
+
.rn-root > .rn-visual { display: none; }
|
|
972
|
+
.rn-root[data-rn-measuring] > .rn-measure {
|
|
973
|
+
display: inline-flex;
|
|
974
|
+
position: relative;
|
|
975
|
+
visibility: hidden;
|
|
976
|
+
white-space: pre;
|
|
977
|
+
padding: 0;
|
|
978
|
+
border: 0;
|
|
979
|
+
}
|
|
980
|
+
.rn-token { display: inline-block; padding: 0; border: 0; }
|
|
981
|
+
.rn-root[data-rn-measuring] > .rn-value,
|
|
982
|
+
.rn-react[data-rn-hydrated] > .rn-semantic,
|
|
983
|
+
.rn-solid[data-rn-hydrated] > .rn-semantic {
|
|
984
|
+
position: absolute;
|
|
985
|
+
inset: 0;
|
|
986
|
+
padding: 0;
|
|
987
|
+
margin: 0;
|
|
988
|
+
color: transparent;
|
|
989
|
+
z-index: 1;
|
|
990
|
+
}
|
|
991
|
+
/* A single native text layer is selectable; decorative reels never enter a copy. */
|
|
992
|
+
.rn-value::selection, .rn-semantic::selection {
|
|
993
|
+
color: HighlightText;
|
|
994
|
+
background: Highlight;
|
|
995
|
+
}
|
|
996
|
+
.rn-root[data-rn-ready] > .rn-visual {
|
|
997
|
+
display: block;
|
|
998
|
+
position: absolute;
|
|
999
|
+
inset: 0;
|
|
1000
|
+
pointer-events: none;
|
|
1001
|
+
user-select: none;
|
|
1002
|
+
}
|
|
1003
|
+
.rn-slot {
|
|
1004
|
+
position: absolute;
|
|
1005
|
+
left: 0;
|
|
1006
|
+
overflow: hidden;
|
|
1007
|
+
/* Space for italic overhang without changing the measured advance width. */
|
|
1008
|
+
box-sizing: content-box;
|
|
1009
|
+
padding-inline: 0.15em;
|
|
1010
|
+
margin-inline-start: -0.15em;
|
|
1011
|
+
-webkit-mask-image: var(--rn-mask, linear-gradient(
|
|
1012
|
+
to bottom,
|
|
1013
|
+
transparent,
|
|
1014
|
+
#000 var(--rn-edge-fade, 0.12em),
|
|
1015
|
+
#000 calc(100% - var(--rn-edge-fade, 0.12em)),
|
|
1016
|
+
transparent
|
|
1017
|
+
));
|
|
1018
|
+
mask-image: var(--rn-mask, linear-gradient(
|
|
1019
|
+
to bottom,
|
|
1020
|
+
transparent,
|
|
1021
|
+
#000 var(--rn-edge-fade, 0.12em),
|
|
1022
|
+
#000 calc(100% - var(--rn-edge-fade, 0.12em)),
|
|
1023
|
+
transparent
|
|
1024
|
+
));
|
|
1025
|
+
-webkit-mask-repeat: no-repeat;
|
|
1026
|
+
mask-repeat: no-repeat;
|
|
402
1027
|
}
|
|
1028
|
+
@supports (overflow: clip) {
|
|
1029
|
+
.rn-slot { overflow-x: visible; overflow-y: clip; }
|
|
1030
|
+
}
|
|
1031
|
+
.rn-slot:not([data-rn-wheel]) {
|
|
1032
|
+
overflow: visible;
|
|
1033
|
+
-webkit-mask-image: none;
|
|
1034
|
+
mask-image: none;
|
|
1035
|
+
}
|
|
1036
|
+
.rn-enter, .rn-reel, .rn-face { display: block; }
|
|
1037
|
+
.rn-reel { position: relative; }
|
|
1038
|
+
.rn-ink { display: block; position: absolute; inset: 0; will-change: transform; }
|
|
1039
|
+
/* Safari's SVG filter must rasterize the smear's descendants into its input. */
|
|
1040
|
+
.rn-smear .rn-ink { will-change: auto; }
|
|
1041
|
+
/* Each turning half carries its own perspective transform, not a shared 3D scene. */
|
|
1042
|
+
/* The slot acts as the bezel: the card stays vertically framed while it hinges. */
|
|
1043
|
+
.rn-slot[data-rn-flap] { -webkit-mask-image: none; mask-image: none; }
|
|
1044
|
+
/* Hinged faces must occlude the cards behind them, including with default CSS. */
|
|
1045
|
+
:where(.rn-slot[data-rn-flap]) .rn-face { background: var(--rn-flap-background, Canvas); }
|
|
1046
|
+
.rn-flap { position: absolute; inset: 0; transform-origin: 50% 50%; backface-visibility: hidden; }
|
|
1047
|
+
/* --rn-crease is the visible hinge gap between the two halves. */
|
|
1048
|
+
.rn-flap-top { clip-path: inset(0 0 calc(50% + var(--rn-crease, 1px) / 2) 0); }
|
|
1049
|
+
.rn-flap-bottom { clip-path: inset(calc(50% + var(--rn-crease, 1px) / 2) 0 0 0); }
|
|
1050
|
+
.rn-sharp, .rn-smear { display: block; }
|
|
1051
|
+
.rn-smear { position: absolute; inset: 0; pointer-events: none; }
|
|
1052
|
+
.rn-blur-defs { position: absolute; width: 0; height: 0; overflow: hidden; }
|
|
1053
|
+
.rn-face { text-align: center; white-space: pre; }
|
|
1054
|
+
.rn-react, .rn-solid { display: inline-block; position: relative; white-space: pre; unicode-bidi: isolate; }
|
|
1055
|
+
.rn-mount { display: inline-block; user-select: none; }
|
|
1056
|
+
.rn-mount .rn-value { pointer-events: none; }
|
|
1057
|
+
.rn-react:not([data-rn-hydrated]) > .rn-mount,
|
|
1058
|
+
.rn-solid:not([data-rn-hydrated]) > .rn-mount { display: none; }
|
|
1059
|
+
`;
|
|
403
1060
|
|
|
404
1061
|
// src/core/theme.ts
|
|
405
1062
|
var block = (tokens) => Object.entries(tokens).map(([k, v]) => `${k.startsWith("--") || k === "color-scheme" ? k : `--k-${k}`}:${v}`).join(";");
|
|
@@ -493,6 +1150,88 @@ var problemsPreset = {
|
|
|
493
1150
|
}
|
|
494
1151
|
};
|
|
495
1152
|
|
|
1153
|
+
// src/ui/activity-css.ts
|
|
1154
|
+
var open = springEasing(2 * Math.PI / 0.22, 1);
|
|
1155
|
+
var activityCss = `
|
|
1156
|
+
:where(.knobs-activity,.knobs-activity *){box-sizing:border-box}
|
|
1157
|
+
:where(.knobs-activity){color-scheme:dark}
|
|
1158
|
+
.knobs-ui.knobs-activity{position:relative;display:block;height:100%;overflow:auto;overflow-anchor:none;outline:none;scrollbar-width:none;overscroll-behavior:contain;user-select:none;-webkit-user-select:none;
|
|
1159
|
+
font:var(--ku-font-weight) var(--ku-font-size)/1.35 var(--ku-font);color:var(--ku-text);text-align:left;letter-spacing:normal;-webkit-font-smoothing:antialiased;
|
|
1160
|
+
--ka-gutter:var(--k-activity-gutter,28px);--ka-row:var(--k-activity-row,var(--ku-row));--ka-row-child:var(--k-activity-row-child,calc(var(--ka-row) - 6px));
|
|
1161
|
+
--ka-line:var(--k-activity-line,var(--ku-line-hi));--ka-select:var(--k-activity-select,var(--ku-wash-hi));--ka-hover:var(--k-activity-hover,var(--ku-wash));
|
|
1162
|
+
--ka-you:var(--k-activity-you,light-dark(#007aff,#0a84ff));--ka-agent:var(--k-activity-agent,var(--ku-label));--ka-proposal:var(--k-activity-proposal,var(--ku-text));
|
|
1163
|
+
--ka-review:var(--k-activity-review,var(--ku-text));--ka-pr:var(--k-activity-pr,light-dark(#8944ab,#bf5af2));--ka-build:var(--k-activity-build,var(--ku-label));
|
|
1164
|
+
--ka-blocked:var(--k-activity-blocked,var(--ku-warn));--ka-shipped:var(--k-activity-shipped,light-dark(#248a3d,#30d158));--ka-open-ease:${open.easing};--ka-open-ms:${open.ms}ms}
|
|
1165
|
+
.knobs-activity [hidden]{display:none!important}
|
|
1166
|
+
/* Clipped below: the content grows on a spring, so a new last row shows as the rail makes room. */
|
|
1167
|
+
.knobs-activity-space{position:relative;width:100%;overflow-x:visible;overflow-y:clip;pointer-events:none}
|
|
1168
|
+
.knobs-activity-line{position:absolute;top:0;left:calc(var(--ku-row-x) + var(--ka-gutter) / 2 - 1px);width:2px;height:100%;overflow:visible;pointer-events:none;z-index:1}
|
|
1169
|
+
.knobs-activity-line path{fill:none;stroke:var(--ka-line);stroke-width:1px}
|
|
1170
|
+
/* The selection's tint: a wash that glides between rows, shown only once keys move it. It leaves
|
|
1171
|
+
in 100 ms when the pointer is used, and comes back at once. */
|
|
1172
|
+
.knobs-activity-hl{position:absolute;left:var(--ku-row-x);right:var(--ku-row-x);top:0;height:0;border-radius:var(--ku-row-radius);background:var(--ka-select);opacity:0;
|
|
1173
|
+
transition:opacity 100ms ease-out;pointer-events:none;will-change:transform}
|
|
1174
|
+
.knobs-activity[data-keys] .knobs-activity-hl{opacity:1;transition:none}
|
|
1175
|
+
.knobs-activity-row{position:absolute;left:var(--ku-row-x);right:var(--ku-row-x);top:0;display:flex;align-items:center;height:var(--ka-row);padding-right:calc(var(--ku-inset) - 4px);
|
|
1176
|
+
color:var(--ku-text);white-space:nowrap;cursor:default;will-change:transform;transform-origin:calc(var(--ka-gutter) / 2) 50%;pointer-events:auto;--ka-tint:var(--ka-agent)}
|
|
1177
|
+
.knobs-activity-row[data-leaving]{pointer-events:none}
|
|
1178
|
+
.knobs-activity-row[data-depth="1"]{height:var(--ka-row-child)}
|
|
1179
|
+
.knobs-activity-row>*{position:relative}
|
|
1180
|
+
/* Hover: in at once, out over 100 ms; not while keys drive the selection. */
|
|
1181
|
+
.knobs-activity-row::before{content:"";position:absolute;inset:0;border-radius:var(--ku-row-radius);background:var(--ka-hover);opacity:0;transition:opacity calc(var(--k-hover-out,100) * 1ms) ease-out;pointer-events:none}
|
|
1182
|
+
.knobs-activity:not([data-keys]) .knobs-activity-row:not([data-leaving]):hover::before{opacity:1;transition-duration:calc(var(--k-hover-in,0) * 1ms)}
|
|
1183
|
+
.knobs-activity-row[data-kind="you"]{--ka-tint:var(--ka-you)}
|
|
1184
|
+
.knobs-activity-row[data-kind="agent"]{--ka-tint:var(--ka-agent)}
|
|
1185
|
+
.knobs-activity-row[data-kind="proposal"]{--ka-tint:var(--ka-proposal)}
|
|
1186
|
+
.knobs-activity-row[data-kind="review"]{--ka-tint:var(--ka-review)}
|
|
1187
|
+
.knobs-activity-row[data-kind="pr"]{--ka-tint:var(--ka-pr)}
|
|
1188
|
+
.knobs-activity-row[data-kind="build"]{--ka-tint:var(--ka-build)}
|
|
1189
|
+
.knobs-activity-row[data-kind="blocked"]{--ka-tint:var(--ka-blocked)}
|
|
1190
|
+
.knobs-activity-row[data-kind="shipped"]{--ka-tint:var(--ka-shipped)}
|
|
1191
|
+
/* The node: the kind's icon on the line (which stops clear of it), or a small dot inside a group. */
|
|
1192
|
+
.knobs-activity-node{flex:none;display:flex;align-items:center;justify-content:center;width:var(--ka-gutter);height:100%;color:var(--ka-tint)}
|
|
1193
|
+
.knobs-activity-node svg{display:block;width:14px;height:14px}
|
|
1194
|
+
.knobs-activity-glyph{display:flex;align-items:center;justify-content:center;width:14px;height:14px;font-size:calc(var(--ku-font-size) - 1px);line-height:1}
|
|
1195
|
+
.knobs-activity-node::before{content:"";position:absolute;left:50%;top:50%;width:22px;height:22px;margin:-11px 0 0 -11px;border-radius:50%;background:color-mix(in srgb,var(--ka-tint) 15%,transparent);
|
|
1196
|
+
opacity:0;scale:.8;transition:opacity .16s ease-out,scale var(--ka-open-ms) var(--ka-open-ease)}
|
|
1197
|
+
.knobs-activity-row[aria-selected="true"] .knobs-activity-node::before{opacity:1;scale:1}
|
|
1198
|
+
.knobs-activity-node::after{content:"";position:absolute;left:50%;top:50%;width:22px;height:22px;margin:-11px 0 0 -11px;border-radius:50%;box-shadow:inset 0 0 0 1px color-mix(in srgb,var(--ka-tint) 55%,transparent);opacity:0;pointer-events:none}
|
|
1199
|
+
.knobs-activity-row[data-current] .knobs-activity-node::after{opacity:1;animation:knobs-activity-breathe 2.6s ease-in-out infinite}
|
|
1200
|
+
@keyframes knobs-activity-breathe{0%,100%{box-shadow:inset 0 0 0 1px color-mix(in srgb,var(--ka-tint) 55%,transparent),0 0 0 0 color-mix(in srgb,var(--ka-tint) 0%,transparent)}
|
|
1201
|
+
50%{box-shadow:inset 0 0 0 1px color-mix(in srgb,var(--ka-tint) 70%,transparent),0 0 0 3px color-mix(in srgb,var(--ka-tint) 14%,transparent)}}
|
|
1202
|
+
.knobs-activity-dot{display:block;width:5px;height:5px;border-radius:50%;background:currentColor}
|
|
1203
|
+
.knobs-activity-row[data-depth="1"] .knobs-activity-node::before,.knobs-activity-row[data-depth="1"] .knobs-activity-node::after{width:13px;height:13px;margin:-6.5px 0 0 -6.5px}
|
|
1204
|
+
/* The words: who in the text colour, then the title, one line that fades where it's cut off. */
|
|
1205
|
+
.knobs-activity-main{flex:1;min-width:0;overflow:hidden;white-space:nowrap}
|
|
1206
|
+
.knobs-activity-actor{margin-right:6px;font-weight:var(--ku-label-weight);color:var(--ku-text)}
|
|
1207
|
+
.knobs-activity-actor:empty{display:none}
|
|
1208
|
+
.knobs-activity-title{color:var(--ku-label)}
|
|
1209
|
+
.knobs-activity-row[data-depth="1"] .knobs-activity-main{font-size:var(--ku-text-meta)}
|
|
1210
|
+
.knobs-activity-row[data-depth="1"] .knobs-activity-actor{font-weight:var(--ku-font-weight);color:var(--ku-label)}
|
|
1211
|
+
.knobs-activity-row[data-depth="1"] .knobs-activity-title{color:var(--ku-dim)}
|
|
1212
|
+
.knobs-activity-row[aria-selected="true"] .knobs-activity-title{color:var(--ku-text)}
|
|
1213
|
+
.knobs-activity-row[data-dimmed] :is(.knobs-activity-main,.knobs-activity-meta){opacity:.55}
|
|
1214
|
+
.knobs-activity-row[data-dimmed] .knobs-activity-node{opacity:.6}
|
|
1215
|
+
/* Trailing: thumbnails, a duration chip, the group's chevron and the time. */
|
|
1216
|
+
.knobs-activity-meta{flex:none;display:flex;align-items:center;gap:6px;margin-left:8px;color:var(--ku-dim);font-size:var(--ku-text-meta);font-variant-numeric:tabular-nums}
|
|
1217
|
+
.knobs-activity-meta:empty{display:none}
|
|
1218
|
+
.knobs-activity-thumbs{display:flex;gap:3px}
|
|
1219
|
+
.knobs-activity-thumbs img{display:block;width:18px;height:18px;border-radius:4px;object-fit:cover;background:var(--ku-surface);box-shadow:inset 0 0 0 .5px var(--ku-line)}
|
|
1220
|
+
.knobs-activity-row[data-depth="1"] .knobs-activity-thumbs img{width:15px;height:15px}
|
|
1221
|
+
:where(.knobs-ui) .knobs-activity-chip{all:revert;box-sizing:border-box;display:inline-flex;align-items:center;gap:4px;height:18px;padding:0 6px 0 5px;border:0;margin:0;border-radius:9px;
|
|
1222
|
+
background:var(--ku-surface);color:var(--ku-label);font:inherit;font-variant-numeric:tabular-nums;letter-spacing:inherit;outline:none;transition:background calc(var(--k-hover-out,100) * 1ms) ease-out}
|
|
1223
|
+
.knobs-activity-chip svg{width:8px;height:8px}
|
|
1224
|
+
button.knobs-activity-chip{cursor:pointer}
|
|
1225
|
+
button.knobs-activity-chip:hover{background:var(--ku-surface-hi);color:var(--ku-text);transition-duration:0s}
|
|
1226
|
+
.knobs-activity-caret{display:flex;width:12px;height:12px;color:var(--ku-dim);transition:rotate .18s var(--ku-ease)}
|
|
1227
|
+
.knobs-activity-caret svg{width:10px;height:10px;margin:1px}
|
|
1228
|
+
.knobs-activity-row[aria-expanded="true"] .knobs-activity-caret{rotate:90deg;transition:rotate var(--ka-open-ms) var(--ka-open-ease)}
|
|
1229
|
+
.knobs-activity-time{min-width:2ch;text-align:right;white-space:nowrap}
|
|
1230
|
+
.knobs-activity-time:empty{display:none}
|
|
1231
|
+
.knobs-activity-empty{padding:10px var(--ku-inset);color:var(--ku-dim)}
|
|
1232
|
+
@media(prefers-reduced-motion:reduce){.knobs-activity :where(*,::before,::after){transition:none!important;animation:none!important}}
|
|
1233
|
+
`;
|
|
1234
|
+
|
|
496
1235
|
// src/ui/bento-css.ts
|
|
497
1236
|
var bentoCss = `
|
|
498
1237
|
.knobs-ui.knobs-bento{--ku-bento-radius:var(--k-ui-bento-radius,max(0px,calc(var(--ku-radius) - 5px)));
|
|
@@ -625,6 +1364,59 @@ var overlayCss = `
|
|
|
625
1364
|
.knobs-toast .knobs-toast-action[hidden]{display:none}
|
|
626
1365
|
`;
|
|
627
1366
|
|
|
1367
|
+
// src/ui/picker-css.ts
|
|
1368
|
+
var snappy = springEasing(2 * Math.PI / 0.25, 1);
|
|
1369
|
+
var pickerCss = `
|
|
1370
|
+
:where(.knobs-picker,.knobs-picker-option,.knobs-picker-head,.knobs-picker-head *,.knobs-picker-card,.knobs-picker-body){box-sizing:border-box}
|
|
1371
|
+
.knobs-ui.knobs-picker{position:relative;display:flex;flex-direction:column;gap:2px;padding:0;outline:none;
|
|
1372
|
+
font:var(--ku-font-weight) var(--ku-font-size)/1.35 var(--ku-font);color:var(--ku-text);text-align:left;letter-spacing:normal;-webkit-font-smoothing:antialiased;
|
|
1373
|
+
--kp-card:var(--k-picker-card,var(--ku-surface));--kp-radius:var(--k-picker-radius,var(--ku-panel-radius));--kp-snappy:${snappy.easing};--kp-snappy-ms:${snappy.ms}ms}
|
|
1374
|
+
.knobs-picker [hidden]{display:none!important}
|
|
1375
|
+
/* The selection: one card behind the selected option and its parameters, gliding between them. */
|
|
1376
|
+
.knobs-picker-card{position:absolute;left:0;right:0;top:0;height:0;border-radius:var(--kp-radius);background:var(--kp-card);box-shadow:inset 0 0 0 1px var(--ku-line);pointer-events:none}
|
|
1377
|
+
.knobs-picker[data-frame="flat"] .knobs-picker-card{box-shadow:none}
|
|
1378
|
+
.knobs-picker-option{position:relative;flex:none}
|
|
1379
|
+
:where(.knobs-ui) .knobs-picker-head{all:revert;box-sizing:border-box;position:relative;display:flex;align-items:center;gap:8px;width:100%;height:var(--ku-row);padding:0 calc(var(--ku-inset) - 2px) 0 calc(var(--ku-inset) - 4px);
|
|
1380
|
+
border:0;margin:0;background:transparent;color:var(--ku-label);font:inherit;letter-spacing:inherit;text-align:left;cursor:default;outline:none;user-select:none;-webkit-user-select:none}
|
|
1381
|
+
/* Hover: in at once, out over 100 ms. Keyboard focus: the same wash, a little firmer. */
|
|
1382
|
+
.knobs-picker-head::before{content:"";position:absolute;inset:0;border-radius:var(--kp-radius);background:var(--ku-wash);opacity:0;transition:opacity calc(var(--k-hover-out,100) * 1ms) ease-out;pointer-events:none}
|
|
1383
|
+
.knobs-picker-option:not([data-selected]) .knobs-picker-head:hover::before{opacity:1;transition-duration:calc(var(--k-hover-in,0) * 1ms)}
|
|
1384
|
+
.knobs-picker-head:focus-visible::before{opacity:1;background:var(--ku-wash-hi);transition-duration:0s}
|
|
1385
|
+
.knobs-picker-head>*{position:relative}
|
|
1386
|
+
.knobs-picker-option[data-selected] .knobs-picker-head{color:var(--ku-text)}
|
|
1387
|
+
/* Every row is numbered (only 1–9 are keys): tabular digits in a column two digits wide, so the
|
|
1388
|
+
names line up past the ninth. */
|
|
1389
|
+
.knobs-picker-key{flex:none;display:inline-flex;align-items:center;justify-content:center;width:max(17px,calc(2ch + 6px));height:17px;padding:0;border-radius:5px;background:var(--ku-surface);
|
|
1390
|
+
color:var(--ku-dim);font:var(--ku-label-weight) calc(var(--ku-font-size) - 1.5px)/1 var(--ku-font);font-variant-numeric:tabular-nums;transition:background .15s,color .15s}
|
|
1391
|
+
.knobs-picker-key:empty{visibility:hidden}
|
|
1392
|
+
/* An option's picture: cover-fit, the rows' corner language and a hairline drawn over it. Its
|
|
1393
|
+
space is kept while it loads (nothing drawn), it fades in once loaded, and one that fails
|
|
1394
|
+
gives its space back on the same ease. */
|
|
1395
|
+
.knobs-picker-thumb{flex:none;display:block;width:var(--k-picker-thumb,min(24px,calc(var(--ku-row) - 10px)));aspect-ratio:1;margin:0;border-radius:max(3px,calc(var(--ku-radius) - 2px));
|
|
1396
|
+
object-fit:cover;background:none;outline:1px solid var(--ku-line);outline-offset:-1px;opacity:0;
|
|
1397
|
+
transition:opacity .2s ease-out,width .18s ease-out,margin .18s ease-out}
|
|
1398
|
+
.knobs-picker-thumb[data-state="loaded"]{opacity:1}
|
|
1399
|
+
.knobs-picker-thumb[data-instant]{transition:none}
|
|
1400
|
+
.knobs-picker-thumb[data-state="failed"]{width:0;margin-right:-8px;outline-color:transparent}
|
|
1401
|
+
.knobs-picker-option[data-selected] .knobs-picker-key{background:var(--ku-surface-hi);color:var(--ku-text)}
|
|
1402
|
+
.knobs-picker-name{flex:1;min-width:0;overflow:hidden;white-space:nowrap;font-weight:var(--ku-label-weight)}
|
|
1403
|
+
.knobs-picker-suggested{flex:none;color:var(--ku-dim);font-size:var(--ku-text-meta)}
|
|
1404
|
+
.knobs-picker-check{flex:none;display:flex;width:14px;height:14px;color:var(--ku-accent);opacity:0;scale:.6;filter:blur(2px);
|
|
1405
|
+
transition:opacity .11s ease-in,scale .11s ease-in,filter .11s ease-in}
|
|
1406
|
+
.knobs-picker-check svg{display:block;width:14px;height:14px}
|
|
1407
|
+
.knobs-picker-option[data-chosen] .knobs-picker-check{opacity:1;scale:1;filter:none;transition:opacity .15s ease-out,scale var(--kp-snappy-ms) var(--kp-snappy),filter .15s ease-out}
|
|
1408
|
+
.knobs-picker-body{position:relative;overflow:visible}
|
|
1409
|
+
.knobs-picker-body[data-moving]{overflow:hidden}
|
|
1410
|
+
/* The picker insets its parameters itself (--k-picker-pad), whatever a panel's own pad: a panel
|
|
1411
|
+
inside sheds its card and pad for the picker's, so nothing draws a card inside a card and no
|
|
1412
|
+
heading sits at the card's edge. */
|
|
1413
|
+
.knobs-picker-inner{padding:var(--k-picker-pad,0 8px 8px)}
|
|
1414
|
+
.knobs-picker-inner:empty{padding:0}
|
|
1415
|
+
.knobs-picker-inner .knobs-panel.knobs-inline{--k-chrome:0;padding:0}
|
|
1416
|
+
.knobs-picker-empty{padding:8px var(--ku-inset);color:var(--ku-dim)}
|
|
1417
|
+
@media(prefers-reduced-motion:reduce){.knobs-picker :where(*,::before,::after){transition-duration:.15s!important;scale:none!important;filter:none!important}}
|
|
1418
|
+
`;
|
|
1419
|
+
|
|
628
1420
|
// src/ui/readout-css.ts
|
|
629
1421
|
var readoutCss = `
|
|
630
1422
|
/* Readouts: live values, sparklines, graphs, meters, tables and logs. */
|
|
@@ -714,39 +1506,154 @@ var readoutCss = `
|
|
|
714
1506
|
@media(prefers-reduced-motion:reduce){.knobs-rd :where(*){transition:none!important}}
|
|
715
1507
|
`;
|
|
716
1508
|
|
|
717
|
-
// src/
|
|
718
|
-
var
|
|
719
|
-
var
|
|
720
|
-
|
|
721
|
-
|
|
722
|
-
"
|
|
723
|
-
|
|
724
|
-
|
|
725
|
-
|
|
726
|
-
|
|
727
|
-
|
|
728
|
-
|
|
729
|
-
|
|
730
|
-
|
|
731
|
-
|
|
732
|
-
"
|
|
733
|
-
|
|
734
|
-
|
|
735
|
-
|
|
736
|
-
|
|
737
|
-
|
|
738
|
-
|
|
739
|
-
|
|
740
|
-
|
|
741
|
-
|
|
742
|
-
|
|
743
|
-
|
|
744
|
-
|
|
745
|
-
|
|
746
|
-
|
|
747
|
-
|
|
748
|
-
|
|
749
|
-
|
|
1509
|
+
// src/core/keys.ts
|
|
1510
|
+
var MODS = ["Ctrl", "Alt", "Shift", "Meta"];
|
|
1511
|
+
var ALIASES = {
|
|
1512
|
+
ctrl: "Ctrl",
|
|
1513
|
+
control: "Ctrl",
|
|
1514
|
+
"⌃": "Ctrl",
|
|
1515
|
+
alt: "Alt",
|
|
1516
|
+
option: "Alt",
|
|
1517
|
+
opt: "Alt",
|
|
1518
|
+
"⌥": "Alt",
|
|
1519
|
+
shift: "Shift",
|
|
1520
|
+
"⇧": "Shift",
|
|
1521
|
+
meta: "Meta",
|
|
1522
|
+
cmd: "Meta",
|
|
1523
|
+
command: "Meta",
|
|
1524
|
+
"⌘": "Meta",
|
|
1525
|
+
mod: "Mod"
|
|
1526
|
+
};
|
|
1527
|
+
var NAMED = {
|
|
1528
|
+
" ": "Space",
|
|
1529
|
+
space: "Space",
|
|
1530
|
+
spacebar: "Space",
|
|
1531
|
+
esc: "Escape",
|
|
1532
|
+
escape: "Escape",
|
|
1533
|
+
enter: "Enter",
|
|
1534
|
+
return: "Enter",
|
|
1535
|
+
tab: "Tab",
|
|
1536
|
+
backspace: "Backspace",
|
|
1537
|
+
delete: "Delete",
|
|
1538
|
+
del: "Delete",
|
|
1539
|
+
up: "ArrowUp",
|
|
1540
|
+
down: "ArrowDown",
|
|
1541
|
+
left: "ArrowLeft",
|
|
1542
|
+
right: "ArrowRight",
|
|
1543
|
+
arrowup: "ArrowUp",
|
|
1544
|
+
arrowdown: "ArrowDown",
|
|
1545
|
+
arrowleft: "ArrowLeft",
|
|
1546
|
+
arrowright: "ArrowRight",
|
|
1547
|
+
home: "Home",
|
|
1548
|
+
end: "End",
|
|
1549
|
+
pageup: "PageUp",
|
|
1550
|
+
pagedown: "PageDown"
|
|
1551
|
+
};
|
|
1552
|
+
var isApple = () => typeof navigator !== "undefined" && /mac|iphone|ipad|ipod/i.test(navigator.userAgentData?.platform ?? navigator.platform ?? "");
|
|
1553
|
+
var keyName = (raw) => {
|
|
1554
|
+
if (!raw)
|
|
1555
|
+
return "";
|
|
1556
|
+
const named = NAMED[raw.toLowerCase()];
|
|
1557
|
+
if (named)
|
|
1558
|
+
return named;
|
|
1559
|
+
if (raw.length === 1)
|
|
1560
|
+
return raw.toUpperCase();
|
|
1561
|
+
if (/^f\d{1,2}$/i.test(raw))
|
|
1562
|
+
return raw.toUpperCase();
|
|
1563
|
+
return raw.charAt(0).toUpperCase() + raw.slice(1);
|
|
1564
|
+
};
|
|
1565
|
+
function normalizeCombo(combo) {
|
|
1566
|
+
const trimmed = combo.trim();
|
|
1567
|
+
if (!trimmed)
|
|
1568
|
+
return "";
|
|
1569
|
+
const parts = trimmed === "+" ? ["+"] : trimmed.replace(/\+\+$/, "+plus").split("+");
|
|
1570
|
+
const mods = new Set;
|
|
1571
|
+
let key = "";
|
|
1572
|
+
for (const part of parts) {
|
|
1573
|
+
const alias = ALIASES[part.trim().toLowerCase()] ?? ALIASES[part.trim()];
|
|
1574
|
+
if (alias === "Mod")
|
|
1575
|
+
mods.add(isApple() ? "Meta" : "Ctrl");
|
|
1576
|
+
else if (alias)
|
|
1577
|
+
mods.add(alias);
|
|
1578
|
+
else
|
|
1579
|
+
key = part === "plus" ? "+" : keyName(part.trim() || part);
|
|
1580
|
+
}
|
|
1581
|
+
if (!key)
|
|
1582
|
+
return "";
|
|
1583
|
+
return [...MODS.filter((m) => mods.has(m)), key].join("+");
|
|
1584
|
+
}
|
|
1585
|
+
var GLYPHS = { Ctrl: "⌃", Alt: "⌥", Shift: "⇧", Meta: "⌘" };
|
|
1586
|
+
var WORDS = { Ctrl: "Ctrl", Alt: "Alt", Shift: "Shift", Meta: "Meta" };
|
|
1587
|
+
var KEY_GLYPHS = {
|
|
1588
|
+
ArrowUp: "↑",
|
|
1589
|
+
ArrowDown: "↓",
|
|
1590
|
+
ArrowLeft: "←",
|
|
1591
|
+
ArrowRight: "→",
|
|
1592
|
+
Backspace: "⌫",
|
|
1593
|
+
Delete: "⌦",
|
|
1594
|
+
Enter: "↩",
|
|
1595
|
+
Escape: "Esc",
|
|
1596
|
+
Tab: "⇥",
|
|
1597
|
+
PageUp: "PgUp",
|
|
1598
|
+
PageDown: "PgDn"
|
|
1599
|
+
};
|
|
1600
|
+
function comboParts(combo, apple = isApple()) {
|
|
1601
|
+
const norm = normalizeCombo(combo);
|
|
1602
|
+
if (!norm)
|
|
1603
|
+
return [];
|
|
1604
|
+
const parts = norm === "+" ? ["+"] : norm.split("+");
|
|
1605
|
+
if (norm.endsWith("++"))
|
|
1606
|
+
parts.splice(-2, 2, "+");
|
|
1607
|
+
const key = parts.pop();
|
|
1608
|
+
const mods = parts;
|
|
1609
|
+
const shown = KEY_GLYPHS[key] ?? key;
|
|
1610
|
+
return apple ? [...mods.map((m) => GLYPHS[m]), shown] : [...mods.map((m) => WORDS[m]), shown];
|
|
1611
|
+
}
|
|
1612
|
+
function isTypingTarget(target) {
|
|
1613
|
+
const el = target;
|
|
1614
|
+
if (!el || typeof el.tagName !== "string")
|
|
1615
|
+
return false;
|
|
1616
|
+
const tag = el.tagName;
|
|
1617
|
+
if (tag === "SELECT" || el.isContentEditable)
|
|
1618
|
+
return true;
|
|
1619
|
+
if (tag === "TEXTAREA")
|
|
1620
|
+
return !el.readOnly;
|
|
1621
|
+
return tag === "INPUT" && !el.readOnly && !["range", "checkbox", "radio", "button", "submit", "reset", "color"].includes(el.type);
|
|
1622
|
+
}
|
|
1623
|
+
|
|
1624
|
+
// src/ui/look.ts
|
|
1625
|
+
var ease = springEasing(26, 1);
|
|
1626
|
+
var TOKENS = {
|
|
1627
|
+
bg: "#1c1c1e",
|
|
1628
|
+
surface: "rgba(255,255,255,.055)",
|
|
1629
|
+
"surface-hi": "rgba(255,255,255,.11)",
|
|
1630
|
+
line: "rgba(255,255,255,.1)",
|
|
1631
|
+
"line-hi": "rgba(255,255,255,.18)",
|
|
1632
|
+
text: "rgba(255,255,255,.92)",
|
|
1633
|
+
label: "rgba(255,255,255,.64)",
|
|
1634
|
+
dim: "rgba(255,255,255,.42)",
|
|
1635
|
+
accent: "#fff",
|
|
1636
|
+
font: 'system-ui,-apple-system,"SF Pro Text",sans-serif',
|
|
1637
|
+
mono: '"SF Mono",SFMono-Regular,ui-monospace,Menlo,monospace',
|
|
1638
|
+
"font-size": "12px",
|
|
1639
|
+
"font-weight": "400",
|
|
1640
|
+
"label-weight": "500",
|
|
1641
|
+
"title-weight": "600",
|
|
1642
|
+
row: "32px",
|
|
1643
|
+
inset: "10px",
|
|
1644
|
+
radius: "8px",
|
|
1645
|
+
"hover-in": "150",
|
|
1646
|
+
"hover-out": "150",
|
|
1647
|
+
warn: "#ff9f0a",
|
|
1648
|
+
critical: "#ff453a",
|
|
1649
|
+
rim: "rgba(255,255,255,.09)",
|
|
1650
|
+
page: "#101012",
|
|
1651
|
+
"seg-bg": "rgba(0,0,0,.26)",
|
|
1652
|
+
shadow: "0 12px 36px rgba(0,0,0,.52),0 1px 2px rgba(0,0,0,.3)"
|
|
1653
|
+
};
|
|
1654
|
+
var LIGHT = {
|
|
1655
|
+
bg: "#f5f5f7",
|
|
1656
|
+
surface: "rgba(0,0,0,.05)",
|
|
750
1657
|
"surface-hi": "rgba(0,0,0,.095)",
|
|
751
1658
|
line: "rgba(0,0,0,.1)",
|
|
752
1659
|
"line-hi": "rgba(0,0,0,.2)",
|
|
@@ -809,6 +1716,39 @@ var css = `
|
|
|
809
1716
|
:is(.knobs-tree,.knobs-split>.knobs-pane>.knobs-pane-clip>.knobs-pane-inner>.knobs-pane-head) [hidden]{display:none!important}
|
|
810
1717
|
:is(.knobs-pane-head,.knobs-pane-rail,.knobs-tree) svg{display:block;flex:none}
|
|
811
1718
|
|
|
1719
|
+
/* Scroll fades: a scroller's content fades out over --knobs-fade at an edge with more beyond it,
|
|
1720
|
+
coming in over the first --knobs-fade-travel of scroll, so reaching an end never pops. They're
|
|
1721
|
+
scroll-driven (no script), so where those aren't supported there's no fade. --knobs-fade-inset
|
|
1722
|
+
starts the top one below a sticky header; .knobs-scroll-fade-x fades across instead (a single
|
|
1723
|
+
line that's cut off, like a tree's labels: no ellipsis). Pane bodies, trees and the palette's
|
|
1724
|
+
list carry the class; around a panel the GPU draws, the mask goes (it would trap the panel's
|
|
1725
|
+
overlays under it). */
|
|
1726
|
+
@property --knobs-fade-a{syntax:"<number>";inherits:false;initial-value:1}
|
|
1727
|
+
@property --knobs-fade-b{syntax:"<number>";inherits:false;initial-value:1}
|
|
1728
|
+
@keyframes knobs-fade-a{from{--knobs-fade-a:1}to{--knobs-fade-a:0}}
|
|
1729
|
+
@keyframes knobs-fade-b{from{--knobs-fade-b:0}to{--knobs-fade-b:1}}
|
|
1730
|
+
@supports (animation-timeline:scroll()){
|
|
1731
|
+
:is(.knobs-scroll-fade,.knobs-scroll-fade-x){--knobs-fade:20px;--knobs-fade-travel:24px;animation:knobs-fade-a linear both,knobs-fade-b linear both;animation-duration:auto;
|
|
1732
|
+
animation-timeline:scroll(self block),scroll(self block);animation-range:0 var(--knobs-fade-travel),calc(100% - var(--knobs-fade-travel)) 100%}
|
|
1733
|
+
.knobs-scroll-fade{--knobs-fade-mask:linear-gradient(to bottom,#000 var(--knobs-fade-inset,0px),rgb(0 0 0/var(--knobs-fade-a)) var(--knobs-fade-inset,0px),#000 calc(var(--knobs-fade-inset,0px) + var(--knobs-fade)),#000 calc(100% - var(--knobs-fade)),rgb(0 0 0/var(--knobs-fade-b)));-webkit-mask-image:var(--knobs-fade-mask);mask-image:var(--knobs-fade-mask)}
|
|
1734
|
+
.knobs-scroll-fade-x{--knobs-fade:16px;--knobs-fade-travel:4px;animation-timeline:scroll(self inline),scroll(self inline);
|
|
1735
|
+
--knobs-fade-mask:linear-gradient(to right,rgb(0 0 0/var(--knobs-fade-a)),#000 var(--knobs-fade),#000 calc(100% - var(--knobs-fade)),rgb(0 0 0/var(--knobs-fade-b)));-webkit-mask-image:var(--knobs-fade-mask);mask-image:var(--knobs-fade-mask)}
|
|
1736
|
+
.knobs-scroll-fade:has(.knobs-panel[data-gpu-active="true"]){-webkit-mask-image:none;mask-image:none}
|
|
1737
|
+
.knobs-tree-row[data-editing] .knobs-tree-label{-webkit-mask-image:none;mask-image:none}
|
|
1738
|
+
}
|
|
1739
|
+
@supports not (animation-timeline:scroll()){.knobs-scroll-fade-x{text-overflow:ellipsis}}
|
|
1740
|
+
|
|
1741
|
+
/* Overlay scrollbars (trees, activity rails): a thin thumb over the content, so the rows never
|
|
1742
|
+
give up width to a scrollbar as they start or stop overflowing. It shows while the content
|
|
1743
|
+
scrolls or the pointer is over it. */
|
|
1744
|
+
:is(.knobs-tree,.knobs-activity)::-webkit-scrollbar{display:none}
|
|
1745
|
+
.knobs-scrollbar{position:sticky;top:0;z-index:3;height:0;pointer-events:none}
|
|
1746
|
+
.knobs-scrollbar[hidden]{display:none}
|
|
1747
|
+
.knobs-scrollbar-thumb{position:absolute;top:0;right:2px;width:5px;border-radius:3px;background:color-mix(in srgb,var(--ku-text) 30%,transparent);opacity:0;
|
|
1748
|
+
transition:opacity .3s ease-out,background .15s}
|
|
1749
|
+
:hover>.knobs-scrollbar>.knobs-scrollbar-thumb,.knobs-scrollbar:is([data-active],[data-dragging])>.knobs-scrollbar-thumb{opacity:1;pointer-events:auto;transition-duration:.12s,.15s}
|
|
1750
|
+
.knobs-scrollbar-thumb:hover,.knobs-scrollbar[data-dragging]>.knobs-scrollbar-thumb{background:color-mix(in srgb,var(--ku-text) 48%,transparent)}
|
|
1751
|
+
|
|
812
1752
|
/* Split: panes along a row or column, hairline dividers with a wider grip. */
|
|
813
1753
|
.knobs-split{position:relative;display:flex;width:100%;height:100%;min-width:0;min-height:0;overflow:hidden}
|
|
814
1754
|
.knobs-split[data-direction="column"]{flex-direction:column}
|
|
@@ -821,7 +1761,7 @@ var css = `
|
|
|
821
1761
|
.knobs-pane-inner{position:absolute;inset:0;display:flex;flex-direction:column;min-width:0;min-height:0}
|
|
822
1762
|
.knobs-pane-body{position:relative;flex:1;min-width:0;min-height:0;overflow:hidden}
|
|
823
1763
|
.knobs-pane[data-scroll] .knobs-pane-body{overflow:auto;scrollbar-width:thin}
|
|
824
|
-
.knobs-pane[data-rail][data-collapsed][data-settled]>.knobs-pane-clip,.knobs-pane:not([data-rail])[data-collapsed][data-settled]>.knobs-pane-clip>.knobs-pane-inner>.knobs-pane-body{visibility:hidden}
|
|
1764
|
+
.knobs-pane[data-rail][data-collapsed][data-settled]>.knobs-pane-clip>.knobs-pane-inner,.knobs-pane:not([data-rail])[data-collapsed][data-settled]>.knobs-pane-clip>.knobs-pane-inner>.knobs-pane-body{visibility:hidden}
|
|
825
1765
|
.knobs-ui .knobs-pane-head{display:flex;align-items:center;gap:6px;flex:none;height:var(--ku-row);padding:0 calc(var(--ku-inset) - 4px) 0 var(--ku-inset);user-select:none;-webkit-user-select:none}
|
|
826
1766
|
.knobs-pane-title{min-width:0;overflow:hidden;text-overflow:ellipsis;white-space:nowrap;font-weight:var(--ku-title-weight);color:var(--ku-text)}
|
|
827
1767
|
.knobs-pane-count{flex:none;color:var(--ku-dim);font-variant-numeric:tabular-nums}
|
|
@@ -832,17 +1772,35 @@ var css = `
|
|
|
832
1772
|
.knobs-ui .knobs-pane-btn[aria-pressed="true"]{color:var(--ku-accent)}
|
|
833
1773
|
.knobs-pane-btn svg{width:13px;height:13px}
|
|
834
1774
|
.knobs-pane-head[data-toggles]{cursor:pointer}
|
|
835
|
-
|
|
836
|
-
|
|
837
|
-
|
|
838
|
-
.knobs-ui .knobs-pane-rail{position:absolute;
|
|
1775
|
+
/* A collapsed pane's rail: its title along the edge; a click opens it. It holds on to the edge the
|
|
1776
|
+
pane folds toward, as wide as the rail, over the card (which folds to its size): only its label
|
|
1777
|
+
and chevron draw, and a nested split's slot, which has no card, gives it one of its own. */
|
|
1778
|
+
.knobs-ui .knobs-pane-rail{position:absolute;margin:var(--ku-pane-m);border-radius:var(--ku-pane-radius);background-color:transparent;display:flex;align-items:center;gap:8px;color:var(--ku-label);cursor:pointer;opacity:0;pointer-events:none;
|
|
839
1779
|
font-weight:var(--ku-title-weight);transition:background .15s,color .15s}
|
|
840
|
-
.knobs-
|
|
841
|
-
.knobs-split[data-direction="
|
|
1780
|
+
.knobs-ui .knobs-pane[data-nested]>.knobs-pane-rail{background-color:var(--ku-pane-bg);box-shadow:var(--ku-pane-ring)}
|
|
1781
|
+
.knobs-split[data-direction="row"]>.knobs-pane>.knobs-pane-rail{top:0;bottom:0;width:var(--ku-rail);writing-mode:vertical-rl;padding:10px 0}
|
|
1782
|
+
.knobs-split[data-direction="row"]>.knobs-pane[data-edge="start"]>.knobs-pane-rail{right:0}
|
|
1783
|
+
.knobs-split[data-direction="row"]>.knobs-pane[data-edge="end"]>.knobs-pane-rail{left:0}
|
|
1784
|
+
.knobs-split[data-direction="column"]>.knobs-pane>.knobs-pane-rail{left:0;right:0;height:var(--ku-rail);padding:0 var(--ku-inset)}
|
|
1785
|
+
.knobs-split[data-direction="column"]>.knobs-pane[data-edge="start"]>.knobs-pane-rail{bottom:0}
|
|
1786
|
+
.knobs-split[data-direction="column"]>.knobs-pane[data-edge="end"]>.knobs-pane-rail{top:0}
|
|
842
1787
|
.knobs-pane[data-collapsed]>.knobs-pane-rail{pointer-events:auto}
|
|
843
|
-
|
|
844
|
-
.knobs-pane-rail
|
|
845
|
-
.knobs-pane-rail
|
|
1788
|
+
/* Its hover wash fades, rather than going in one frame as a click opens the pane. */
|
|
1789
|
+
.knobs-ui .knobs-pane-rail::before{content:"";position:absolute;inset:0;border-radius:inherit;background:var(--ku-wash);opacity:0;transition:opacity .15s;pointer-events:none}
|
|
1790
|
+
.knobs-pane-rail>*{position:relative}
|
|
1791
|
+
.knobs-ui .knobs-pane[data-collapsed]>.knobs-pane-rail:is(:hover,:focus-visible){color:var(--ku-text)}
|
|
1792
|
+
.knobs-ui .knobs-pane[data-collapsed]>.knobs-pane-rail:is(:hover,:focus-visible)::before{opacity:1}
|
|
1793
|
+
.knobs-pane-rail-label{white-space:nowrap;overflow:hidden;text-overflow:ellipsis}
|
|
1794
|
+
.knobs-pane-chevron{display:flex;flex:none;width:11px;height:11px;writing-mode:horizontal-tb;color:var(--ku-dim)}
|
|
1795
|
+
.knobs-pane-turn{display:flex;width:100%;height:100%;transform-origin:50% 50%}
|
|
1796
|
+
.knobs-pane-rail svg{width:11px;height:11px}
|
|
1797
|
+
/* The rail's chevron stands in for the header's collapse icon (it turns into the rail's), so the
|
|
1798
|
+
icon itself isn't drawn; hovering its button lights the chevron instead. */
|
|
1799
|
+
.knobs-pane[data-morph]>.knobs-pane-clip>.knobs-pane-inner>.knobs-pane-head>.knobs-pane-collapse>svg{visibility:hidden}
|
|
1800
|
+
/* So does the rail's label for the title, when it travels (split's fold: { title: "travel" }). */
|
|
1801
|
+
.knobs-pane[data-travel]>.knobs-pane-clip>.knobs-pane-inner>.knobs-pane-head>.knobs-pane-title{visibility:hidden}
|
|
1802
|
+
.knobs-pane[data-morph]:not([data-collapsed]):has(>.knobs-pane-clip>.knobs-pane-inner>.knobs-pane-head>.knobs-pane-collapse:is(:hover,:focus-visible))>.knobs-pane-rail .knobs-pane-chevron{color:var(--ku-text)}
|
|
1803
|
+
.knobs-pane[data-morph]:not([data-collapsed])>.knobs-pane-rail .knobs-pane-chevron{transition:color .15s}
|
|
846
1804
|
|
|
847
1805
|
.knobs-divider{position:relative;z-index:2;flex:none;width:1px;background:var(--ku-divider);touch-action:none;outline:none}
|
|
848
1806
|
.knobs-split[data-direction="column"]>.knobs-divider{width:auto;height:1px}
|
|
@@ -868,7 +1826,7 @@ var css = `
|
|
|
868
1826
|
:root[data-knobs-resizing] :is(iframe,canvas:not(.knobs-gpu-canvas)){pointer-events:none}
|
|
869
1827
|
|
|
870
1828
|
/* Tree: one scroller, rows placed by transform, only those near the view in the DOM. */
|
|
871
|
-
.knobs-tree{position:relative;overflow:auto;height:100%;outline:none;scrollbar-width:
|
|
1829
|
+
.knobs-tree{position:relative;overflow:auto;height:100%;outline:none;scrollbar-width:none;overscroll-behavior:contain;user-select:none;-webkit-user-select:none}
|
|
872
1830
|
.knobs-tree-space{position:relative;width:100%;pointer-events:none}
|
|
873
1831
|
.knobs-tree-row{position:absolute;left:var(--ku-row-x);right:var(--ku-row-x);top:0;display:flex;align-items:center;height:var(--ku-row);padding-right:calc(var(--ku-inset) - 4px);
|
|
874
1832
|
color:var(--ku-text);white-space:nowrap;cursor:default;will-change:transform;contain:layout style}
|
|
@@ -885,7 +1843,7 @@ var css = `
|
|
|
885
1843
|
.knobs-tree-row[aria-expanded="true"]>.knobs-tree-caret{transform:rotate(90deg)}
|
|
886
1844
|
.knobs-tree-row:not([aria-expanded])>.knobs-tree-caret{visibility:hidden}
|
|
887
1845
|
.knobs-tree-icon{flex:none;display:flex;align-items:center;justify-content:center;width:14px;margin-right:6px;color:var(--ku-dim);font-size:calc(var(--ku-font-size) - 1px)}
|
|
888
|
-
.knobs-tree-label{position:relative;flex:1;min-width:0;overflow:hidden;
|
|
1846
|
+
.knobs-tree-label{position:relative;flex:1;min-width:0;overflow:hidden;font-weight:var(--ku-font-weight)}
|
|
889
1847
|
.knobs-tree-row[data-ancestor] .knobs-tree-label{color:var(--ku-label)}
|
|
890
1848
|
.knobs-tree-label .knobs-tree-mark{color:var(--ku-accent)}
|
|
891
1849
|
.knobs-tree-row[data-off~="visible"] :is(.knobs-tree-label,.knobs-tree-icon){opacity:.45}
|
|
@@ -923,7 +1881,7 @@ var css = `
|
|
|
923
1881
|
.knobs-tl-play svg{width:12px;height:12px}
|
|
924
1882
|
.knobs-timeline[data-playing] .knobs-tl-play{background:var(--ku-accent);color:var(--ku-bg)}
|
|
925
1883
|
.knobs-tl-time{display:flex;flex-direction:column;line-height:1.2;font-variant-numeric:tabular-nums;font-feature-settings:"tnum"}
|
|
926
|
-
.knobs-tl-now{font:var(--ku-font-weight) var(--ku-value-size)/1.2 var(--ku-value-font);color:var(--ku-text);white-space:nowrap}
|
|
1884
|
+
.knobs-tl-now{font:var(--ku-font-weight) var(--ku-value-size)/1.2 var(--ku-value-font);font-variant-numeric:tabular-nums;font-feature-settings:"tnum";color:var(--ku-text);white-space:nowrap}
|
|
927
1885
|
.knobs-tl-sub{font-size:var(--ku-text-meta);color:var(--ku-dim);white-space:nowrap}
|
|
928
1886
|
.knobs-tl-track{position:relative;flex:1;min-width:0;overflow:hidden;border-radius:var(--ku-radius);background:var(--ku-surface);cursor:col-resize;touch-action:none;
|
|
929
1887
|
transition:background calc(var(--ku-hover-out) * 1ms)}
|
|
@@ -975,7 +1933,7 @@ function injectUiStyles(doc) {
|
|
|
975
1933
|
return;
|
|
976
1934
|
const style = doc.createElement("style");
|
|
977
1935
|
style.dataset.knobsUi = "";
|
|
978
|
-
style.textContent = css + readoutCss + overlayCss + bentoCss;
|
|
1936
|
+
style.textContent = css + readoutCss + overlayCss + bentoCss + activityCss + pickerCss + rolling_css_default;
|
|
979
1937
|
doc.head.append(style);
|
|
980
1938
|
}
|
|
981
1939
|
function injectUiPreset(doc, preset) {
|
|
@@ -999,81 +1957,1166 @@ function applyLook(root, look) {
|
|
|
999
1957
|
injectUiPreset(doc, look.preset);
|
|
1000
1958
|
root.dataset.preset = look.preset.name;
|
|
1001
1959
|
}
|
|
1002
|
-
if (look.theme)
|
|
1003
|
-
root.dataset.theme = look.theme;
|
|
1004
|
-
syncFrame(root);
|
|
1960
|
+
if (look.theme)
|
|
1961
|
+
root.dataset.theme = look.theme;
|
|
1962
|
+
syncFrame(root);
|
|
1963
|
+
}
|
|
1964
|
+
function syncFrame(root) {
|
|
1965
|
+
if (!root.isConnected)
|
|
1966
|
+
return;
|
|
1967
|
+
const view = root.ownerDocument.defaultView;
|
|
1968
|
+
const flat = view?.getComputedStyle(root).getPropertyValue("--k-frame").trim() === "flat";
|
|
1969
|
+
if (flat)
|
|
1970
|
+
root.dataset.frame = "flat";
|
|
1971
|
+
else
|
|
1972
|
+
delete root.dataset.frame;
|
|
1973
|
+
}
|
|
1974
|
+
var lookDefaults = { current: {} };
|
|
1975
|
+
var reducedMotion = (win) => win?.matchMedia?.("(prefers-reduced-motion: reduce)").matches ?? false;
|
|
1976
|
+
var tune = (s, m) => {
|
|
1977
|
+
s.omega = m.omega;
|
|
1978
|
+
s.zeta = m.zeta;
|
|
1979
|
+
return s;
|
|
1980
|
+
};
|
|
1981
|
+
function notifyLayout(doc) {
|
|
1982
|
+
doc.dispatchEvent(new Event("knobs-layout"));
|
|
1983
|
+
}
|
|
1984
|
+
function frameLoop(win, step) {
|
|
1985
|
+
let raf = 0;
|
|
1986
|
+
let last = 0;
|
|
1987
|
+
const tick = (now) => {
|
|
1988
|
+
const dt = last ? Math.min(0.064, (now - last) / 1000) : 1 / 60;
|
|
1989
|
+
last = now;
|
|
1990
|
+
raf = step(dt) ? win.requestAnimationFrame(tick) : 0;
|
|
1991
|
+
if (!raf)
|
|
1992
|
+
last = 0;
|
|
1993
|
+
};
|
|
1994
|
+
return {
|
|
1995
|
+
wake() {
|
|
1996
|
+
if (!raf)
|
|
1997
|
+
raf = win.requestAnimationFrame(tick);
|
|
1998
|
+
},
|
|
1999
|
+
get running() {
|
|
2000
|
+
return raf !== 0;
|
|
2001
|
+
},
|
|
2002
|
+
stop() {
|
|
2003
|
+
if (raf)
|
|
2004
|
+
win.cancelAnimationFrame(raf);
|
|
2005
|
+
raf = 0;
|
|
2006
|
+
last = 0;
|
|
2007
|
+
}
|
|
2008
|
+
};
|
|
2009
|
+
}
|
|
2010
|
+
var svg = (body, box = 16) => `<svg viewBox="0 0 ${box} ${box}" aria-hidden="true" fill="none" stroke="currentColor" stroke-width="1.5" stroke-linecap="round" stroke-linejoin="round">${body}</svg>`;
|
|
2011
|
+
var ICONS = {
|
|
2012
|
+
caret: svg('<path d="M6 3.5 10.5 8 6 12.5"/>'),
|
|
2013
|
+
chevronDown: svg('<path d="M3.5 6 8 10.5 12.5 6"/>'),
|
|
2014
|
+
eye: svg('<path d="M1.5 8s2.4-4.5 6.5-4.5S14.5 8 14.5 8 12.1 12.5 8 12.5 1.5 8 1.5 8Z"/><circle cx="8" cy="8" r="2"/>'),
|
|
2015
|
+
eyeOff: svg('<path d="M2.5 2.5l11 11M6.6 4.2A6.6 6.6 0 0 1 8 3.5c4.1 0 6.5 4.5 6.5 4.5a11 11 0 0 1-1.7 2.2M10.2 11.9A6 6 0 0 1 8 12.5C3.9 12.5 1.5 8 1.5 8a11 11 0 0 1 2.4-2.9"/>'),
|
|
2016
|
+
lock: svg('<rect x="3.5" y="7" width="9" height="6.5" rx="1.2"/><path d="M5.5 7V5a2.5 2.5 0 0 1 5 0v2"/>'),
|
|
2017
|
+
unlock: svg('<rect x="3.5" y="7" width="9" height="6.5" rx="1.2"/><path d="M5.5 7V5a2.5 2.5 0 0 1 4.8-1"/>')
|
|
2018
|
+
};
|
|
2019
|
+
|
|
2020
|
+
// src/ui/scrollbar.ts
|
|
2021
|
+
function overlayScrollbar(scroller, content) {
|
|
2022
|
+
const doc = scroller.ownerDocument;
|
|
2023
|
+
const win = doc.defaultView;
|
|
2024
|
+
const track = doc.createElement("div");
|
|
2025
|
+
track.className = "knobs-scrollbar";
|
|
2026
|
+
track.setAttribute("aria-hidden", "true");
|
|
2027
|
+
const thumb = doc.createElement("div");
|
|
2028
|
+
thumb.className = "knobs-scrollbar-thumb";
|
|
2029
|
+
track.append(thumb);
|
|
2030
|
+
scroller.prepend(track);
|
|
2031
|
+
let size = 0;
|
|
2032
|
+
const update = () => {
|
|
2033
|
+
const view = scroller.clientHeight;
|
|
2034
|
+
const over = scroller.scrollHeight - view;
|
|
2035
|
+
const shown = over > 1 && view > 0;
|
|
2036
|
+
track.hidden = !shown;
|
|
2037
|
+
if (!shown)
|
|
2038
|
+
return;
|
|
2039
|
+
size = Math.max(20, view * view / (view + over));
|
|
2040
|
+
const y = Math.min(over, Math.max(0, scroller.scrollTop)) / over * (view - size);
|
|
2041
|
+
thumb.style.height = `${size.toFixed(1)}px`;
|
|
2042
|
+
thumb.style.transform = `translate3d(0,${y.toFixed(1)}px,0)`;
|
|
2043
|
+
};
|
|
2044
|
+
let idle = 0;
|
|
2045
|
+
const onScroll = () => {
|
|
2046
|
+
update();
|
|
2047
|
+
track.dataset.active = "";
|
|
2048
|
+
win.clearTimeout(idle);
|
|
2049
|
+
idle = win.setTimeout(() => delete track.dataset.active, 800);
|
|
2050
|
+
};
|
|
2051
|
+
scroller.addEventListener("scroll", onScroll, { passive: true });
|
|
2052
|
+
let drag;
|
|
2053
|
+
thumb.addEventListener("pointerdown", (e) => {
|
|
2054
|
+
if (e.button !== 0)
|
|
2055
|
+
return;
|
|
2056
|
+
e.preventDefault();
|
|
2057
|
+
e.stopPropagation();
|
|
2058
|
+
drag = { y: e.clientY, top: scroller.scrollTop, pointer: e.pointerId };
|
|
2059
|
+
thumb.setPointerCapture(e.pointerId);
|
|
2060
|
+
track.dataset.dragging = "";
|
|
2061
|
+
});
|
|
2062
|
+
thumb.addEventListener("pointermove", (e) => {
|
|
2063
|
+
if (!drag || e.pointerId !== drag.pointer)
|
|
2064
|
+
return;
|
|
2065
|
+
const view = scroller.clientHeight;
|
|
2066
|
+
const over = scroller.scrollHeight - view;
|
|
2067
|
+
scroller.scrollTop = drag.top + (e.clientY - drag.y) * over / Math.max(1, view - size);
|
|
2068
|
+
});
|
|
2069
|
+
const end = (e) => {
|
|
2070
|
+
if (!drag || e.pointerId !== drag.pointer)
|
|
2071
|
+
return;
|
|
2072
|
+
drag = undefined;
|
|
2073
|
+
delete track.dataset.dragging;
|
|
2074
|
+
};
|
|
2075
|
+
thumb.addEventListener("pointerup", end);
|
|
2076
|
+
thumb.addEventListener("pointercancel", end);
|
|
2077
|
+
thumb.addEventListener("lostpointercapture", end);
|
|
2078
|
+
const ro = new ResizeObserver(update);
|
|
2079
|
+
ro.observe(scroller);
|
|
2080
|
+
ro.observe(content);
|
|
2081
|
+
update();
|
|
2082
|
+
return {
|
|
2083
|
+
update,
|
|
2084
|
+
dispose() {
|
|
2085
|
+
ro.disconnect();
|
|
2086
|
+
win.clearTimeout(idle);
|
|
2087
|
+
scroller.removeEventListener("scroll", onScroll);
|
|
2088
|
+
track.remove();
|
|
2089
|
+
}
|
|
2090
|
+
};
|
|
2091
|
+
}
|
|
2092
|
+
|
|
2093
|
+
// src/ui/activity.ts
|
|
2094
|
+
var ACTIVITY_ICONS = {
|
|
2095
|
+
you: svg('<circle cx="8" cy="5.5" r="2.5"/><path d="M3.2 13.5c.8-2.4 2.6-3.8 4.8-3.8s4 1.4 4.8 3.8"/>'),
|
|
2096
|
+
agent: svg('<path d="M8 2.2 9.4 6.6 13.8 8 9.4 9.4 8 13.8 6.6 9.4 2.2 8 6.6 6.6Z"/>'),
|
|
2097
|
+
proposal: svg('<rect x="2.5" y="2.5" width="4.5" height="4.5" rx="1.2"/><rect x="9" y="2.5" width="4.5" height="4.5" rx="1.2"/><rect x="2.5" y="9" width="4.5" height="4.5" rx="1.2"/><rect x="9" y="9" width="4.5" height="4.5" rx="1.2"/>'),
|
|
2098
|
+
review: svg('<rect x="2" y="3.5" width="12" height="9" rx="2.2"/><path d="M7 6.3v3.4L9.8 8Z"/>'),
|
|
2099
|
+
pr: svg('<circle cx="4.5" cy="3.8" r="1.6"/><circle cx="4.5" cy="12.2" r="1.6"/><circle cx="11.5" cy="12.2" r="1.6"/><path d="M4.5 5.4v5.2M11.5 10.6V6.8a2 2 0 0 0-2-2H7.2M8.8 3.2 7.2 4.8l1.6 1.6"/>'),
|
|
2100
|
+
build: svg('<path d="M8 2.2 13.5 5v6L8 13.8 2.5 11V5Z"/><path d="M2.5 5 8 7.9 13.5 5M8 7.9v5.9"/>'),
|
|
2101
|
+
blocked: svg('<path d="M3.8 14V2.6M3.8 3h8l-1.9 3 1.9 3h-8"/>'),
|
|
2102
|
+
shipped: svg('<circle cx="8" cy="8" r="5.8"/><path d="m5.6 8.2 1.7 1.7 3.2-3.6"/>')
|
|
2103
|
+
};
|
|
2104
|
+
var PLAY = '<svg viewBox="0 0 8 8" aria-hidden="true"><path d="M1.5 1v6l5.2-3Z" fill="currentColor"/></svg>';
|
|
2105
|
+
var REFLOW = springMotion({ duration: 0.3, bounce: 0 });
|
|
2106
|
+
var OPEN = springMotion({ duration: 0.22, bounce: 0 });
|
|
2107
|
+
var CLOSE = springMotion({ duration: 0.18, bounce: 0 });
|
|
2108
|
+
var BLOOM = springMotion({ duration: 0.3, bounce: 0 });
|
|
2109
|
+
var EXIT = springMotion({ duration: 0.24, bounce: 0 });
|
|
2110
|
+
var FADE = springMotion({ duration: 0.16, bounce: 0 });
|
|
2111
|
+
var CLEAR = [11, 5];
|
|
2112
|
+
var PAD = 6;
|
|
2113
|
+
var SLIDE = 8;
|
|
2114
|
+
function activity(container, options) {
|
|
2115
|
+
const doc = container.ownerDocument;
|
|
2116
|
+
const win = doc.defaultView;
|
|
2117
|
+
let newest = options.newest ?? "bottom";
|
|
2118
|
+
const keysFrom = options.keys ?? "focus";
|
|
2119
|
+
let events = options.events;
|
|
2120
|
+
let index = indexActivity(events);
|
|
2121
|
+
const expanded = new Set(options.expanded ?? []);
|
|
2122
|
+
let rows = [];
|
|
2123
|
+
let selected = options.selected ?? null;
|
|
2124
|
+
let showing = false;
|
|
2125
|
+
const root = doc.createElement("div");
|
|
2126
|
+
root.className = "knobs-activity knobs-scroll-fade";
|
|
2127
|
+
root.tabIndex = 0;
|
|
2128
|
+
root.setAttribute("role", "tree");
|
|
2129
|
+
if (options.label)
|
|
2130
|
+
root.setAttribute("aria-label", options.label);
|
|
2131
|
+
applyLook(root, options);
|
|
2132
|
+
const space = doc.createElement("div");
|
|
2133
|
+
space.className = "knobs-activity-space";
|
|
2134
|
+
const hl = doc.createElement("div");
|
|
2135
|
+
hl.className = "knobs-activity-hl";
|
|
2136
|
+
hl.setAttribute("aria-hidden", "true");
|
|
2137
|
+
const line = doc.createElementNS("http://www.w3.org/2000/svg", "svg");
|
|
2138
|
+
line.setAttribute("class", "knobs-activity-line");
|
|
2139
|
+
line.setAttribute("aria-hidden", "true");
|
|
2140
|
+
const path = doc.createElementNS("http://www.w3.org/2000/svg", "path");
|
|
2141
|
+
line.append(path);
|
|
2142
|
+
const empty = doc.createElement("div");
|
|
2143
|
+
empty.className = "knobs-activity-empty";
|
|
2144
|
+
empty.hidden = true;
|
|
2145
|
+
space.append(hl, line);
|
|
2146
|
+
root.append(empty, space);
|
|
2147
|
+
container.append(root);
|
|
2148
|
+
const scrollbar = overlayScrollbar(root, space);
|
|
2149
|
+
let heights = [32, 26];
|
|
2150
|
+
const measure = () => {
|
|
2151
|
+
syncFrame(root);
|
|
2152
|
+
const probe = (depth) => {
|
|
2153
|
+
const p = doc.createElement("div");
|
|
2154
|
+
p.className = "knobs-activity-row";
|
|
2155
|
+
p.dataset.depth = String(depth);
|
|
2156
|
+
p.style.visibility = "hidden";
|
|
2157
|
+
space.append(p);
|
|
2158
|
+
const h = p.getBoundingClientRect().height;
|
|
2159
|
+
p.remove();
|
|
2160
|
+
return h;
|
|
2161
|
+
};
|
|
2162
|
+
heights = [probe(0) || 32, probe(1) || 26];
|
|
2163
|
+
};
|
|
2164
|
+
measure();
|
|
2165
|
+
const uid = `ka${Math.random().toString(36).slice(2, 7)}`;
|
|
2166
|
+
const domIds = new Map;
|
|
2167
|
+
const domId = (id) => {
|
|
2168
|
+
let d = domIds.get(id);
|
|
2169
|
+
if (!d)
|
|
2170
|
+
domIds.set(id, d = `${uid}-${domIds.size}`);
|
|
2171
|
+
return d;
|
|
2172
|
+
};
|
|
2173
|
+
const views = new Map;
|
|
2174
|
+
const makeView = (row) => {
|
|
2175
|
+
const el = doc.createElement("div");
|
|
2176
|
+
el.className = "knobs-activity-row";
|
|
2177
|
+
el.setAttribute("role", "treeitem");
|
|
2178
|
+
el.id = domId(row.id);
|
|
2179
|
+
el.dataset.id = row.id;
|
|
2180
|
+
const node = doc.createElement("span");
|
|
2181
|
+
node.className = "knobs-activity-node";
|
|
2182
|
+
const main = doc.createElement("span");
|
|
2183
|
+
main.className = "knobs-activity-main knobs-scroll-fade-x";
|
|
2184
|
+
const actor = doc.createElement("span");
|
|
2185
|
+
actor.className = "knobs-activity-actor";
|
|
2186
|
+
const title = doc.createElement("span");
|
|
2187
|
+
title.className = "knobs-activity-title";
|
|
2188
|
+
main.append(actor, title);
|
|
2189
|
+
const meta = doc.createElement("span");
|
|
2190
|
+
meta.className = "knobs-activity-meta";
|
|
2191
|
+
const thumbs = doc.createElement("span");
|
|
2192
|
+
thumbs.className = "knobs-activity-thumbs";
|
|
2193
|
+
const caret = doc.createElement("span");
|
|
2194
|
+
caret.className = "knobs-activity-caret";
|
|
2195
|
+
caret.innerHTML = ICONS.caret;
|
|
2196
|
+
const time = doc.createElement("span");
|
|
2197
|
+
time.className = "knobs-activity-time";
|
|
2198
|
+
el.append(node, main, meta);
|
|
2199
|
+
space.append(el);
|
|
2200
|
+
const v = {
|
|
2201
|
+
id: row.id,
|
|
2202
|
+
el,
|
|
2203
|
+
node,
|
|
2204
|
+
actor,
|
|
2205
|
+
title,
|
|
2206
|
+
meta,
|
|
2207
|
+
thumbs,
|
|
2208
|
+
chip: null,
|
|
2209
|
+
caret,
|
|
2210
|
+
time,
|
|
2211
|
+
roll: null,
|
|
2212
|
+
chipRoll: null,
|
|
2213
|
+
y: new Spring(0, REFLOW.omega, REFLOW.zeta, 0.05),
|
|
2214
|
+
p: new Spring(1, BLOOM.omega, BLOOM.zeta, 0.002),
|
|
2215
|
+
depth: row.depth,
|
|
2216
|
+
under: null,
|
|
2217
|
+
from: 0,
|
|
2218
|
+
leaving: false,
|
|
2219
|
+
event: undefined,
|
|
2220
|
+
state: ""
|
|
2221
|
+
};
|
|
2222
|
+
views.set(row.id, v);
|
|
2223
|
+
return v;
|
|
2224
|
+
};
|
|
2225
|
+
const dropView = (v) => {
|
|
2226
|
+
v.roll?.destroy();
|
|
2227
|
+
v.chipRoll?.destroy();
|
|
2228
|
+
v.el.remove();
|
|
2229
|
+
views.delete(v.id);
|
|
2230
|
+
};
|
|
2231
|
+
const rollInto = (host, roll, text) => {
|
|
2232
|
+
if (!text) {
|
|
2233
|
+
roll?.destroy();
|
|
2234
|
+
host.textContent = "";
|
|
2235
|
+
return null;
|
|
2236
|
+
}
|
|
2237
|
+
if (roll) {
|
|
2238
|
+
roll.update({ text });
|
|
2239
|
+
return roll;
|
|
2240
|
+
}
|
|
2241
|
+
return rt(host, {
|
|
2242
|
+
text,
|
|
2243
|
+
charset: "0123456789",
|
|
2244
|
+
transition: "direct",
|
|
2245
|
+
animated: !reducedMotion(win)
|
|
2246
|
+
});
|
|
2247
|
+
};
|
|
2248
|
+
const bind = (v, row) => {
|
|
2249
|
+
const e = row.event;
|
|
2250
|
+
const state = `${row.depth}|${row.group}|${row.expanded}|${selected === row.id}`;
|
|
2251
|
+
if (v.event === e && v.state === state)
|
|
2252
|
+
return;
|
|
2253
|
+
const changed = v.event !== e;
|
|
2254
|
+
v.event = e;
|
|
2255
|
+
v.state = state;
|
|
2256
|
+
v.depth = row.depth;
|
|
2257
|
+
const el = v.el;
|
|
2258
|
+
el.dataset.depth = String(row.depth);
|
|
2259
|
+
el.setAttribute("aria-level", String(row.depth + 1));
|
|
2260
|
+
el.setAttribute("aria-selected", String(selected === row.id));
|
|
2261
|
+
if (row.group)
|
|
2262
|
+
el.setAttribute("aria-expanded", String(row.expanded));
|
|
2263
|
+
else
|
|
2264
|
+
el.removeAttribute("aria-expanded");
|
|
2265
|
+
if (!changed)
|
|
2266
|
+
return;
|
|
2267
|
+
if (e.kind)
|
|
2268
|
+
el.dataset.kind = e.kind;
|
|
2269
|
+
else
|
|
2270
|
+
delete el.dataset.kind;
|
|
2271
|
+
el.toggleAttribute("data-current", Boolean(e.current));
|
|
2272
|
+
el.toggleAttribute("data-dimmed", Boolean(e.dimmed));
|
|
2273
|
+
const tint = e.tint ?? (e.kind ? options.tints?.[e.kind] : undefined);
|
|
2274
|
+
if (tint)
|
|
2275
|
+
el.style.setProperty("--ka-tint", tint);
|
|
2276
|
+
else
|
|
2277
|
+
el.style.removeProperty("--ka-tint");
|
|
2278
|
+
const icon = e.icon ?? (row.depth === 1 ? undefined : (e.kind && (options.icons?.[e.kind] ?? ACTIVITY_ICONS[e.kind])) ?? undefined);
|
|
2279
|
+
if (icon instanceof Node)
|
|
2280
|
+
v.node.replaceChildren(icon);
|
|
2281
|
+
else if (icon === undefined) {
|
|
2282
|
+
if (!v.node.firstElementChild?.classList.contains("knobs-activity-dot"))
|
|
2283
|
+
v.node.innerHTML = '<i class="knobs-activity-dot"></i>';
|
|
2284
|
+
} else if (icon.trimStart().startsWith("<")) {
|
|
2285
|
+
if (v.node.innerHTML !== icon)
|
|
2286
|
+
v.node.innerHTML = icon;
|
|
2287
|
+
} else {
|
|
2288
|
+
const glyph = doc.createElement("span");
|
|
2289
|
+
glyph.className = "knobs-activity-glyph";
|
|
2290
|
+
glyph.textContent = icon;
|
|
2291
|
+
v.node.replaceChildren(glyph);
|
|
2292
|
+
}
|
|
2293
|
+
const kids = e.children?.length ?? 0;
|
|
2294
|
+
v.actor.textContent = e.actor ?? "";
|
|
2295
|
+
const title = e.title ?? (kids ? groupTitle(kids) : "");
|
|
2296
|
+
if (v.title.textContent !== title)
|
|
2297
|
+
v.title.textContent = title;
|
|
2298
|
+
el.setAttribute("aria-label", [e.actor, title, e.time].filter(Boolean).join(", "));
|
|
2299
|
+
const parts = [];
|
|
2300
|
+
const thumbs = e.thumbs?.slice(0, 4) ?? [];
|
|
2301
|
+
const shown = [...v.thumbs.querySelectorAll("img")].map((i) => i.getAttribute("src"));
|
|
2302
|
+
if (thumbs.join(`
|
|
2303
|
+
`) !== shown.join(`
|
|
2304
|
+
`))
|
|
2305
|
+
v.thumbs.replaceChildren(...thumbs.map((src) => {
|
|
2306
|
+
const img = doc.createElement("img");
|
|
2307
|
+
img.src = src;
|
|
2308
|
+
img.alt = "";
|
|
2309
|
+
img.loading = "lazy";
|
|
2310
|
+
img.draggable = false;
|
|
2311
|
+
return img;
|
|
2312
|
+
}));
|
|
2313
|
+
if (thumbs.length)
|
|
2314
|
+
parts.push(v.thumbs);
|
|
2315
|
+
if (e.duration !== undefined) {
|
|
2316
|
+
const button = Boolean(options.onPlay);
|
|
2317
|
+
if (!v.chip || v.chip.tagName === "BUTTON" !== button) {
|
|
2318
|
+
v.chipRoll?.destroy();
|
|
2319
|
+
v.chipRoll = null;
|
|
2320
|
+
v.chip = doc.createElement(button ? "button" : "span");
|
|
2321
|
+
v.chip.className = "knobs-activity-chip";
|
|
2322
|
+
if (button) {
|
|
2323
|
+
v.chip.type = "button";
|
|
2324
|
+
v.chip.tabIndex = -1;
|
|
2325
|
+
v.chip.setAttribute("aria-label", "Play");
|
|
2326
|
+
}
|
|
2327
|
+
v.chip.innerHTML = `${PLAY}<span></span>`;
|
|
2328
|
+
}
|
|
2329
|
+
v.chipRoll = rollInto(v.chip.lastElementChild, v.chipRoll, formatDuration(e.duration));
|
|
2330
|
+
parts.push(v.chip);
|
|
2331
|
+
}
|
|
2332
|
+
if (kids)
|
|
2333
|
+
parts.push(v.caret);
|
|
2334
|
+
v.roll = rollInto(v.time, v.roll, e.time ?? "");
|
|
2335
|
+
if (e.time)
|
|
2336
|
+
parts.push(v.time);
|
|
2337
|
+
const now = [...v.meta.children];
|
|
2338
|
+
if (now.length !== parts.length || now.some((n, i) => n !== parts[i]))
|
|
2339
|
+
v.meta.replaceChildren(...parts);
|
|
2340
|
+
};
|
|
2341
|
+
const nodeY = (v) => v.y.x + heights[v.depth] / 2;
|
|
2342
|
+
let targets = new Map;
|
|
2343
|
+
const lineTop = new Spring(0, REFLOW.omega, REFLOW.zeta, 0.05);
|
|
2344
|
+
const lineBottom = new Spring(0, REFLOW.omega, REFLOW.zeta, 0.05);
|
|
2345
|
+
const hlY = new Spring(0, REFLOW.omega, REFLOW.zeta, 0.05);
|
|
2346
|
+
const hlH = new Spring(0, REFLOW.omega, REFLOW.zeta, 0.05);
|
|
2347
|
+
const height = new Spring(0, REFLOW.omega, REFLOW.zeta, 0.05);
|
|
2348
|
+
let following = false;
|
|
2349
|
+
let scrollHold = null;
|
|
2350
|
+
const place = (v, reduced) => {
|
|
2351
|
+
const p = Math.max(0, Math.min(1, v.p.x));
|
|
2352
|
+
let y = v.y.x;
|
|
2353
|
+
let clip = 0;
|
|
2354
|
+
let transform = "";
|
|
2355
|
+
let filter = "";
|
|
2356
|
+
if (v.under !== null) {
|
|
2357
|
+
const parent = views.get(v.under);
|
|
2358
|
+
const bottom = (parent ? parent.y.x : targets.get(v.under) ?? 0) + heights[0];
|
|
2359
|
+
clip = Math.min(heights[v.depth], Math.max(0, bottom - y));
|
|
2360
|
+
} else if (p < 1 && !reduced) {
|
|
2361
|
+
y += (1 - p) * SLIDE * v.from;
|
|
2362
|
+
transform = ` scale(${(0.97 + 0.03 * p).toFixed(4)})`;
|
|
2363
|
+
const blur = (1 - p) * 3;
|
|
2364
|
+
if (blur > 0.05)
|
|
2365
|
+
filter = `blur(${blur.toFixed(2)}px)`;
|
|
2366
|
+
}
|
|
2367
|
+
v.el.style.transform = `translate3d(0,${Math.round(y * 2) / 2}px,0)${transform}`;
|
|
2368
|
+
v.el.style.clipPath = clip > 0 ? `inset(${clip}px 0 0 0)` : "";
|
|
2369
|
+
v.el.style.opacity = p < 0.999 ? p.toFixed(3) : "";
|
|
2370
|
+
v.el.style.filter = filter;
|
|
2371
|
+
};
|
|
2372
|
+
const drawLine = () => {
|
|
2373
|
+
const nodes = [];
|
|
2374
|
+
for (const v of views.values()) {
|
|
2375
|
+
const p = Math.max(0, Math.min(1, v.p.x));
|
|
2376
|
+
if (p <= 0.001)
|
|
2377
|
+
continue;
|
|
2378
|
+
const y = nodeY(v);
|
|
2379
|
+
if (v.under !== null) {
|
|
2380
|
+
const parent = views.get(v.under);
|
|
2381
|
+
if (parent && y < parent.y.x + heights[0])
|
|
2382
|
+
continue;
|
|
2383
|
+
}
|
|
2384
|
+
nodes.push({ y, r: CLEAR[v.depth] * Math.min(1, p * 1.5) });
|
|
2385
|
+
}
|
|
2386
|
+
const segs = railSegments(nodes, lineTop.x, lineBottom.x);
|
|
2387
|
+
path.setAttribute("d", segs.map(([a, b]) => `M1 ${a.toFixed(2)}V${b.toFixed(2)}`).join(""));
|
|
2388
|
+
};
|
|
2389
|
+
const placeHighlight = () => {
|
|
2390
|
+
hl.style.transform = `translate3d(0,${Math.round(hlY.x * 2) / 2}px,0)`;
|
|
2391
|
+
hl.style.height = `${Math.max(0, hlH.x).toFixed(2)}px`;
|
|
2392
|
+
};
|
|
2393
|
+
const loop = frameLoop(win, (dt) => {
|
|
2394
|
+
const reduced = reducedMotion(win);
|
|
2395
|
+
let moving = false;
|
|
2396
|
+
for (const s of [lineTop, lineBottom, hlY, hlH, height]) {
|
|
2397
|
+
s.step(dt);
|
|
2398
|
+
if (!s.resting)
|
|
2399
|
+
moving = true;
|
|
2400
|
+
}
|
|
2401
|
+
for (const v of [...views.values()]) {
|
|
2402
|
+
v.y.step(dt);
|
|
2403
|
+
v.p.step(dt);
|
|
2404
|
+
if (v.y.resting && v.p.resting) {
|
|
2405
|
+
if (v.leaving) {
|
|
2406
|
+
dropView(v);
|
|
2407
|
+
continue;
|
|
2408
|
+
}
|
|
2409
|
+
v.under = null;
|
|
2410
|
+
v.from = 0;
|
|
2411
|
+
} else
|
|
2412
|
+
moving = true;
|
|
2413
|
+
place(v, reduced);
|
|
2414
|
+
}
|
|
2415
|
+
drawLine();
|
|
2416
|
+
placeHighlight();
|
|
2417
|
+
if (scrollHold && !following) {
|
|
2418
|
+
const { scroll, space: reserved } = holdScroll(scrollHold, height.x, root.clientHeight);
|
|
2419
|
+
space.style.height = `${Math.max(0, reserved).toFixed(1)}px`;
|
|
2420
|
+
root.scrollTop = scroll;
|
|
2421
|
+
} else
|
|
2422
|
+
space.style.height = `${Math.max(0, height.x).toFixed(1)}px`;
|
|
2423
|
+
if (following)
|
|
2424
|
+
root.scrollTop = newest === "bottom" ? root.scrollHeight : 0;
|
|
2425
|
+
if (!moving) {
|
|
2426
|
+
following = false;
|
|
2427
|
+
scrollHold = null;
|
|
2428
|
+
}
|
|
2429
|
+
return moving;
|
|
2430
|
+
});
|
|
2431
|
+
const atNewestEnd = () => newest === "bottom" ? root.scrollTop + root.clientHeight >= root.scrollHeight - 4 : root.scrollTop <= 4;
|
|
2432
|
+
function sync(cause, toggled) {
|
|
2433
|
+
const reduced = reducedMotion(win);
|
|
2434
|
+
const animate = cause !== "snap";
|
|
2435
|
+
const glide = cause === "open" ? OPEN : cause === "close" ? CLOSE : REFLOW;
|
|
2436
|
+
const before = [...views.values()].filter((v) => !v.leaving).map((v) => v.id);
|
|
2437
|
+
rows = flattenActivity(events, expanded);
|
|
2438
|
+
selected = keepSelection(rows, selected, (id) => index.get(id)?.parent);
|
|
2439
|
+
const stack = stackRows(rows.map((r) => heights[r.depth]), PAD);
|
|
2440
|
+
targets = new Map(rows.map((r, i) => [r.id, stack.y[i]]));
|
|
2441
|
+
const { entered } = diffActivity(before, rows.map((r) => r.id));
|
|
2442
|
+
const fresh = new Set(entered);
|
|
2443
|
+
let opened = 0;
|
|
2444
|
+
if (cause === "open") {
|
|
2445
|
+
for (const r of rows)
|
|
2446
|
+
if (r.parent === toggled && fresh.has(r.id))
|
|
2447
|
+
opened += heights[r.depth];
|
|
2448
|
+
}
|
|
2449
|
+
let shut = 0;
|
|
2450
|
+
const live = new Set(rows.map((r) => r.id));
|
|
2451
|
+
if (cause === "close") {
|
|
2452
|
+
for (const v of views.values())
|
|
2453
|
+
if (!live.has(v.id) && !v.leaving && index.get(v.id)?.parent === toggled)
|
|
2454
|
+
shut += heights[v.depth];
|
|
2455
|
+
}
|
|
2456
|
+
const following0 = animate && options.follow !== false && atNewestEnd();
|
|
2457
|
+
const lastIndex = rows.length - 1;
|
|
2458
|
+
rows.forEach((row, i) => {
|
|
2459
|
+
const goal = stack.y[i];
|
|
2460
|
+
let v = views.get(row.id);
|
|
2461
|
+
if (v && v.leaving) {
|
|
2462
|
+
v.leaving = false;
|
|
2463
|
+
delete v.el.dataset.leaving;
|
|
2464
|
+
tune(v.p, cause === "open" ? OPEN : BLOOM).set(1);
|
|
2465
|
+
}
|
|
2466
|
+
if (!v) {
|
|
2467
|
+
v = makeView(row);
|
|
2468
|
+
if (!animate) {
|
|
2469
|
+
v.y.snap(goal);
|
|
2470
|
+
v.p.snap(1);
|
|
2471
|
+
} else if (reduced) {
|
|
2472
|
+
v.y.snap(goal);
|
|
2473
|
+
tune(v.p, FADE).snap(0).set(1);
|
|
2474
|
+
} else {
|
|
2475
|
+
const parent = row.parent !== null ? views.get(row.parent) : undefined;
|
|
2476
|
+
if (parent && !parent.leaving) {
|
|
2477
|
+
v.under = row.parent;
|
|
2478
|
+
const drawer = cause === "open" && row.parent === toggled;
|
|
2479
|
+
v.y.snap(drawer ? goal - opened + parent.y.x - parent.y.target : parent.y.x);
|
|
2480
|
+
tune(v.y, drawer ? OPEN : glide).set(goal);
|
|
2481
|
+
tune(v.p, drawer ? OPEN : BLOOM).snap(0).set(1);
|
|
2482
|
+
} else {
|
|
2483
|
+
v.y.snap(goal);
|
|
2484
|
+
v.from = newest === "bottom" ? 1 : -1;
|
|
2485
|
+
tune(v.p, BLOOM).snap(0).set(1);
|
|
2486
|
+
}
|
|
2487
|
+
}
|
|
2488
|
+
} else if (!animate || reduced)
|
|
2489
|
+
v.y.snap(goal);
|
|
2490
|
+
else
|
|
2491
|
+
tune(v.y, glide).set(goal);
|
|
2492
|
+
bind(v, row);
|
|
2493
|
+
});
|
|
2494
|
+
for (const v of [...views.values()]) {
|
|
2495
|
+
if (live.has(v.id) || v.leaving)
|
|
2496
|
+
continue;
|
|
2497
|
+
if (!animate) {
|
|
2498
|
+
dropView(v);
|
|
2499
|
+
continue;
|
|
2500
|
+
}
|
|
2501
|
+
v.leaving = true;
|
|
2502
|
+
v.el.dataset.leaving = "";
|
|
2503
|
+
v.el.setAttribute("aria-selected", "false");
|
|
2504
|
+
if (reduced) {
|
|
2505
|
+
tune(v.p, FADE).set(0);
|
|
2506
|
+
continue;
|
|
2507
|
+
}
|
|
2508
|
+
const group = index.get(v.id)?.parent ?? null;
|
|
2509
|
+
const parent = group !== null ? views.get(group) : undefined;
|
|
2510
|
+
if (parent && !parent.leaving && v.depth === 1) {
|
|
2511
|
+
v.under = group;
|
|
2512
|
+
const drawer = cause === "close" && group === toggled;
|
|
2513
|
+
tune(v.y, drawer ? CLOSE : glide).set(drawer ? v.y.target - shut : targets.get(group) ?? parent.y.target);
|
|
2514
|
+
tune(v.p, drawer ? CLOSE : EXIT).set(0);
|
|
2515
|
+
} else {
|
|
2516
|
+
v.from = 0;
|
|
2517
|
+
tune(v.p, EXIT).set(0);
|
|
2518
|
+
}
|
|
2519
|
+
}
|
|
2520
|
+
const first = rows[0];
|
|
2521
|
+
const last = rows[lastIndex];
|
|
2522
|
+
const firstY = first ? targets.get(first.id) + heights[first.depth] / 2 : PAD;
|
|
2523
|
+
const lastY = last ? targets.get(last.id) + heights[last.depth] / 2 : PAD;
|
|
2524
|
+
const bottom = stack.bottom + PAD;
|
|
2525
|
+
if (!animate || reduced) {
|
|
2526
|
+
lineTop.snap(firstY);
|
|
2527
|
+
lineBottom.snap(lastY);
|
|
2528
|
+
height.snap(bottom);
|
|
2529
|
+
} else {
|
|
2530
|
+
if (!before.length) {
|
|
2531
|
+
lineTop.snap(firstY);
|
|
2532
|
+
lineBottom.snap(firstY);
|
|
2533
|
+
}
|
|
2534
|
+
tune(lineTop, glide).set(firstY);
|
|
2535
|
+
tune(lineBottom, cause === "set" ? BLOOM : glide).set(lastY);
|
|
2536
|
+
tune(height, glide).set(bottom);
|
|
2537
|
+
}
|
|
2538
|
+
moveHighlight(animate && !reduced ? glide : null);
|
|
2539
|
+
hl.hidden = !selected;
|
|
2540
|
+
empty.hidden = rows.length > 0;
|
|
2541
|
+
empty.textContent = options.empty ?? "Nothing yet";
|
|
2542
|
+
if (selected)
|
|
2543
|
+
root.setAttribute("aria-activedescendant", domId(selected));
|
|
2544
|
+
else
|
|
2545
|
+
root.removeAttribute("aria-activedescendant");
|
|
2546
|
+
following = following || following0 && cause === "set" && fresh.size > 0;
|
|
2547
|
+
scrollHold = animate && !following && !reduced ? { from: root.scrollTop, h0: height.x, h1: height.target } : null;
|
|
2548
|
+
for (const v of views.values())
|
|
2549
|
+
place(v, reduced);
|
|
2550
|
+
drawLine();
|
|
2551
|
+
placeHighlight();
|
|
2552
|
+
space.style.height = `${height.x.toFixed(1)}px`;
|
|
2553
|
+
loop.wake();
|
|
2554
|
+
}
|
|
2555
|
+
const moveHighlight = (motion) => {
|
|
2556
|
+
if (!showing)
|
|
2557
|
+
return;
|
|
2558
|
+
const y = selected ? targets.get(selected) : undefined;
|
|
2559
|
+
if (y === undefined)
|
|
2560
|
+
return;
|
|
2561
|
+
const h = heights[rows.find((r) => r.id === selected)?.depth ?? 0];
|
|
2562
|
+
if (!motion) {
|
|
2563
|
+
hlY.snap(y);
|
|
2564
|
+
hlH.snap(h);
|
|
2565
|
+
return;
|
|
2566
|
+
}
|
|
2567
|
+
tune(hlY, motion).set(y);
|
|
2568
|
+
tune(hlH, motion).set(h);
|
|
2569
|
+
};
|
|
2570
|
+
const setShowing = (input) => {
|
|
2571
|
+
const next = showsSelection(showing, input, keysFrom);
|
|
2572
|
+
if (next === showing)
|
|
2573
|
+
return;
|
|
2574
|
+
showing = next;
|
|
2575
|
+
root.toggleAttribute("data-keys", next);
|
|
2576
|
+
};
|
|
2577
|
+
const rebind = () => {
|
|
2578
|
+
for (const row of rows) {
|
|
2579
|
+
const v = views.get(row.id);
|
|
2580
|
+
if (v && !v.leaving)
|
|
2581
|
+
bind(v, row);
|
|
2582
|
+
}
|
|
2583
|
+
};
|
|
2584
|
+
const reveal = (id) => {
|
|
2585
|
+
const y = targets.get(id);
|
|
2586
|
+
if (y === undefined)
|
|
2587
|
+
return;
|
|
2588
|
+
const h = heights[rows.find((r) => r.id === id)?.depth ?? 0];
|
|
2589
|
+
const top = root.scrollTop;
|
|
2590
|
+
const view = root.clientHeight;
|
|
2591
|
+
if (view <= 0 || root.scrollHeight <= view)
|
|
2592
|
+
return;
|
|
2593
|
+
const behavior = reducedMotion(win) ? "auto" : "smooth";
|
|
2594
|
+
if (y - PAD < top)
|
|
2595
|
+
root.scrollTo({ top: y - PAD, behavior });
|
|
2596
|
+
else if (y + h + PAD > top + view)
|
|
2597
|
+
root.scrollTo({ top: y + h + PAD - view, behavior });
|
|
2598
|
+
};
|
|
2599
|
+
const choose = (id, source) => {
|
|
2600
|
+
if (source === "key") {
|
|
2601
|
+
const wasShowing = showing;
|
|
2602
|
+
setShowing("key");
|
|
2603
|
+
if (!wasShowing)
|
|
2604
|
+
moveHighlight(null);
|
|
2605
|
+
} else if (source === "pointer")
|
|
2606
|
+
setShowing("pointer");
|
|
2607
|
+
if (id === selected)
|
|
2608
|
+
return;
|
|
2609
|
+
const from = selected ? targets.get(selected) : undefined;
|
|
2610
|
+
selected = id;
|
|
2611
|
+
rebind();
|
|
2612
|
+
hl.hidden = !id;
|
|
2613
|
+
if (id) {
|
|
2614
|
+
const to = targets.get(id);
|
|
2615
|
+
const far = from !== undefined && to !== undefined && Math.abs(to - from) > 400;
|
|
2616
|
+
const reduced = reducedMotion(win);
|
|
2617
|
+
moveHighlight(reduced || from === undefined ? null : springMotion({ duration: far ? 0.42 : 0.3, bounce: 0 }));
|
|
2618
|
+
root.setAttribute("aria-activedescendant", domId(id));
|
|
2619
|
+
if (source === "key") {
|
|
2620
|
+
scrollHold = null;
|
|
2621
|
+
reveal(id);
|
|
2622
|
+
}
|
|
2623
|
+
const event = index.get(id)?.event;
|
|
2624
|
+
if (event && source !== "code")
|
|
2625
|
+
options.onSelect?.(id, event, source);
|
|
2626
|
+
} else
|
|
2627
|
+
root.removeAttribute("aria-activedescendant");
|
|
2628
|
+
loop.wake();
|
|
2629
|
+
};
|
|
2630
|
+
const toggle = (id, open) => {
|
|
2631
|
+
const row = rows.find((r) => r.id === id);
|
|
2632
|
+
if (!row?.group || row.expanded === open)
|
|
2633
|
+
return;
|
|
2634
|
+
if (open)
|
|
2635
|
+
expanded.add(id);
|
|
2636
|
+
else
|
|
2637
|
+
expanded.delete(id);
|
|
2638
|
+
sync(open ? "open" : "close", id);
|
|
2639
|
+
options.onToggle?.(id, open);
|
|
2640
|
+
};
|
|
2641
|
+
root.addEventListener("pointerdown", (e) => {
|
|
2642
|
+
if (e.button !== 0)
|
|
2643
|
+
return;
|
|
2644
|
+
setShowing("pointer");
|
|
2645
|
+
const el = e.target.closest(".knobs-activity-row");
|
|
2646
|
+
const id = el?.dataset.id;
|
|
2647
|
+
if (!el || !id || el.dataset.leaving !== undefined)
|
|
2648
|
+
return;
|
|
2649
|
+
e.preventDefault();
|
|
2650
|
+
root.focus({ preventScroll: true });
|
|
2651
|
+
if (e.target.closest(".knobs-activity-chip"))
|
|
2652
|
+
return;
|
|
2653
|
+
const row = rows.find((r) => r.id === id);
|
|
2654
|
+
choose(id, "pointer");
|
|
2655
|
+
if (row?.group)
|
|
2656
|
+
toggle(id, !row.expanded);
|
|
2657
|
+
});
|
|
2658
|
+
root.addEventListener("click", (e) => {
|
|
2659
|
+
const chip = e.target.closest(".knobs-activity-chip");
|
|
2660
|
+
const id = chip?.closest(".knobs-activity-row")?.dataset.id;
|
|
2661
|
+
const event = id ? index.get(id)?.event : undefined;
|
|
2662
|
+
if (chip && id && event && options.onPlay) {
|
|
2663
|
+
e.stopPropagation();
|
|
2664
|
+
options.onPlay(id, event);
|
|
2665
|
+
}
|
|
2666
|
+
});
|
|
2667
|
+
const release = () => {
|
|
2668
|
+
scrollHold = null;
|
|
2669
|
+
following = false;
|
|
2670
|
+
};
|
|
2671
|
+
root.addEventListener("wheel", release, { passive: true });
|
|
2672
|
+
root.addEventListener("touchstart", release, { passive: true });
|
|
2673
|
+
const onPointer = () => setShowing("pointer");
|
|
2674
|
+
if (keysFrom === "page")
|
|
2675
|
+
win.addEventListener("pointerdown", onPointer, true);
|
|
2676
|
+
const onKey = (e) => {
|
|
2677
|
+
if (e.defaultPrevented || e.metaKey || e.ctrlKey || e.altKey)
|
|
2678
|
+
return;
|
|
2679
|
+
if (keysFrom === "page") {
|
|
2680
|
+
if (e.target !== root && (isTypingTarget(e.target) || !root.getClientRects().length))
|
|
2681
|
+
return;
|
|
2682
|
+
const t = e.target;
|
|
2683
|
+
if (t !== root && t?.closest?.(".knobs-panel,[role=tree],[role=listbox],[role=grid],[role=slider]"))
|
|
2684
|
+
return;
|
|
2685
|
+
}
|
|
2686
|
+
let handled = true;
|
|
2687
|
+
if (isActivityKey(e.key) && !e.shiftKey) {
|
|
2688
|
+
const r = navigateActivity(rows, selected, e.key);
|
|
2689
|
+
if (!takesActivityKey(e.key, r, rows.length))
|
|
2690
|
+
return;
|
|
2691
|
+
if (r.toggle) {
|
|
2692
|
+
setShowing("key");
|
|
2693
|
+
toggle(r.toggle.id, r.toggle.expanded);
|
|
2694
|
+
} else if (r.select)
|
|
2695
|
+
choose(r.select, "key");
|
|
2696
|
+
else
|
|
2697
|
+
setShowing("key");
|
|
2698
|
+
} else if (e.key === "Enter" && selected) {
|
|
2699
|
+
setShowing("key");
|
|
2700
|
+
const row = rows.find((r) => r.id === selected);
|
|
2701
|
+
const event = index.get(selected)?.event;
|
|
2702
|
+
if (options.onActivate && event)
|
|
2703
|
+
options.onActivate(selected, event);
|
|
2704
|
+
else if (row?.group)
|
|
2705
|
+
toggle(row.id, !row.expanded);
|
|
2706
|
+
else
|
|
2707
|
+
handled = false;
|
|
2708
|
+
} else
|
|
2709
|
+
handled = false;
|
|
2710
|
+
if (handled) {
|
|
2711
|
+
e.preventDefault();
|
|
2712
|
+
e.stopPropagation();
|
|
2713
|
+
}
|
|
2714
|
+
};
|
|
2715
|
+
if (keysFrom === "page")
|
|
2716
|
+
win.addEventListener("keydown", onKey);
|
|
2717
|
+
else
|
|
2718
|
+
root.addEventListener("keydown", onKey);
|
|
2719
|
+
root.addEventListener("blur", () => setShowing("blur"));
|
|
2720
|
+
const remeasure = () => {
|
|
2721
|
+
const was = heights.join();
|
|
2722
|
+
measure();
|
|
2723
|
+
if (heights.join() !== was)
|
|
2724
|
+
sync("snap");
|
|
2725
|
+
};
|
|
2726
|
+
const ro = new ResizeObserver(remeasure);
|
|
2727
|
+
ro.observe(root);
|
|
2728
|
+
root.addEventListener("knobs-restyle", () => {
|
|
2729
|
+
measure();
|
|
2730
|
+
sync("snap");
|
|
2731
|
+
});
|
|
2732
|
+
sync("snap");
|
|
2733
|
+
const handle = {
|
|
2734
|
+
element: root,
|
|
2735
|
+
set(next) {
|
|
2736
|
+
handle.update({ events: next });
|
|
2737
|
+
},
|
|
2738
|
+
add(event) {
|
|
2739
|
+
handle.set(newest === "bottom" ? [...events, event] : [event, ...events]);
|
|
2740
|
+
},
|
|
2741
|
+
update(next) {
|
|
2742
|
+
if (next.newest)
|
|
2743
|
+
newest = next.newest;
|
|
2744
|
+
if (next.events) {
|
|
2745
|
+
events = next.events;
|
|
2746
|
+
index = indexActivity(events);
|
|
2747
|
+
for (const id of [...expanded])
|
|
2748
|
+
if (!index.has(id))
|
|
2749
|
+
expanded.delete(id);
|
|
2750
|
+
}
|
|
2751
|
+
sync("set");
|
|
2752
|
+
},
|
|
2753
|
+
select(id) {
|
|
2754
|
+
if (id !== null) {
|
|
2755
|
+
const parent = index.get(id)?.parent;
|
|
2756
|
+
if (parent && !expanded.has(parent)) {
|
|
2757
|
+
expanded.add(parent);
|
|
2758
|
+
sync("open", parent);
|
|
2759
|
+
}
|
|
2760
|
+
}
|
|
2761
|
+
choose(id, "code");
|
|
2762
|
+
},
|
|
2763
|
+
get selected() {
|
|
2764
|
+
return selected;
|
|
2765
|
+
},
|
|
2766
|
+
expand: (id, open = !expanded.has(id)) => toggle(id, open),
|
|
2767
|
+
isExpanded: (id) => expanded.has(id),
|
|
2768
|
+
focus: () => root.focus({ preventScroll: true }),
|
|
2769
|
+
destroy() {
|
|
2770
|
+
loop.stop();
|
|
2771
|
+
ro.disconnect();
|
|
2772
|
+
scrollbar.dispose();
|
|
2773
|
+
if (keysFrom === "page") {
|
|
2774
|
+
win.removeEventListener("keydown", onKey);
|
|
2775
|
+
win.removeEventListener("pointerdown", onPointer, true);
|
|
2776
|
+
}
|
|
2777
|
+
for (const v of [...views.values()])
|
|
2778
|
+
dropView(v);
|
|
2779
|
+
root.remove();
|
|
2780
|
+
},
|
|
2781
|
+
dispose: () => handle.destroy()
|
|
2782
|
+
};
|
|
2783
|
+
return handle;
|
|
2784
|
+
}
|
|
2785
|
+
|
|
2786
|
+
// src/ui/bento-model.ts
|
|
2787
|
+
var FIT_MAX = 24;
|
|
2788
|
+
var clamp = (x, a, b) => Math.min(b, Math.max(a, x));
|
|
2789
|
+
var sum = (xs) => xs.reduce((s, x) => s + x, 0);
|
|
2790
|
+
var rowTarget = (width, height) => clamp(Math.round(Math.min(height / 3, width / 2.6)), 120, 420);
|
|
2791
|
+
function justify(aspects, start, end, width, target, gap, tol) {
|
|
2792
|
+
const rows = [];
|
|
2793
|
+
let i = start;
|
|
2794
|
+
while (i < end) {
|
|
2795
|
+
let total = 0;
|
|
2796
|
+
let j = i;
|
|
2797
|
+
let h = Number.POSITIVE_INFINITY;
|
|
2798
|
+
let closed = false;
|
|
2799
|
+
while (j < end) {
|
|
2800
|
+
const k = j - i + 1;
|
|
2801
|
+
const next = (width - (k - 1) * gap) / (total + aspects[j]);
|
|
2802
|
+
if (next <= target) {
|
|
2803
|
+
if (k === 1 || Math.abs(Math.log(next / target)) <= Math.abs(Math.log(h / target))) {
|
|
2804
|
+
total += aspects[j];
|
|
2805
|
+
j++;
|
|
2806
|
+
h = next;
|
|
2807
|
+
}
|
|
2808
|
+
closed = true;
|
|
2809
|
+
break;
|
|
2810
|
+
}
|
|
2811
|
+
total += aspects[j];
|
|
2812
|
+
h = next;
|
|
2813
|
+
j++;
|
|
2814
|
+
}
|
|
2815
|
+
const slice = aspects.slice(i, j);
|
|
2816
|
+
if (closed || h <= target * 1.25) {
|
|
2817
|
+
rows.push({ start: i, end: j, h, ws: slice.map((a) => a * h) });
|
|
2818
|
+
} else {
|
|
2819
|
+
const free = width - (j - i - 1) * gap;
|
|
2820
|
+
const f = free / (target * total);
|
|
2821
|
+
const ws = slice.map((a) => a * target * (f - 1 <= tol ? f : 1));
|
|
2822
|
+
rows.push({ start: i, end: j, h: target, ws });
|
|
2823
|
+
}
|
|
2824
|
+
i = j;
|
|
2825
|
+
}
|
|
2826
|
+
return rows;
|
|
2827
|
+
}
|
|
2828
|
+
function partition(xs, k) {
|
|
2829
|
+
const n = xs.length;
|
|
2830
|
+
k = clamp(k, 1, n);
|
|
2831
|
+
const prefix = [0];
|
|
2832
|
+
for (const x of xs)
|
|
2833
|
+
prefix.push(prefix.at(-1) + x);
|
|
2834
|
+
const cost = Array.from({ length: k + 1 }, () => new Float64Array(n + 1).fill(Infinity));
|
|
2835
|
+
const cut = Array.from({ length: k + 1 }, () => new Int32Array(n + 1));
|
|
2836
|
+
cost[0][0] = 0;
|
|
2837
|
+
for (let j = 1;j <= k; j++)
|
|
2838
|
+
for (let i = j;i <= n; i++)
|
|
2839
|
+
for (let m = j - 1;m < i; m++) {
|
|
2840
|
+
const c = Math.max(cost[j - 1][m], prefix[i] - prefix[m]);
|
|
2841
|
+
if (c < cost[j][i]) {
|
|
2842
|
+
cost[j][i] = c;
|
|
2843
|
+
cut[j][i] = m;
|
|
2844
|
+
}
|
|
2845
|
+
}
|
|
2846
|
+
const counts = [];
|
|
2847
|
+
for (let j = k, i = n;j > 0; j--) {
|
|
2848
|
+
const m = cut[j][i];
|
|
2849
|
+
counts.unshift(i - m);
|
|
2850
|
+
i = m;
|
|
2851
|
+
}
|
|
2852
|
+
return counts;
|
|
2853
|
+
}
|
|
2854
|
+
function rowsBlock(aspects, w, h, gap, tol) {
|
|
2855
|
+
const n = aspects.length;
|
|
2856
|
+
if (!n || w <= 0 || h <= 0)
|
|
2857
|
+
return null;
|
|
2858
|
+
let best = null;
|
|
2859
|
+
const most = Math.min(n, Math.max(2, Math.ceil(Math.sqrt(n) * 2)));
|
|
2860
|
+
for (let r = 1;r <= most; r++) {
|
|
2861
|
+
const counts = partition(aspects, r);
|
|
2862
|
+
let at = 0;
|
|
2863
|
+
const groups = counts.map((c) => aspects.slice(at, at += c));
|
|
2864
|
+
const heights = groups.map((g) => (w - (g.length - 1) * gap) / sum(g));
|
|
2865
|
+
const budget = h - (r - 1) * gap;
|
|
2866
|
+
if (budget <= 0 || heights.some((x) => x <= 0))
|
|
2867
|
+
break;
|
|
2868
|
+
const s = Math.min(1, budget / sum(heights));
|
|
2869
|
+
const fill = s < 1 && 1 / s - 1 <= tol ? 1 / s : 1;
|
|
2870
|
+
const rects = [];
|
|
2871
|
+
let y = 0;
|
|
2872
|
+
let blockW = 0;
|
|
2873
|
+
groups.forEach((g, k) => {
|
|
2874
|
+
const rh = heights[k] * s;
|
|
2875
|
+
const rowW = sum(g.map((a) => a * rh * fill)) + (g.length - 1) * gap;
|
|
2876
|
+
blockW = Math.max(blockW, rowW);
|
|
2877
|
+
let x = 0;
|
|
2878
|
+
for (const a of g) {
|
|
2879
|
+
rects.push({ x, y, w: a * rh * fill, h: rh });
|
|
2880
|
+
x += a * rh * fill + gap;
|
|
2881
|
+
}
|
|
2882
|
+
y += rh + gap;
|
|
2883
|
+
});
|
|
2884
|
+
let i = 0;
|
|
2885
|
+
groups.forEach((g, k) => {
|
|
2886
|
+
const rowW = sum(g.map((a) => a * heights[k] * s * fill)) + (g.length - 1) * gap;
|
|
2887
|
+
const dx = (blockW - rowW) / 2;
|
|
2888
|
+
for (let m = 0;m < g.length; m++, i++) {
|
|
2889
|
+
const q = rects[i];
|
|
2890
|
+
rects[i] = { ...q, x: q.x + dx };
|
|
2891
|
+
}
|
|
2892
|
+
});
|
|
2893
|
+
const area = sum(rects.map((q) => q.w * q.h));
|
|
2894
|
+
const block = {
|
|
2895
|
+
rects,
|
|
2896
|
+
w: blockW,
|
|
2897
|
+
h: y - gap,
|
|
2898
|
+
area,
|
|
2899
|
+
crop: fill - 1,
|
|
2900
|
+
minH: Math.min(...rects.map((q) => q.h))
|
|
2901
|
+
};
|
|
2902
|
+
if (!best || block.area > best.area * 1.0001)
|
|
2903
|
+
best = block;
|
|
2904
|
+
}
|
|
2905
|
+
return best;
|
|
2906
|
+
}
|
|
2907
|
+
var offset = (rects, dx, dy) => rects.map((q) => ({ x: q.x + dx, y: q.y + dy, w: q.w, h: q.h }));
|
|
2908
|
+
function heroBlock(aspects, hero, w, h, gap, tol) {
|
|
2909
|
+
const a = aspects[hero];
|
|
2910
|
+
const rest = aspects.filter((_, i) => i !== hero);
|
|
2911
|
+
if (!rest.length)
|
|
2912
|
+
return null;
|
|
2913
|
+
let best = null;
|
|
2914
|
+
const place = (heroRect, restBlock, restAt, bw, bh) => {
|
|
2915
|
+
if (!restBlock)
|
|
2916
|
+
return;
|
|
2917
|
+
const [rx, ry] = restAt(restBlock);
|
|
2918
|
+
const others = offset(restBlock.rects, rx, ry);
|
|
2919
|
+
const rects = [...others.slice(0, hero), heroRect, ...others.slice(hero)];
|
|
2920
|
+
const area = heroRect.w * heroRect.h + restBlock.area;
|
|
2921
|
+
if (!best || area > best.area * 1.0001)
|
|
2922
|
+
best = { rects, w: bw, h: bh, area, crop: restBlock.crop, minH: restBlock.minH };
|
|
2923
|
+
};
|
|
2924
|
+
for (let f = 0.4;f <= 0.761; f += 0.04) {
|
|
2925
|
+
{
|
|
2926
|
+
const hw = Math.min(w * f, h * a);
|
|
2927
|
+
const hh = hw / a;
|
|
2928
|
+
const block = rowsBlock(rest, w - hw - gap, h, gap, tol);
|
|
2929
|
+
if (block) {
|
|
2930
|
+
const bw = hw + gap + block.w;
|
|
2931
|
+
const bh = Math.max(hh, block.h);
|
|
2932
|
+
place({ x: 0, y: (bh - hh) / 2, w: hw, h: hh }, block, (b) => [hw + gap, (bh - b.h) / 2], bw, bh);
|
|
2933
|
+
}
|
|
2934
|
+
}
|
|
2935
|
+
{
|
|
2936
|
+
const hh = Math.min(h * f, w / a);
|
|
2937
|
+
const hw = hh * a;
|
|
2938
|
+
const block = rowsBlock(rest, w, h - hh - gap, gap, tol);
|
|
2939
|
+
if (block) {
|
|
2940
|
+
const bw = Math.max(hw, block.w);
|
|
2941
|
+
place({ x: (bw - hw) / 2, y: 0, w: hw, h: hh }, block, (b) => [(bw - b.w) / 2, hh + gap], bw, hh + gap + block.h);
|
|
2942
|
+
}
|
|
2943
|
+
}
|
|
2944
|
+
}
|
|
2945
|
+
return best;
|
|
2946
|
+
}
|
|
2947
|
+
function planBento(aspects, layout) {
|
|
2948
|
+
const gap = layout.gap ?? 2;
|
|
2949
|
+
const pad = layout.padding ?? gap;
|
|
2950
|
+
const tol = layout.tolerance ?? 0.08;
|
|
2951
|
+
const vw = Math.max(0, layout.width);
|
|
2952
|
+
const vh = Math.max(0, layout.height);
|
|
2953
|
+
const w = Math.max(1, vw - pad * 2);
|
|
2954
|
+
const h = Math.max(1, vh - pad * 2);
|
|
2955
|
+
const as = aspects.map((a) => Number.isFinite(a) && a > 0 ? clamp(a, 0.05, 20) : 1);
|
|
2956
|
+
const empty = {
|
|
2957
|
+
mode: "rows",
|
|
2958
|
+
rects: [],
|
|
2959
|
+
width: vw,
|
|
2960
|
+
height: 0,
|
|
2961
|
+
coverage: 0,
|
|
2962
|
+
crop: 0,
|
|
2963
|
+
headers: []
|
|
2964
|
+
};
|
|
2965
|
+
if (!as.length || vw <= 0)
|
|
2966
|
+
return empty;
|
|
2967
|
+
const grouped = layout.groups?.some((g) => g !== undefined) ?? false;
|
|
2968
|
+
const minTile = layout.minTile ?? Math.max(80, vh / 6);
|
|
2969
|
+
if ((layout.fit ?? true) && !grouped && as.length <= FIT_MAX && vh > 0) {
|
|
2970
|
+
const rows = rowsBlock(as, w, h, gap, tol);
|
|
2971
|
+
let pick = rows && rows.minH >= minTile ? { mode: "fit", block: rows } : null;
|
|
2972
|
+
const asked = layout.hero !== undefined && layout.hero >= 0 && layout.hero < as.length;
|
|
2973
|
+
const hero = as.length > 2 ? heroBlock(as, asked ? layout.hero : 0, w, h, gap, tol) : null;
|
|
2974
|
+
if (hero && hero.minH >= minTile && (asked || !pick || hero.area > pick.block.area * 1.04))
|
|
2975
|
+
pick = { mode: "hero", block: hero };
|
|
2976
|
+
if (pick) {
|
|
2977
|
+
const { block } = pick;
|
|
2978
|
+
const rects = offset(block.rects, pad + (w - block.w) / 2, pad + (h - block.h) / 2);
|
|
2979
|
+
return {
|
|
2980
|
+
mode: pick.mode,
|
|
2981
|
+
rects,
|
|
2982
|
+
width: vw,
|
|
2983
|
+
height: vh,
|
|
2984
|
+
coverage: block.area / (vw * vh),
|
|
2985
|
+
crop: block.crop,
|
|
2986
|
+
headers: []
|
|
2987
|
+
};
|
|
2988
|
+
}
|
|
2989
|
+
}
|
|
2990
|
+
const target = layout.rowHeight ?? rowTarget(vw, vh);
|
|
2991
|
+
const header = layout.header ?? 28;
|
|
2992
|
+
const rects = new Array(as.length);
|
|
2993
|
+
const headers = [];
|
|
2994
|
+
let y = pad;
|
|
2995
|
+
let crop = 0;
|
|
2996
|
+
let area = 0;
|
|
2997
|
+
let i = 0;
|
|
2998
|
+
while (i < as.length) {
|
|
2999
|
+
const group = layout.groups?.[i];
|
|
3000
|
+
let end = i + 1;
|
|
3001
|
+
while (end < as.length && layout.groups?.[end] === group)
|
|
3002
|
+
end++;
|
|
3003
|
+
if (group !== undefined) {
|
|
3004
|
+
if (i > 0)
|
|
3005
|
+
y += header / 2;
|
|
3006
|
+
headers.push({ group, y, index: i });
|
|
3007
|
+
y += header;
|
|
3008
|
+
}
|
|
3009
|
+
for (const row of justify(as, i, end, w, target, gap, tol)) {
|
|
3010
|
+
let x = pad;
|
|
3011
|
+
row.ws.forEach((rw, k) => {
|
|
3012
|
+
rects[row.start + k] = { x, y, w: rw, h: row.h };
|
|
3013
|
+
crop = Math.max(crop, rw / (as[row.start + k] * row.h) - 1);
|
|
3014
|
+
area += rw * row.h;
|
|
3015
|
+
x += rw + gap;
|
|
3016
|
+
});
|
|
3017
|
+
y += row.h + gap;
|
|
3018
|
+
}
|
|
3019
|
+
i = end;
|
|
3020
|
+
}
|
|
3021
|
+
const height = y - gap + pad;
|
|
3022
|
+
return { mode: "rows", rects, width: vw, height, coverage: area / (vw * height), crop, headers };
|
|
3023
|
+
}
|
|
3024
|
+
function windowOf(plan, top, bottom) {
|
|
3025
|
+
const { rects } = plan;
|
|
3026
|
+
if (plan.mode !== "rows")
|
|
3027
|
+
return rects.map((_, i) => i);
|
|
3028
|
+
let lo = 0;
|
|
3029
|
+
let hi = rects.length;
|
|
3030
|
+
while (lo < hi) {
|
|
3031
|
+
const mid = lo + hi >> 1;
|
|
3032
|
+
if (rects[mid].y + rects[mid].h < top)
|
|
3033
|
+
lo = mid + 1;
|
|
3034
|
+
else
|
|
3035
|
+
hi = mid;
|
|
3036
|
+
}
|
|
3037
|
+
const out = [];
|
|
3038
|
+
for (let i = lo;i < rects.length && rects[i].y <= bottom; i++)
|
|
3039
|
+
out.push(i);
|
|
3040
|
+
return out;
|
|
1005
3041
|
}
|
|
1006
|
-
|
|
1007
|
-
|
|
1008
|
-
|
|
1009
|
-
|
|
1010
|
-
|
|
1011
|
-
|
|
1012
|
-
|
|
1013
|
-
|
|
1014
|
-
|
|
3042
|
+
var overlapY = (a, b) => Math.min(a.y + a.h, b.y + b.h) - Math.max(a.y, b.y) >= Math.min(a.h, b.h) * 0.5;
|
|
3043
|
+
function neighbour(rects, from, dir) {
|
|
3044
|
+
const f = rects[from];
|
|
3045
|
+
if (!f)
|
|
3046
|
+
return from;
|
|
3047
|
+
const fx = f.x + f.w / 2;
|
|
3048
|
+
const fy = f.y + f.h / 2;
|
|
3049
|
+
if (dir === "left" || dir === "right") {
|
|
3050
|
+
const right = dir === "right";
|
|
3051
|
+
let best = -1;
|
|
3052
|
+
let bestD = Number.POSITIVE_INFINITY;
|
|
3053
|
+
rects.forEach((r, i) => {
|
|
3054
|
+
if (i === from || !overlapY(f, r))
|
|
3055
|
+
return;
|
|
3056
|
+
const d = (r.x + r.w / 2 - fx) * (right ? 1 : -1);
|
|
3057
|
+
if (d > 1 && d < bestD) {
|
|
3058
|
+
bestD = d;
|
|
3059
|
+
best = i;
|
|
3060
|
+
}
|
|
3061
|
+
});
|
|
3062
|
+
if (best >= 0)
|
|
3063
|
+
return best;
|
|
3064
|
+
return clamp(from + (right ? 1 : -1), 0, rects.length - 1);
|
|
3065
|
+
}
|
|
3066
|
+
const down = dir === "down";
|
|
3067
|
+
let edge = Number.POSITIVE_INFINITY;
|
|
3068
|
+
for (const r of rects) {
|
|
3069
|
+
const d = (r.y + r.h / 2 - fy) * (down ? 1 : -1);
|
|
3070
|
+
if (d > f.h * 0.25 && !overlapY(f, r))
|
|
3071
|
+
edge = Math.min(edge, down ? r.y : -(r.y + r.h));
|
|
3072
|
+
}
|
|
3073
|
+
if (!Number.isFinite(edge))
|
|
3074
|
+
return from;
|
|
3075
|
+
let best = from;
|
|
3076
|
+
let bestD = Number.POSITIVE_INFINITY;
|
|
3077
|
+
rects.forEach((r, i) => {
|
|
3078
|
+
const d = (r.y + r.h / 2 - fy) * (down ? 1 : -1);
|
|
3079
|
+
if (i === from || d <= f.h * 0.25 || overlapY(f, r))
|
|
3080
|
+
return;
|
|
3081
|
+
const near = down ? r.y - edge : -(r.y + r.h) - edge;
|
|
3082
|
+
if (near > Math.min(r.h, f.h) * 0.5)
|
|
3083
|
+
return;
|
|
3084
|
+
const dx = Math.abs(r.x + r.w / 2 - fx);
|
|
3085
|
+
if (dx < bestD) {
|
|
3086
|
+
bestD = dx;
|
|
3087
|
+
best = i;
|
|
3088
|
+
}
|
|
3089
|
+
});
|
|
3090
|
+
return best;
|
|
1015
3091
|
}
|
|
1016
|
-
|
|
1017
|
-
|
|
1018
|
-
|
|
1019
|
-
|
|
3092
|
+
function fitRect(aspect, w, h, inset = 0) {
|
|
3093
|
+
const bw = Math.max(1, w - inset * 2);
|
|
3094
|
+
const bh = Math.max(1, h - inset * 2);
|
|
3095
|
+
const fw = Math.min(bw, bh * aspect);
|
|
3096
|
+
const fh = fw / aspect;
|
|
3097
|
+
return { x: (w - fw) / 2, y: (h - fh) / 2, w: fw, h: fh };
|
|
1020
3098
|
}
|
|
1021
|
-
function
|
|
1022
|
-
|
|
1023
|
-
|
|
1024
|
-
const tick = (now) => {
|
|
1025
|
-
const dt = last ? Math.min(0.064, (now - last) / 1000) : 1 / 60;
|
|
1026
|
-
last = now;
|
|
1027
|
-
raf = step(dt) ? win.requestAnimationFrame(tick) : 0;
|
|
1028
|
-
if (!raf)
|
|
1029
|
-
last = 0;
|
|
1030
|
-
};
|
|
1031
|
-
return {
|
|
1032
|
-
wake() {
|
|
1033
|
-
if (!raf)
|
|
1034
|
-
raf = win.requestAnimationFrame(tick);
|
|
1035
|
-
},
|
|
1036
|
-
get running() {
|
|
1037
|
-
return raf !== 0;
|
|
1038
|
-
},
|
|
1039
|
-
stop() {
|
|
1040
|
-
if (raf)
|
|
1041
|
-
win.cancelAnimationFrame(raf);
|
|
1042
|
-
raf = 0;
|
|
1043
|
-
last = 0;
|
|
1044
|
-
}
|
|
1045
|
-
};
|
|
3099
|
+
function motionBlur(speed, max = 3, per = 1600) {
|
|
3100
|
+
const b = Math.min(max, Math.abs(speed) / per);
|
|
3101
|
+
return b < 0.25 ? 0 : Math.round(b * 4) / 4;
|
|
1046
3102
|
}
|
|
1047
|
-
var svg = (body, box = 16) => `<svg viewBox="0 0 ${box} ${box}" aria-hidden="true" fill="none" stroke="currentColor" stroke-width="1.5" stroke-linecap="round" stroke-linejoin="round">${body}</svg>`;
|
|
1048
|
-
var ICONS = {
|
|
1049
|
-
caret: svg('<path d="M6 3.5 10.5 8 6 12.5"/>'),
|
|
1050
|
-
chevronDown: svg('<path d="M3.5 6 8 10.5 12.5 6"/>'),
|
|
1051
|
-
eye: svg('<path d="M1.5 8s2.4-4.5 6.5-4.5S14.5 8 14.5 8 12.1 12.5 8 12.5 1.5 8 1.5 8Z"/><circle cx="8" cy="8" r="2"/>'),
|
|
1052
|
-
eyeOff: svg('<path d="M2.5 2.5l11 11M6.6 4.2A6.6 6.6 0 0 1 8 3.5c4.1 0 6.5 4.5 6.5 4.5a11 11 0 0 1-1.7 2.2M10.2 11.9A6 6 0 0 1 8 12.5C3.9 12.5 1.5 8 1.5 8a11 11 0 0 1 2.4-2.9"/>'),
|
|
1053
|
-
lock: svg('<rect x="3.5" y="7" width="9" height="6.5" rx="1.2"/><path d="M5.5 7V5a2.5 2.5 0 0 1 5 0v2"/>'),
|
|
1054
|
-
unlock: svg('<rect x="3.5" y="7" width="9" height="6.5" rx="1.2"/><path d="M5.5 7V5a2.5 2.5 0 0 1 4.8-1"/>')
|
|
1055
|
-
};
|
|
1056
3103
|
|
|
1057
3104
|
// src/ui/bento.ts
|
|
1058
3105
|
var LAYOUT = springMotion({ duration: 0.4, bounce: 0 });
|
|
1059
3106
|
var ZOOM_IN = springMotion({ duration: 0.45, bounce: 0 });
|
|
1060
3107
|
var ZOOM_OUT = springMotion({ duration: 0.38, bounce: 0 });
|
|
1061
|
-
var
|
|
3108
|
+
var SLIDE2 = springMotion({ duration: 0.3, bounce: 0 });
|
|
1062
3109
|
var SLIDE_OUT = springMotion({ duration: 0.25, bounce: 0 });
|
|
1063
3110
|
var ENTER = springMotion({ duration: 0.3, bounce: 0 });
|
|
1064
|
-
var
|
|
3111
|
+
var EXIT2 = springMotion({ duration: 0.25, bounce: 0 });
|
|
1065
3112
|
var WASH = springMotion({ duration: 0.25, bounce: 0 });
|
|
1066
|
-
var
|
|
3113
|
+
var FADE2 = springMotion({ duration: 0.18, bounce: 0 });
|
|
1067
3114
|
var INSTANT = springMotion({ duration: 0.1, bounce: 0 });
|
|
1068
3115
|
var SLIDE_BY = 0.12;
|
|
1069
3116
|
var EXIT_BLUR = 3;
|
|
1070
3117
|
var px = (x) => new Spring(x, LAYOUT.omega, LAYOUT.zeta, 0.05);
|
|
1071
3118
|
var unit = (x) => new Spring(x, ENTER.omega, ENTER.zeta, 0.0005);
|
|
1072
|
-
var drive = (s, m, target) =>
|
|
1073
|
-
s.omega = m.omega;
|
|
1074
|
-
s.zeta = m.zeta;
|
|
1075
|
-
return s.set(target);
|
|
1076
|
-
};
|
|
3119
|
+
var drive = (s, m, target) => tune(s, m).set(target);
|
|
1077
3120
|
var probed = new Map;
|
|
1078
3121
|
var smallest = (srcset) => {
|
|
1079
3122
|
let best;
|
|
@@ -1233,7 +3276,7 @@ function bento(container, options) {
|
|
|
1233
3276
|
if (enter && !reduced())
|
|
1234
3277
|
drive(t.life.snap(0), ENTER, 1);
|
|
1235
3278
|
else if (enter)
|
|
1236
|
-
drive(t.life.snap(0),
|
|
3279
|
+
drive(t.life.snap(0), FADE2, 1);
|
|
1237
3280
|
if (reduced() || !from)
|
|
1238
3281
|
for (const s of [t.x, t.y, t.w, t.h])
|
|
1239
3282
|
s.snap(s.target);
|
|
@@ -1563,7 +3606,7 @@ function bento(container, options) {
|
|
|
1563
3606
|
if (r)
|
|
1564
3607
|
setBox(t, r.w, r.h);
|
|
1565
3608
|
if (f.out || f.veil)
|
|
1566
|
-
drive(t.life.snap(0), reduced() ?
|
|
3609
|
+
drive(t.life.snap(0), reduced() ? FADE2 : ENTER, 1);
|
|
1567
3610
|
t.drawn = "";
|
|
1568
3611
|
draw(t);
|
|
1569
3612
|
syncZoomed();
|
|
@@ -1596,11 +3639,11 @@ function bento(container, options) {
|
|
|
1596
3639
|
f.z.snap(1);
|
|
1597
3640
|
f.dx.snap(0);
|
|
1598
3641
|
f.veil = true;
|
|
1599
|
-
drive(f.alpha.snap(f.alpha.target === 0 ? f.alpha.x : 0),
|
|
3642
|
+
drive(f.alpha.snap(f.alpha.target === 0 ? f.alpha.x : 0), FADE2, 1);
|
|
1600
3643
|
} else {
|
|
1601
3644
|
f.veil = false;
|
|
1602
3645
|
drive(f.z, ZOOM_IN, 1);
|
|
1603
|
-
drive(f.dx,
|
|
3646
|
+
drive(f.dx, SLIDE2, 0);
|
|
1604
3647
|
drive(f.alpha, ENTER, 1);
|
|
1605
3648
|
}
|
|
1606
3649
|
syncZoomed();
|
|
@@ -1631,7 +3674,7 @@ function bento(container, options) {
|
|
|
1631
3674
|
revealUnder(f.tile.id);
|
|
1632
3675
|
if (reduced()) {
|
|
1633
3676
|
f.veil = true;
|
|
1634
|
-
drive(f.alpha,
|
|
3677
|
+
drive(f.alpha, FADE2, 0);
|
|
1635
3678
|
} else
|
|
1636
3679
|
drive(f.z, ZOOM_OUT, 0);
|
|
1637
3680
|
syncZoomed();
|
|
@@ -1652,7 +3695,7 @@ function bento(container, options) {
|
|
|
1652
3695
|
cur.out = true;
|
|
1653
3696
|
cur.veil = false;
|
|
1654
3697
|
drive(cur.dx, SLIDE_OUT, -dir * by);
|
|
1655
|
-
drive(cur.alpha, reduced() ?
|
|
3698
|
+
drive(cur.alpha, reduced() ? FADE2 : SLIDE_OUT, 0);
|
|
1656
3699
|
let f = focusOf(t);
|
|
1657
3700
|
if (!f) {
|
|
1658
3701
|
f = beginFocus(t);
|
|
@@ -1663,8 +3706,8 @@ function bento(container, options) {
|
|
|
1663
3706
|
f.out = false;
|
|
1664
3707
|
f.veil = false;
|
|
1665
3708
|
drive(f.z, ZOOM_IN, 1);
|
|
1666
|
-
drive(f.dx,
|
|
1667
|
-
drive(f.alpha, reduced() ?
|
|
3709
|
+
drive(f.dx, SLIDE2, 0);
|
|
3710
|
+
drive(f.alpha, reduced() ? FADE2 : SLIDE2, 1);
|
|
1668
3711
|
setActive(id);
|
|
1669
3712
|
syncZoomed();
|
|
1670
3713
|
loop.wake();
|
|
@@ -1834,7 +3877,7 @@ function bento(container, options) {
|
|
|
1834
3877
|
}
|
|
1835
3878
|
zoomIn(t.id);
|
|
1836
3879
|
};
|
|
1837
|
-
const typing = (target) => target
|
|
3880
|
+
const typing = (target) => target !== root && isTypingTarget(target);
|
|
1838
3881
|
const reveal = (id) => {
|
|
1839
3882
|
const r = rectOf(id);
|
|
1840
3883
|
if (!r)
|
|
@@ -1988,7 +4031,7 @@ function bento(container, options) {
|
|
|
1988
4031
|
if (f)
|
|
1989
4032
|
endFocus(f);
|
|
1990
4033
|
t.leaving = true;
|
|
1991
|
-
drive(t.life, reduced() ?
|
|
4034
|
+
drive(t.life, reduced() ? FADE2 : EXIT2, 0);
|
|
1992
4035
|
}
|
|
1993
4036
|
items = next;
|
|
1994
4037
|
reindex();
|
|
@@ -2020,36 +4063,590 @@ function bento(container, options) {
|
|
|
2020
4063
|
if (id && index.has(id))
|
|
2021
4064
|
move(id);
|
|
2022
4065
|
},
|
|
2023
|
-
get picks() {
|
|
2024
|
-
return orderedPicks();
|
|
4066
|
+
get picks() {
|
|
4067
|
+
return orderedPicks();
|
|
4068
|
+
},
|
|
4069
|
+
pick(ids) {
|
|
4070
|
+
picks = new Set(ids);
|
|
4071
|
+
for (const t of tiles.values())
|
|
4072
|
+
t.el.setAttribute("aria-selected", String(picks.has(t.id)));
|
|
4073
|
+
},
|
|
4074
|
+
get plan() {
|
|
4075
|
+
return plan;
|
|
4076
|
+
},
|
|
4077
|
+
get moving() {
|
|
4078
|
+
return loop.running;
|
|
4079
|
+
},
|
|
4080
|
+
stats() {
|
|
4081
|
+
const s = [...times].sort((a, b) => a - b);
|
|
4082
|
+
const q = (p) => Math.round((s[Math.floor(p * (s.length - 1))] ?? 0) * 1000) / 1000;
|
|
4083
|
+
return { frames, p50: q(0.5), p95: q(0.95), max: q(1) };
|
|
4084
|
+
},
|
|
4085
|
+
dispose() {
|
|
4086
|
+
loop.stop();
|
|
4087
|
+
ro.disconnect();
|
|
4088
|
+
win.clearTimeout(swipe.timer);
|
|
4089
|
+
if (hand)
|
|
4090
|
+
win.clearTimeout(hand.timer);
|
|
4091
|
+
for (const t of [...tiles.values()])
|
|
4092
|
+
unmount(t);
|
|
4093
|
+
root.remove();
|
|
4094
|
+
}
|
|
4095
|
+
};
|
|
4096
|
+
}
|
|
4097
|
+
|
|
4098
|
+
// src/ui/picker-model.ts
|
|
4099
|
+
function digitPick(ids, key) {
|
|
4100
|
+
if (!/^[1-9]$/.test(key))
|
|
4101
|
+
return;
|
|
4102
|
+
return ids[Number(key) - 1];
|
|
4103
|
+
}
|
|
4104
|
+
function stepPick(ids, current, key) {
|
|
4105
|
+
if (!ids.length)
|
|
4106
|
+
return;
|
|
4107
|
+
if (key === "Home")
|
|
4108
|
+
return ids[0];
|
|
4109
|
+
if (key === "End")
|
|
4110
|
+
return ids[ids.length - 1];
|
|
4111
|
+
const i = current === null ? -1 : ids.indexOf(current);
|
|
4112
|
+
if (i < 0)
|
|
4113
|
+
return key === "ArrowDown" ? ids[0] : ids[ids.length - 1];
|
|
4114
|
+
return ids[Math.max(0, Math.min(ids.length - 1, i + (key === "ArrowDown" ? 1 : -1)))];
|
|
4115
|
+
}
|
|
4116
|
+
function keepPick(ids, selected, wanted = null) {
|
|
4117
|
+
if (wanted !== null && ids.includes(wanted))
|
|
4118
|
+
return wanted;
|
|
4119
|
+
if (selected !== null && ids.includes(selected))
|
|
4120
|
+
return selected;
|
|
4121
|
+
return ids[0] ?? null;
|
|
4122
|
+
}
|
|
4123
|
+
function askPick(ids, id) {
|
|
4124
|
+
return ids.includes(id) ? { pick: id, wanted: null } : { pick: undefined, wanted: id };
|
|
4125
|
+
}
|
|
4126
|
+
var stillWanted = (ids, wanted) => wanted !== null && ids.includes(wanted) ? null : wanted;
|
|
4127
|
+
var pickDigit = (index) => String(index + 1);
|
|
4128
|
+
function claimsDigit(event, typing = false) {
|
|
4129
|
+
return /^[1-9]$/.test(event.key) && !event.defaultPrevented && !event.metaKey && !event.ctrlKey && !event.altKey && !event.shiftKey && !event.repeat && !event.typing && !typing;
|
|
4130
|
+
}
|
|
4131
|
+
function cardTarget(heads, index, body, gap = 0, top = 0) {
|
|
4132
|
+
if (body === undefined)
|
|
4133
|
+
return null;
|
|
4134
|
+
let y = top;
|
|
4135
|
+
for (let i = 0;i < index; i++)
|
|
4136
|
+
y += heads[i] + gap;
|
|
4137
|
+
return { y, h: (heads[index] ?? 0) + body };
|
|
4138
|
+
}
|
|
4139
|
+
function settleHeight(previous, now, frames, limit = 6) {
|
|
4140
|
+
if (previous !== undefined && Math.abs(now - previous) < 0.5)
|
|
4141
|
+
return now;
|
|
4142
|
+
return frames >= limit ? now : undefined;
|
|
4143
|
+
}
|
|
4144
|
+
|
|
4145
|
+
// src/ui/picker.ts
|
|
4146
|
+
var OPEN2 = springMotion({ duration: 0.22, bounce: 0 });
|
|
4147
|
+
var CLOSE2 = springMotion({ duration: 0.18, bounce: 0 });
|
|
4148
|
+
var CARD = springMotion({ duration: 0.3, bounce: 0 });
|
|
4149
|
+
var FADE3 = springMotion({ duration: 0.16, bounce: 0 });
|
|
4150
|
+
var DRIFT = 6;
|
|
4151
|
+
var CHECK = svg('<path d="m3.5 8.4 3 3 6-6.6"/>');
|
|
4152
|
+
function optionPicker(container, options) {
|
|
4153
|
+
const doc = container.ownerDocument;
|
|
4154
|
+
const win = doc.defaultView;
|
|
4155
|
+
let list = options.options;
|
|
4156
|
+
let wanted = null;
|
|
4157
|
+
let selected = null;
|
|
4158
|
+
{
|
|
4159
|
+
const ids = list.map((o) => o.id);
|
|
4160
|
+
if (options.selected != null)
|
|
4161
|
+
wanted = askPick(ids, options.selected).wanted;
|
|
4162
|
+
selected = keepPick(ids, options.selected ?? null);
|
|
4163
|
+
}
|
|
4164
|
+
let chosen = options.chosen ?? null;
|
|
4165
|
+
const digits = options.digits ?? "focus";
|
|
4166
|
+
const root = doc.createElement("div");
|
|
4167
|
+
root.className = "knobs-picker";
|
|
4168
|
+
root.setAttribute("role", "radiogroup");
|
|
4169
|
+
if (options.label)
|
|
4170
|
+
root.setAttribute("aria-label", options.label);
|
|
4171
|
+
applyLook(root, options);
|
|
4172
|
+
const card = doc.createElement("div");
|
|
4173
|
+
card.className = "knobs-picker-card";
|
|
4174
|
+
card.setAttribute("aria-hidden", "true");
|
|
4175
|
+
root.append(card);
|
|
4176
|
+
container.append(root);
|
|
4177
|
+
const entries = new Map;
|
|
4178
|
+
const uid = `kp${Math.random().toString(36).slice(2, 7)}`;
|
|
4179
|
+
let serial = 0;
|
|
4180
|
+
const make = (option) => {
|
|
4181
|
+
const el = doc.createElement("div");
|
|
4182
|
+
el.className = "knobs-picker-option";
|
|
4183
|
+
el.dataset.id = option.id;
|
|
4184
|
+
const head = doc.createElement("button");
|
|
4185
|
+
head.type = "button";
|
|
4186
|
+
head.className = "knobs-picker-head";
|
|
4187
|
+
head.setAttribute("role", "radio");
|
|
4188
|
+
head.id = `${uid}-${serial++}`;
|
|
4189
|
+
const key = doc.createElement("span");
|
|
4190
|
+
key.className = "knobs-picker-key";
|
|
4191
|
+
key.setAttribute("aria-hidden", "true");
|
|
4192
|
+
const name = doc.createElement("span");
|
|
4193
|
+
name.className = "knobs-picker-name knobs-scroll-fade-x";
|
|
4194
|
+
const suggested = doc.createElement("span");
|
|
4195
|
+
suggested.className = "knobs-picker-suggested";
|
|
4196
|
+
const check = doc.createElement("span");
|
|
4197
|
+
check.className = "knobs-picker-check";
|
|
4198
|
+
check.innerHTML = CHECK;
|
|
4199
|
+
check.setAttribute("aria-hidden", "true");
|
|
4200
|
+
head.append(key, name, suggested, check);
|
|
4201
|
+
const body = doc.createElement("div");
|
|
4202
|
+
body.className = "knobs-picker-body";
|
|
4203
|
+
body.hidden = true;
|
|
4204
|
+
body.setAttribute("role", "group");
|
|
4205
|
+
body.setAttribute("aria-labelledby", head.id);
|
|
4206
|
+
const inner = doc.createElement("div");
|
|
4207
|
+
inner.className = "knobs-picker-inner";
|
|
4208
|
+
body.append(inner);
|
|
4209
|
+
el.append(head, body);
|
|
4210
|
+
const entry = {
|
|
4211
|
+
option,
|
|
4212
|
+
el,
|
|
4213
|
+
head,
|
|
4214
|
+
key,
|
|
4215
|
+
thumb: undefined,
|
|
4216
|
+
name,
|
|
4217
|
+
suggested,
|
|
4218
|
+
body,
|
|
4219
|
+
inner,
|
|
4220
|
+
mounted: false,
|
|
4221
|
+
measured: true,
|
|
4222
|
+
cleanup: undefined,
|
|
4223
|
+
h: new Spring(0, OPEN2.omega, OPEN2.zeta, 0.05),
|
|
4224
|
+
o: new Spring(0, OPEN2.omega, OPEN2.zeta, 0.002),
|
|
4225
|
+
moving: false,
|
|
4226
|
+
open: false,
|
|
4227
|
+
presence: new Spring(1, OPEN2.omega, OPEN2.zeta, 0.002),
|
|
4228
|
+
leaving: false
|
|
4229
|
+
};
|
|
4230
|
+
entries.set(option.id, entry);
|
|
4231
|
+
return entry;
|
|
4232
|
+
};
|
|
4233
|
+
const setThumb = (e, url) => {
|
|
4234
|
+
if ((e.thumb?.getAttribute("src") ?? undefined) === (url || undefined))
|
|
4235
|
+
return;
|
|
4236
|
+
if (!url) {
|
|
4237
|
+
e.thumb?.remove();
|
|
4238
|
+
e.thumb = undefined;
|
|
4239
|
+
return;
|
|
4240
|
+
}
|
|
4241
|
+
let img = e.thumb;
|
|
4242
|
+
if (!img) {
|
|
4243
|
+
const el = doc.createElement("img");
|
|
4244
|
+
el.className = "knobs-picker-thumb";
|
|
4245
|
+
el.alt = "";
|
|
4246
|
+
el.loading = "lazy";
|
|
4247
|
+
el.decoding = "async";
|
|
4248
|
+
el.draggable = false;
|
|
4249
|
+
el.addEventListener("load", () => el.dataset.state = "loaded");
|
|
4250
|
+
el.addEventListener("error", () => el.dataset.state = "failed");
|
|
4251
|
+
e.key.after(el);
|
|
4252
|
+
e.thumb = img = el;
|
|
4253
|
+
}
|
|
4254
|
+
const shown = img;
|
|
4255
|
+
shown.dataset.state = "loading";
|
|
4256
|
+
shown.src = url;
|
|
4257
|
+
if (shown.complete && shown.naturalWidth > 0) {
|
|
4258
|
+
shown.dataset.state = "loaded";
|
|
4259
|
+
shown.dataset.instant = "";
|
|
4260
|
+
win.requestAnimationFrame(() => delete shown.dataset.instant);
|
|
4261
|
+
}
|
|
4262
|
+
};
|
|
4263
|
+
const bind = (e, i) => {
|
|
4264
|
+
e.option = list[i];
|
|
4265
|
+
e.key.textContent = pickDigit(i);
|
|
4266
|
+
setThumb(e, e.option.thumb);
|
|
4267
|
+
if (e.name.textContent !== e.option.label)
|
|
4268
|
+
e.name.textContent = e.option.label;
|
|
4269
|
+
e.suggested.textContent = e.option.suggested ? options.suggestedLabel ?? "Suggested" : "";
|
|
4270
|
+
e.suggested.hidden = !e.option.suggested;
|
|
4271
|
+
const on = e.option.id === selected;
|
|
4272
|
+
e.el.toggleAttribute("data-selected", on);
|
|
4273
|
+
e.head.setAttribute("aria-checked", String(on));
|
|
4274
|
+
e.head.tabIndex = on ? 0 : -1;
|
|
4275
|
+
e.el.toggleAttribute("data-chosen", e.option.id === chosen);
|
|
4276
|
+
e.head.setAttribute("aria-label", [
|
|
4277
|
+
e.option.label,
|
|
4278
|
+
e.option.suggested ? options.suggestedLabel ?? "Suggested" : "",
|
|
4279
|
+
e.option.id === chosen ? "chosen" : ""
|
|
4280
|
+
].filter(Boolean).join(", "));
|
|
4281
|
+
};
|
|
4282
|
+
const mount = (e) => {
|
|
4283
|
+
if (e.mounted || !options.body)
|
|
4284
|
+
return;
|
|
4285
|
+
e.mounted = true;
|
|
4286
|
+
const out = options.body(e.option, e.inner);
|
|
4287
|
+
if (typeof out === "function")
|
|
4288
|
+
e.cleanup = out;
|
|
4289
|
+
};
|
|
4290
|
+
const cardY = new Spring(0, CARD.omega, CARD.zeta, 0.05);
|
|
4291
|
+
const cardH = new Spring(0, CARD.omega, CARD.zeta, 0.05);
|
|
4292
|
+
let gliding = false;
|
|
4293
|
+
const liveCard = () => {
|
|
4294
|
+
const e = selected ? entries.get(selected) : undefined;
|
|
4295
|
+
return e && !e.leaving ? { y: e.el.offsetTop, h: e.el.offsetHeight } : null;
|
|
4296
|
+
};
|
|
4297
|
+
const finalCard = () => {
|
|
4298
|
+
const live = list.map((o) => entries.get(o.id)).filter((e) => !!e && !e.leaving);
|
|
4299
|
+
const index = live.findIndex((e) => e.option.id === selected);
|
|
4300
|
+
if (index < 0)
|
|
4301
|
+
return null;
|
|
4302
|
+
const gap = parseFloat(win.getComputedStyle(root).rowGap) || 0;
|
|
4303
|
+
const e = live[index];
|
|
4304
|
+
return cardTarget(live.map((x) => x.head.offsetHeight), index, e.measured ? natural(e) : undefined, gap);
|
|
4305
|
+
};
|
|
4306
|
+
const placeCard = () => {
|
|
4307
|
+
card.style.transform = `translate3d(0,${Math.round(cardY.x * 2) / 2}px,0)`;
|
|
4308
|
+
card.style.height = `${Math.max(0, cardH.x).toFixed(2)}px`;
|
|
4309
|
+
card.hidden = cardH.x < 0.5;
|
|
4310
|
+
};
|
|
4311
|
+
const natural = (e) => e.inner.offsetHeight;
|
|
4312
|
+
const setOpen = (e, open, animate) => {
|
|
4313
|
+
if (e.open === open && (e.moving || e.body.hidden === !open))
|
|
4314
|
+
return;
|
|
4315
|
+
e.open = open;
|
|
4316
|
+
const reduced = reducedMotion(win);
|
|
4317
|
+
if (open && !e.mounted) {
|
|
4318
|
+
if (animate && e.body.hidden) {
|
|
4319
|
+
e.body.hidden = false;
|
|
4320
|
+
e.body.style.height = "0px";
|
|
4321
|
+
e.body.dataset.moving = "";
|
|
4322
|
+
e.h.snap(0);
|
|
4323
|
+
e.o.snap(0);
|
|
4324
|
+
e.moving = true;
|
|
4325
|
+
}
|
|
4326
|
+
mount(e);
|
|
4327
|
+
}
|
|
4328
|
+
if (!animate) {
|
|
4329
|
+
e.body.hidden = !open;
|
|
4330
|
+
e.moving = false;
|
|
4331
|
+
e.body.style.height = "";
|
|
4332
|
+
delete e.body.dataset.moving;
|
|
4333
|
+
e.inner.style.opacity = "";
|
|
4334
|
+
e.inner.style.transform = "";
|
|
4335
|
+
return;
|
|
4336
|
+
}
|
|
4337
|
+
if (!e.moving) {
|
|
4338
|
+
const from = e.body.hidden ? 0 : e.body.offsetHeight;
|
|
4339
|
+
e.h.snap(from);
|
|
4340
|
+
e.o.snap(from > 0.5 ? 1 : 0);
|
|
4341
|
+
}
|
|
4342
|
+
e.body.hidden = false;
|
|
4343
|
+
e.moving = true;
|
|
4344
|
+
e.body.dataset.moving = "";
|
|
4345
|
+
if (reduced) {
|
|
4346
|
+
e.h.snap(open ? natural(e) : 0);
|
|
4347
|
+
tune(e.o, FADE3).set(open ? 1 : 0);
|
|
4348
|
+
} else {
|
|
4349
|
+
tune(e.h, open ? OPEN2 : CLOSE2).set(open ? natural(e) : 0);
|
|
4350
|
+
tune(e.o, open ? OPEN2 : CLOSE2).set(open ? 1 : 0);
|
|
4351
|
+
}
|
|
4352
|
+
loop.wake();
|
|
4353
|
+
};
|
|
4354
|
+
let pending = null;
|
|
4355
|
+
const premount = (e) => {
|
|
4356
|
+
e.body.hidden = false;
|
|
4357
|
+
e.body.style.height = "0px";
|
|
4358
|
+
e.body.dataset.moving = "";
|
|
4359
|
+
e.inner.style.opacity = "0";
|
|
4360
|
+
e.measured = false;
|
|
4361
|
+
mount(e);
|
|
4362
|
+
};
|
|
4363
|
+
const shelve = (e) => {
|
|
4364
|
+
if (e.open || e.moving)
|
|
4365
|
+
return;
|
|
4366
|
+
e.body.hidden = true;
|
|
4367
|
+
e.body.style.height = "";
|
|
4368
|
+
delete e.body.dataset.moving;
|
|
4369
|
+
e.inner.style.opacity = "";
|
|
4370
|
+
e.measured = true;
|
|
4371
|
+
};
|
|
4372
|
+
const start = (was, now, animate) => {
|
|
4373
|
+
now.measured = true;
|
|
4374
|
+
if (was && was !== now)
|
|
4375
|
+
setOpen(was, false, animate);
|
|
4376
|
+
setOpen(now, true, animate);
|
|
4377
|
+
if (animate && !reducedMotion(win)) {
|
|
4378
|
+
tune(cardY, CARD);
|
|
4379
|
+
tune(cardH, CARD);
|
|
4380
|
+
gliding = true;
|
|
4381
|
+
}
|
|
4382
|
+
loop.wake();
|
|
4383
|
+
};
|
|
4384
|
+
const flush = () => {
|
|
4385
|
+
const p = pending;
|
|
4386
|
+
if (!p)
|
|
4387
|
+
return;
|
|
4388
|
+
pending = null;
|
|
4389
|
+
start(p.was, p.now, true);
|
|
4390
|
+
};
|
|
4391
|
+
const loop = frameLoop(win, (dt) => {
|
|
4392
|
+
let moving = false;
|
|
4393
|
+
if (pending) {
|
|
4394
|
+
const p = pending;
|
|
4395
|
+
const h = natural(p.now);
|
|
4396
|
+
p.frames++;
|
|
4397
|
+
if (settleHeight(p.last, h, p.frames) !== undefined)
|
|
4398
|
+
flush();
|
|
4399
|
+
else {
|
|
4400
|
+
p.last = h;
|
|
4401
|
+
moving = true;
|
|
4402
|
+
}
|
|
4403
|
+
}
|
|
4404
|
+
for (const e of [...entries.values()]) {
|
|
4405
|
+
if (e.moving) {
|
|
4406
|
+
if (e.open && !reducedMotion(win))
|
|
4407
|
+
e.h.set(natural(e));
|
|
4408
|
+
e.h.step(dt);
|
|
4409
|
+
e.o.step(dt);
|
|
4410
|
+
const fade = Math.max(0, Math.min(1, e.o.x));
|
|
4411
|
+
e.body.style.height = `${Math.max(0, e.h.x).toFixed(2)}px`;
|
|
4412
|
+
const shown = e.open ? fade : Math.max(0, (fade - 0.4) / 0.6);
|
|
4413
|
+
e.inner.style.opacity = shown < 0.999 ? shown.toFixed(3) : "";
|
|
4414
|
+
const drift = reducedMotion(win) ? 0 : (1 - fade) * -DRIFT;
|
|
4415
|
+
e.inner.style.transform = Math.abs(drift) > 0.05 ? `translateY(${drift.toFixed(2)}px)` : "";
|
|
4416
|
+
if (e.h.resting && e.o.resting) {
|
|
4417
|
+
e.moving = false;
|
|
4418
|
+
delete e.body.dataset.moving;
|
|
4419
|
+
e.body.style.height = "";
|
|
4420
|
+
e.inner.style.opacity = "";
|
|
4421
|
+
e.inner.style.transform = "";
|
|
4422
|
+
e.body.hidden = !e.open;
|
|
4423
|
+
} else
|
|
4424
|
+
moving = true;
|
|
4425
|
+
}
|
|
4426
|
+
if (e.leaving || !e.presence.resting) {
|
|
4427
|
+
e.presence.step(dt);
|
|
4428
|
+
const p = Math.max(0, Math.min(1, e.presence.x));
|
|
4429
|
+
e.el.style.opacity = p < 0.999 ? p.toFixed(3) : "";
|
|
4430
|
+
e.el.style.height = p < 0.999 ? `${(p * e.head.offsetHeight).toFixed(2)}px` : "";
|
|
4431
|
+
e.el.style.overflow = p < 0.999 ? "hidden" : "";
|
|
4432
|
+
if (e.presence.resting) {
|
|
4433
|
+
if (e.leaving) {
|
|
4434
|
+
e.cleanup?.();
|
|
4435
|
+
e.el.remove();
|
|
4436
|
+
entries.delete(e.option.id);
|
|
4437
|
+
}
|
|
4438
|
+
} else
|
|
4439
|
+
moving = true;
|
|
4440
|
+
}
|
|
4441
|
+
}
|
|
4442
|
+
const t = pending ? null : gliding ? finalCard() : liveCard();
|
|
4443
|
+
if (t) {
|
|
4444
|
+
if (gliding) {
|
|
4445
|
+
cardY.set(t.y);
|
|
4446
|
+
cardH.set(t.h);
|
|
4447
|
+
cardY.step(dt);
|
|
4448
|
+
cardH.step(dt);
|
|
4449
|
+
if (cardY.resting && cardH.resting && !moving)
|
|
4450
|
+
gliding = false;
|
|
4451
|
+
else
|
|
4452
|
+
moving = true;
|
|
4453
|
+
} else {
|
|
4454
|
+
cardY.snap(t.y);
|
|
4455
|
+
cardH.snap(t.h);
|
|
4456
|
+
}
|
|
4457
|
+
}
|
|
4458
|
+
placeCard();
|
|
4459
|
+
notifyLayout(doc);
|
|
4460
|
+
return moving;
|
|
4461
|
+
});
|
|
4462
|
+
const rebind = () => {
|
|
4463
|
+
list.forEach((o, i) => {
|
|
4464
|
+
const e = entries.get(o.id);
|
|
4465
|
+
if (e)
|
|
4466
|
+
bind(e, i);
|
|
4467
|
+
});
|
|
4468
|
+
};
|
|
4469
|
+
const pick = (id, animate = true) => {
|
|
4470
|
+
if (id === selected || !entries.has(id))
|
|
4471
|
+
return;
|
|
4472
|
+
let was = selected ? entries.get(selected) : undefined;
|
|
4473
|
+
if (pending) {
|
|
4474
|
+
was = pending.was;
|
|
4475
|
+
const waiting = pending.now;
|
|
4476
|
+
pending = null;
|
|
4477
|
+
if (waiting.option.id !== id)
|
|
4478
|
+
shelve(waiting);
|
|
4479
|
+
}
|
|
4480
|
+
selected = id;
|
|
4481
|
+
rebind();
|
|
4482
|
+
const now = entries.get(id);
|
|
4483
|
+
if (animate && !reducedMotion(win) && !now.mounted && options.body) {
|
|
4484
|
+
premount(now);
|
|
4485
|
+
pending = { was, now, last: natural(now), frames: 0 };
|
|
4486
|
+
loop.wake();
|
|
4487
|
+
return;
|
|
4488
|
+
}
|
|
4489
|
+
start(was, now, animate);
|
|
4490
|
+
};
|
|
4491
|
+
const select = (id, source) => {
|
|
4492
|
+
if (!id)
|
|
4493
|
+
return;
|
|
4494
|
+
wanted = null;
|
|
4495
|
+
if (id === selected)
|
|
4496
|
+
return;
|
|
4497
|
+
const inside = root.contains(doc.activeElement);
|
|
4498
|
+
pick(id);
|
|
4499
|
+
if (source !== "pointer" && inside)
|
|
4500
|
+
entries.get(id)?.head.focus({ preventScroll: true });
|
|
4501
|
+
options.onSelect?.(id, source);
|
|
4502
|
+
};
|
|
4503
|
+
const sync = (animate) => {
|
|
4504
|
+
flush();
|
|
4505
|
+
const ids = list.map((o) => o.id);
|
|
4506
|
+
const was = selected;
|
|
4507
|
+
selected = keepPick(ids, selected, wanted);
|
|
4508
|
+
wanted = stillWanted(ids, wanted);
|
|
4509
|
+
if (animate && was !== selected && !reducedMotion(win))
|
|
4510
|
+
gliding = true;
|
|
4511
|
+
const reduced = reducedMotion(win);
|
|
4512
|
+
const live = new Set(ids);
|
|
4513
|
+
for (const e of entries.values()) {
|
|
4514
|
+
if (live.has(e.option.id) || e.leaving)
|
|
4515
|
+
continue;
|
|
4516
|
+
if (!animate) {
|
|
4517
|
+
e.cleanup?.();
|
|
4518
|
+
e.el.remove();
|
|
4519
|
+
entries.delete(e.option.id);
|
|
4520
|
+
continue;
|
|
4521
|
+
}
|
|
4522
|
+
e.leaving = true;
|
|
4523
|
+
e.el.setAttribute("aria-hidden", "true");
|
|
4524
|
+
e.head.tabIndex = -1;
|
|
4525
|
+
if (e.open)
|
|
4526
|
+
setOpen(e, false, true);
|
|
4527
|
+
tune(e.presence, reduced ? FADE3 : CLOSE2).set(0);
|
|
4528
|
+
}
|
|
4529
|
+
let before = card;
|
|
4530
|
+
list.forEach((o, i) => {
|
|
4531
|
+
let e = entries.get(o.id);
|
|
4532
|
+
if (e?.leaving) {
|
|
4533
|
+
e.leaving = false;
|
|
4534
|
+
e.el.removeAttribute("aria-hidden");
|
|
4535
|
+
tune(e.presence, OPEN2).set(1);
|
|
4536
|
+
}
|
|
4537
|
+
if (!e) {
|
|
4538
|
+
e = make(o);
|
|
4539
|
+
ro.observe(e.el);
|
|
4540
|
+
if (animate)
|
|
4541
|
+
tune(e.presence, reduced ? FADE3 : OPEN2).snap(0).set(1);
|
|
4542
|
+
}
|
|
4543
|
+
if (before.nextElementSibling !== e.el)
|
|
4544
|
+
before.after(e.el);
|
|
4545
|
+
before = e.el;
|
|
4546
|
+
bind(e, i);
|
|
4547
|
+
});
|
|
4548
|
+
const open = selected ? entries.get(selected) : undefined;
|
|
4549
|
+
const ahead = animate && !reduced && open && !open.open && !open.mounted && !!options.body ? { was: was ? entries.get(was) : undefined, now: open } : null;
|
|
4550
|
+
for (const e of entries.values())
|
|
4551
|
+
if (e !== open && e !== ahead?.was && e.open && !e.leaving)
|
|
4552
|
+
setOpen(e, false, animate);
|
|
4553
|
+
if (ahead) {
|
|
4554
|
+
premount(ahead.now);
|
|
4555
|
+
pending = { ...ahead, last: natural(ahead.now), frames: 0 };
|
|
4556
|
+
} else if (open && !open.open)
|
|
4557
|
+
setOpen(open, true, animate);
|
|
4558
|
+
loop.wake();
|
|
4559
|
+
};
|
|
4560
|
+
const ids = () => list.map((o) => o.id).filter((id) => !entries.get(id)?.leaving);
|
|
4561
|
+
const onDigit = (e) => {
|
|
4562
|
+
if (!claimsDigit(e, isTypingTarget(e.target)))
|
|
4563
|
+
return;
|
|
4564
|
+
if (!root.getClientRects().length)
|
|
4565
|
+
return;
|
|
4566
|
+
const id = digitPick(ids(), e.key);
|
|
4567
|
+
if (!id)
|
|
4568
|
+
return;
|
|
4569
|
+
e.preventDefault();
|
|
4570
|
+
select(id, "digit");
|
|
4571
|
+
};
|
|
4572
|
+
root.addEventListener("keydown", (e) => {
|
|
4573
|
+
const head = e.target.closest?.(".knobs-picker-head");
|
|
4574
|
+
if (head && !e.metaKey && !e.ctrlKey && !e.altKey) {
|
|
4575
|
+
if (e.key === "ArrowUp" || e.key === "ArrowDown" || e.key === "Home" || e.key === "End") {
|
|
4576
|
+
e.preventDefault();
|
|
4577
|
+
select(stepPick(ids(), selected, e.key), "key");
|
|
4578
|
+
return;
|
|
4579
|
+
}
|
|
4580
|
+
if (e.key === "Enter" && selected) {
|
|
4581
|
+
e.preventDefault();
|
|
4582
|
+
options.onChoose?.(selected);
|
|
4583
|
+
return;
|
|
4584
|
+
}
|
|
4585
|
+
}
|
|
4586
|
+
if (digits === "focus")
|
|
4587
|
+
onDigit(e);
|
|
4588
|
+
});
|
|
4589
|
+
if (digits === "page")
|
|
4590
|
+
win.addEventListener("keydown", onDigit);
|
|
4591
|
+
root.addEventListener("click", (e) => {
|
|
4592
|
+
const el = e.target.closest(".knobs-picker-head");
|
|
4593
|
+
const id = el?.closest(".knobs-picker-option")?.dataset.id;
|
|
4594
|
+
if (!id || entries.get(id)?.leaving)
|
|
4595
|
+
return;
|
|
4596
|
+
select(id, e.detail === 0 ? "key" : "pointer");
|
|
4597
|
+
});
|
|
4598
|
+
const ro = new ResizeObserver(() => loop.wake());
|
|
4599
|
+
ro.observe(root);
|
|
4600
|
+
root.addEventListener("knobs-restyle", () => {
|
|
4601
|
+
syncFrame(root);
|
|
4602
|
+
loop.wake();
|
|
4603
|
+
});
|
|
4604
|
+
sync(false);
|
|
4605
|
+
const first = liveCard();
|
|
4606
|
+
if (first) {
|
|
4607
|
+
cardY.snap(first.y);
|
|
4608
|
+
cardH.snap(first.h);
|
|
4609
|
+
}
|
|
4610
|
+
placeCard();
|
|
4611
|
+
loop.wake();
|
|
4612
|
+
const handle = {
|
|
4613
|
+
element: root,
|
|
4614
|
+
set(next) {
|
|
4615
|
+
list = next;
|
|
4616
|
+
sync(true);
|
|
4617
|
+
},
|
|
4618
|
+
select(id) {
|
|
4619
|
+
const ask = askPick(ids(), id);
|
|
4620
|
+
wanted = ask.wanted;
|
|
4621
|
+
if (ask.pick)
|
|
4622
|
+
pick(ask.pick);
|
|
2025
4623
|
},
|
|
2026
|
-
|
|
2027
|
-
|
|
2028
|
-
for (const t of tiles.values())
|
|
2029
|
-
t.el.setAttribute("aria-selected", String(picks.has(t.id)));
|
|
4624
|
+
get selected() {
|
|
4625
|
+
return selected;
|
|
2030
4626
|
},
|
|
2031
|
-
|
|
2032
|
-
|
|
4627
|
+
choose(id) {
|
|
4628
|
+
chosen = id;
|
|
4629
|
+
rebind();
|
|
2033
4630
|
},
|
|
2034
|
-
get
|
|
2035
|
-
return
|
|
4631
|
+
get chosen() {
|
|
4632
|
+
return chosen;
|
|
2036
4633
|
},
|
|
2037
|
-
|
|
2038
|
-
|
|
2039
|
-
const q = (p) => Math.round((s[Math.floor(p * (s.length - 1))] ?? 0) * 1000) / 1000;
|
|
2040
|
-
return { frames, p50: q(0.5), p95: q(0.95), max: q(1) };
|
|
4634
|
+
focus() {
|
|
4635
|
+
(selected ? entries.get(selected)?.head : undefined)?.focus({ preventScroll: true });
|
|
2041
4636
|
},
|
|
2042
|
-
|
|
4637
|
+
destroy() {
|
|
2043
4638
|
loop.stop();
|
|
2044
4639
|
ro.disconnect();
|
|
2045
|
-
|
|
2046
|
-
|
|
2047
|
-
|
|
2048
|
-
|
|
2049
|
-
|
|
4640
|
+
if (digits === "page")
|
|
4641
|
+
win.removeEventListener("keydown", onDigit);
|
|
4642
|
+
for (const e of entries.values())
|
|
4643
|
+
e.cleanup?.();
|
|
4644
|
+
entries.clear();
|
|
2050
4645
|
root.remove();
|
|
2051
|
-
}
|
|
4646
|
+
},
|
|
4647
|
+
dispose: () => handle.destroy()
|
|
2052
4648
|
};
|
|
4649
|
+
return handle;
|
|
2053
4650
|
}
|
|
2054
4651
|
|
|
2055
4652
|
// src/core/storage.ts
|
|
@@ -2239,15 +4836,7 @@ function claimsHintKey(doc, key) {
|
|
|
2239
4836
|
const holder = doc;
|
|
2240
4837
|
return key === PAGE_HINT_KEY && Boolean(holder[KEY]?.listening);
|
|
2241
4838
|
}
|
|
2242
|
-
var isTyping =
|
|
2243
|
-
const el = target;
|
|
2244
|
-
if (!el || typeof el.tagName !== "string")
|
|
2245
|
-
return false;
|
|
2246
|
-
const tag = el.tagName;
|
|
2247
|
-
if (tag === "TEXTAREA" || tag === "SELECT" || el.isContentEditable)
|
|
2248
|
-
return true;
|
|
2249
|
-
return tag === "INPUT" && !["range", "checkbox", "radio", "button", "submit", "reset", "color"].includes(el.type);
|
|
2250
|
-
};
|
|
4839
|
+
var isTyping = isTypingTarget;
|
|
2251
4840
|
var PRESSABLE = "button,a[href],summary,[role=button],[role=tab],[role=radio],[role=switch],[role=checkbox],[role=menuitem],[role=option]";
|
|
2252
4841
|
function activateElement(el) {
|
|
2253
4842
|
const pressable = el.matches(PRESSABLE) || el.tabIndex < 0 && !el.matches("input,select,textarea");
|
|
@@ -2627,110 +5216,6 @@ function layerPainter(doc, groups) {
|
|
|
2627
5216
|
};
|
|
2628
5217
|
}
|
|
2629
5218
|
|
|
2630
|
-
// src/core/keys.ts
|
|
2631
|
-
var MODS = ["Ctrl", "Alt", "Shift", "Meta"];
|
|
2632
|
-
var ALIASES = {
|
|
2633
|
-
ctrl: "Ctrl",
|
|
2634
|
-
control: "Ctrl",
|
|
2635
|
-
"⌃": "Ctrl",
|
|
2636
|
-
alt: "Alt",
|
|
2637
|
-
option: "Alt",
|
|
2638
|
-
opt: "Alt",
|
|
2639
|
-
"⌥": "Alt",
|
|
2640
|
-
shift: "Shift",
|
|
2641
|
-
"⇧": "Shift",
|
|
2642
|
-
meta: "Meta",
|
|
2643
|
-
cmd: "Meta",
|
|
2644
|
-
command: "Meta",
|
|
2645
|
-
"⌘": "Meta",
|
|
2646
|
-
mod: "Mod"
|
|
2647
|
-
};
|
|
2648
|
-
var NAMED = {
|
|
2649
|
-
" ": "Space",
|
|
2650
|
-
space: "Space",
|
|
2651
|
-
spacebar: "Space",
|
|
2652
|
-
esc: "Escape",
|
|
2653
|
-
escape: "Escape",
|
|
2654
|
-
enter: "Enter",
|
|
2655
|
-
return: "Enter",
|
|
2656
|
-
tab: "Tab",
|
|
2657
|
-
backspace: "Backspace",
|
|
2658
|
-
delete: "Delete",
|
|
2659
|
-
del: "Delete",
|
|
2660
|
-
up: "ArrowUp",
|
|
2661
|
-
down: "ArrowDown",
|
|
2662
|
-
left: "ArrowLeft",
|
|
2663
|
-
right: "ArrowRight",
|
|
2664
|
-
arrowup: "ArrowUp",
|
|
2665
|
-
arrowdown: "ArrowDown",
|
|
2666
|
-
arrowleft: "ArrowLeft",
|
|
2667
|
-
arrowright: "ArrowRight",
|
|
2668
|
-
home: "Home",
|
|
2669
|
-
end: "End",
|
|
2670
|
-
pageup: "PageUp",
|
|
2671
|
-
pagedown: "PageDown"
|
|
2672
|
-
};
|
|
2673
|
-
var isApple = () => typeof navigator !== "undefined" && /mac|iphone|ipad|ipod/i.test(navigator.userAgentData?.platform ?? navigator.platform ?? "");
|
|
2674
|
-
var keyName = (raw) => {
|
|
2675
|
-
if (!raw)
|
|
2676
|
-
return "";
|
|
2677
|
-
const named = NAMED[raw.toLowerCase()];
|
|
2678
|
-
if (named)
|
|
2679
|
-
return named;
|
|
2680
|
-
if (raw.length === 1)
|
|
2681
|
-
return raw.toUpperCase();
|
|
2682
|
-
if (/^f\d{1,2}$/i.test(raw))
|
|
2683
|
-
return raw.toUpperCase();
|
|
2684
|
-
return raw.charAt(0).toUpperCase() + raw.slice(1);
|
|
2685
|
-
};
|
|
2686
|
-
function normalizeCombo(combo) {
|
|
2687
|
-
const trimmed = combo.trim();
|
|
2688
|
-
if (!trimmed)
|
|
2689
|
-
return "";
|
|
2690
|
-
const parts = trimmed === "+" ? ["+"] : trimmed.replace(/\+\+$/, "+plus").split("+");
|
|
2691
|
-
const mods = new Set;
|
|
2692
|
-
let key = "";
|
|
2693
|
-
for (const part of parts) {
|
|
2694
|
-
const alias = ALIASES[part.trim().toLowerCase()] ?? ALIASES[part.trim()];
|
|
2695
|
-
if (alias === "Mod")
|
|
2696
|
-
mods.add(isApple() ? "Meta" : "Ctrl");
|
|
2697
|
-
else if (alias)
|
|
2698
|
-
mods.add(alias);
|
|
2699
|
-
else
|
|
2700
|
-
key = part === "plus" ? "+" : keyName(part.trim() || part);
|
|
2701
|
-
}
|
|
2702
|
-
if (!key)
|
|
2703
|
-
return "";
|
|
2704
|
-
return [...MODS.filter((m) => mods.has(m)), key].join("+");
|
|
2705
|
-
}
|
|
2706
|
-
var GLYPHS = { Ctrl: "⌃", Alt: "⌥", Shift: "⇧", Meta: "⌘" };
|
|
2707
|
-
var WORDS = { Ctrl: "Ctrl", Alt: "Alt", Shift: "Shift", Meta: "Meta" };
|
|
2708
|
-
var KEY_GLYPHS = {
|
|
2709
|
-
ArrowUp: "↑",
|
|
2710
|
-
ArrowDown: "↓",
|
|
2711
|
-
ArrowLeft: "←",
|
|
2712
|
-
ArrowRight: "→",
|
|
2713
|
-
Backspace: "⌫",
|
|
2714
|
-
Delete: "⌦",
|
|
2715
|
-
Enter: "↩",
|
|
2716
|
-
Escape: "Esc",
|
|
2717
|
-
Tab: "⇥",
|
|
2718
|
-
PageUp: "PgUp",
|
|
2719
|
-
PageDown: "PgDn"
|
|
2720
|
-
};
|
|
2721
|
-
function comboParts(combo, apple = isApple()) {
|
|
2722
|
-
const norm = normalizeCombo(combo);
|
|
2723
|
-
if (!norm)
|
|
2724
|
-
return [];
|
|
2725
|
-
const parts = norm === "+" ? ["+"] : norm.split("+");
|
|
2726
|
-
if (norm.endsWith("++"))
|
|
2727
|
-
parts.splice(-2, 2, "+");
|
|
2728
|
-
const key = parts.pop();
|
|
2729
|
-
const mods = parts;
|
|
2730
|
-
const shown = KEY_GLYPHS[key] ?? key;
|
|
2731
|
-
return apple ? [...mods.map((m) => GLYPHS[m]), shown] : [...mods.map((m) => WORDS[m]), shown];
|
|
2732
|
-
}
|
|
2733
|
-
|
|
2734
5219
|
// src/ui/layer.ts
|
|
2735
5220
|
var lookOf = (look) => ({ ...lookDefaults.current, ...look });
|
|
2736
5221
|
var roots = new WeakMap;
|
|
@@ -3283,6 +5768,43 @@ function edgeOf(panes, id) {
|
|
|
3283
5768
|
const first = panes.findIndex((p) => flex.has(p.id));
|
|
3284
5769
|
return panes.findIndex((p) => p.id === id) < first ? "start" : "end";
|
|
3285
5770
|
}
|
|
5771
|
+
function foldProgress(size, open, rail) {
|
|
5772
|
+
const span = open - rail;
|
|
5773
|
+
if (span <= 0)
|
|
5774
|
+
return size > rail ? 0 : 1;
|
|
5775
|
+
return Math.min(1, Math.max(0, (open - size) / span));
|
|
5776
|
+
}
|
|
5777
|
+
var FACE_OUT = 0.4;
|
|
5778
|
+
var FACE_IN = 0.3;
|
|
5779
|
+
var clamp01 = (x) => Math.min(1, Math.max(0, x));
|
|
5780
|
+
function face(prev, away) {
|
|
5781
|
+
const lo = clamp01(1 - away / FACE_IN);
|
|
5782
|
+
const hi = clamp01(1 - away / FACE_OUT);
|
|
5783
|
+
return Math.min(hi, Math.max(lo, prev));
|
|
5784
|
+
}
|
|
5785
|
+
function foldFaces(prev, p) {
|
|
5786
|
+
return { content: face(prev.content, p), rail: face(prev.rail, 1 - p) };
|
|
5787
|
+
}
|
|
5788
|
+
function chevronTurn(p, edge) {
|
|
5789
|
+
return (edge === "start" ? 180 : -180) * (1 - clamp01(p));
|
|
5790
|
+
}
|
|
5791
|
+
function chevronFlip(f, edge) {
|
|
5792
|
+
return chevronTurn(f, edge);
|
|
5793
|
+
}
|
|
5794
|
+
var FOLD_STYLE = { chevron: "flip", title: "travel", duration: 0.25 };
|
|
5795
|
+
function foldStyle(options = {}) {
|
|
5796
|
+
const duration = options.duration;
|
|
5797
|
+
return {
|
|
5798
|
+
chevron: options.chevron === "turn" ? "turn" : "flip",
|
|
5799
|
+
title: options.title === "fade" ? "fade" : "travel",
|
|
5800
|
+
duration: duration !== undefined && Number.isFinite(duration) && duration > 0 ? Math.min(2, duration) : FOLD_STYLE.duration
|
|
5801
|
+
};
|
|
5802
|
+
}
|
|
5803
|
+
function titleTravel(at, open, rail, edge, p) {
|
|
5804
|
+
const k = 1 - clamp01(p);
|
|
5805
|
+
const dx = at.title - at.label - (edge === "start" ? open - rail : 0);
|
|
5806
|
+
return { x: dx * k, y: at.dy * k, turn: -90 * k };
|
|
5807
|
+
}
|
|
3286
5808
|
function parseLayout(raw, panes) {
|
|
3287
5809
|
if (!raw || typeof raw !== "object")
|
|
3288
5810
|
return;
|
|
@@ -3310,8 +5832,26 @@ function serializeLayout(layout) {
|
|
|
3310
5832
|
}
|
|
3311
5833
|
|
|
3312
5834
|
// src/ui/split.ts
|
|
3313
|
-
var
|
|
5835
|
+
var FLIP = springMotion({ duration: 0.25, bounce: 0 });
|
|
5836
|
+
var FLIP_DEPTH = 28;
|
|
3314
5837
|
var SETTLE = springMotion({ duration: 0.3, bounce: 0 });
|
|
5838
|
+
var CONTENT_BLUR = 3;
|
|
5839
|
+
var LABEL_BLUR = 2;
|
|
5840
|
+
var COUNT_CLEAR = 0.12;
|
|
5841
|
+
var put = (el, prop, value) => {
|
|
5842
|
+
if (el.style.getPropertyValue(prop) !== value)
|
|
5843
|
+
el.style.setProperty(prop, value);
|
|
5844
|
+
};
|
|
5845
|
+
var hide = (el, hidden) => {
|
|
5846
|
+
if (hidden)
|
|
5847
|
+
el.setAttribute("aria-hidden", "true");
|
|
5848
|
+
else
|
|
5849
|
+
el.removeAttribute("aria-hidden");
|
|
5850
|
+
};
|
|
5851
|
+
var fade = (el, a, blur) => {
|
|
5852
|
+
put(el, "opacity", a >= 1 ? "" : a.toFixed(3));
|
|
5853
|
+
put(el, "filter", a >= 1 || a <= 0 || !blur ? "" : `blur(${(blur * (1 - a)).toFixed(2)}px)`);
|
|
5854
|
+
};
|
|
3315
5855
|
var iconNode = (doc, icon, label) => {
|
|
3316
5856
|
if (icon instanceof Node)
|
|
3317
5857
|
return icon;
|
|
@@ -3330,6 +5870,8 @@ function split(container, options) {
|
|
|
3330
5870
|
const row = direction === "row";
|
|
3331
5871
|
const specs = options.panes;
|
|
3332
5872
|
const flex = flexible(specs);
|
|
5873
|
+
const style = foldStyle(options.fold);
|
|
5874
|
+
const COLLAPSE = springMotion({ duration: style.duration, bounce: 0 });
|
|
3333
5875
|
const store = options.storage === undefined ? browserStorage() : options.storage;
|
|
3334
5876
|
const key = options.id ? storageKey(options.id, "split") : undefined;
|
|
3335
5877
|
const saved = (() => {
|
|
@@ -3358,13 +5900,31 @@ function split(container, options) {
|
|
|
3358
5900
|
const el = doc.createElement("section");
|
|
3359
5901
|
el.className = "knobs-pane";
|
|
3360
5902
|
el.dataset.pane = spec.id;
|
|
5903
|
+
const edge = edgeOf(specs, spec.id);
|
|
5904
|
+
el.dataset.edge = edge;
|
|
3361
5905
|
const clip = doc.createElement("div");
|
|
3362
5906
|
clip.className = "knobs-pane-clip";
|
|
3363
5907
|
const inner = doc.createElement("div");
|
|
3364
5908
|
inner.className = "knobs-pane-inner";
|
|
3365
5909
|
const body = doc.createElement("div");
|
|
3366
5910
|
body.className = "knobs-pane-body";
|
|
3367
|
-
const
|
|
5911
|
+
const shut = Boolean(collapsed[spec.id]);
|
|
5912
|
+
const view = {
|
|
5913
|
+
spec,
|
|
5914
|
+
el,
|
|
5915
|
+
clip,
|
|
5916
|
+
inner,
|
|
5917
|
+
body,
|
|
5918
|
+
edge,
|
|
5919
|
+
open: 0,
|
|
5920
|
+
shown: 0,
|
|
5921
|
+
folding: false,
|
|
5922
|
+
faces: { content: shut ? 0 : 1, rail: shut ? 1 : 0 },
|
|
5923
|
+
headPx: 0,
|
|
5924
|
+
chev: { iconEnd: 0, chevEnd: 0, dy: 0, scale: 1 },
|
|
5925
|
+
places: { title: 0, label: 0, dy: 0 },
|
|
5926
|
+
flip: new Spring(shut ? 1 : 0, FLIP.omega, FLIP.zeta, 0.002)
|
|
5927
|
+
};
|
|
3368
5928
|
if (spec.title !== undefined) {
|
|
3369
5929
|
const head = doc.createElement("header");
|
|
3370
5930
|
head.className = "knobs-pane-head";
|
|
@@ -3402,9 +5962,10 @@ function split(container, options) {
|
|
|
3402
5962
|
tooltip(b, () => collapsed[spec.id] ? "Open" : row ? "Collapse" : "Fold");
|
|
3403
5963
|
b.innerHTML = row ? ICONS.caret : ICONS.chevronDown;
|
|
3404
5964
|
if (row)
|
|
3405
|
-
b.firstElementChild?.setAttribute("style",
|
|
5965
|
+
b.firstElementChild?.setAttribute("style", edge === "start" ? "transform:scaleX(-1)" : "");
|
|
3406
5966
|
else
|
|
3407
5967
|
b.firstElementChild?.classList.add("knobs-pane-caret");
|
|
5968
|
+
view.toggleIcon = b.firstElementChild ?? undefined;
|
|
3408
5969
|
b.addEventListener("click", (e) => {
|
|
3409
5970
|
e.stopPropagation();
|
|
3410
5971
|
setCollapsed(spec.id, !collapsed[spec.id]);
|
|
@@ -3413,7 +5974,12 @@ function split(container, options) {
|
|
|
3413
5974
|
if (!row) {
|
|
3414
5975
|
head.dataset.toggles = "";
|
|
3415
5976
|
head.addEventListener("click", () => setCollapsed(spec.id, !collapsed[spec.id]));
|
|
3416
|
-
}
|
|
5977
|
+
} else
|
|
5978
|
+
head.addEventListener("dblclick", (e) => {
|
|
5979
|
+
if (e.target.closest(".knobs-pane-btn"))
|
|
5980
|
+
return;
|
|
5981
|
+
setCollapsed(spec.id, true);
|
|
5982
|
+
});
|
|
3417
5983
|
}
|
|
3418
5984
|
inner.append(head);
|
|
3419
5985
|
view.head = head;
|
|
@@ -3423,8 +5989,10 @@ function split(container, options) {
|
|
|
3423
5989
|
inner.append(body);
|
|
3424
5990
|
clip.append(inner);
|
|
3425
5991
|
el.append(clip);
|
|
3426
|
-
if (spec.scroll ?? spec.title !== undefined)
|
|
5992
|
+
if (spec.scroll ?? spec.title !== undefined) {
|
|
3427
5993
|
el.dataset.scroll = "";
|
|
5994
|
+
body.classList.add("knobs-scroll-fade");
|
|
5995
|
+
}
|
|
3428
5996
|
if (spec.element)
|
|
3429
5997
|
body.append(spec.element);
|
|
3430
5998
|
if (spec.collapsible && (row || !view.head)) {
|
|
@@ -3436,13 +6004,22 @@ function split(container, options) {
|
|
|
3436
6004
|
rail.setAttribute("aria-label", `Open ${label}`);
|
|
3437
6005
|
tooltip(rail, `Open ${label}`, { placement: row ? "right" : "top" });
|
|
3438
6006
|
const text = doc.createElement("span");
|
|
6007
|
+
text.className = "knobs-pane-rail-label";
|
|
3439
6008
|
text.textContent = spec.rail ?? spec.title ?? "";
|
|
3440
|
-
|
|
3441
|
-
|
|
6009
|
+
const chevron = doc.createElement("span");
|
|
6010
|
+
chevron.className = "knobs-pane-chevron";
|
|
6011
|
+
const turn = doc.createElement("span");
|
|
6012
|
+
turn.className = "knobs-pane-turn";
|
|
6013
|
+
turn.innerHTML = row ? ICONS.caret.replace("<svg", `<svg style="transform:scaleX(${edge === "start" ? 1 : -1})"`) : ICONS.chevronDown;
|
|
6014
|
+
chevron.append(turn);
|
|
6015
|
+
rail.append(chevron, text);
|
|
3442
6016
|
rail.addEventListener("click", () => setCollapsed(spec.id, false));
|
|
3443
6017
|
el.append(rail);
|
|
3444
6018
|
el.dataset.rail = "";
|
|
3445
6019
|
view.rail = rail;
|
|
6020
|
+
view.railLabel = text;
|
|
6021
|
+
view.chevron = chevron;
|
|
6022
|
+
view.turn = turn;
|
|
3446
6023
|
}
|
|
3447
6024
|
return view;
|
|
3448
6025
|
};
|
|
@@ -3472,9 +6049,10 @@ function split(container, options) {
|
|
|
3472
6049
|
const total = () => along(root);
|
|
3473
6050
|
let paneM = 0;
|
|
3474
6051
|
const marginOf = (v) => v.el.hasAttribute("data-nested") ? 0 : paneM;
|
|
3475
|
-
const headerSize = (v) => v.head ? v.
|
|
6052
|
+
const headerSize = (v) => v.head ? v.headPx + marginOf(v) * 2 : 0;
|
|
3476
6053
|
let railPx = 0;
|
|
3477
|
-
|
|
6054
|
+
let measured = false;
|
|
6055
|
+
const measure = () => {
|
|
3478
6056
|
syncFrame(root);
|
|
3479
6057
|
const probe = doc.createElement("div");
|
|
3480
6058
|
probe.style.cssText = "position:absolute;visibility:hidden;width:var(--ku-rail);height:var(--ku-pane-m)";
|
|
@@ -3483,8 +6061,44 @@ function split(container, options) {
|
|
|
3483
6061
|
paneM = r.height;
|
|
3484
6062
|
railPx = (r.width || 26) + paneM * 2;
|
|
3485
6063
|
probe.remove();
|
|
6064
|
+
for (const v of views.values())
|
|
6065
|
+
if (v.head)
|
|
6066
|
+
v.headPx = v.head.getBoundingClientRect().height;
|
|
6067
|
+
measured = root.isConnected && r.width > 0;
|
|
6068
|
+
};
|
|
6069
|
+
const measureChevrons = (only) => {
|
|
6070
|
+
if (!row)
|
|
6071
|
+
return;
|
|
6072
|
+
for (const v of only ? [only] : views.values()) {
|
|
6073
|
+
if (!v.chevron || !v.toggleIcon || !v.rail || !v.railLabel)
|
|
6074
|
+
continue;
|
|
6075
|
+
const inner = v.inner.getBoundingClientRect();
|
|
6076
|
+
const icon = v.toggleIcon.getBoundingClientRect();
|
|
6077
|
+
const rail = v.rail.getBoundingClientRect();
|
|
6078
|
+
const chev = v.chevron.getBoundingClientRect();
|
|
6079
|
+
if (!inner.width || !chev.width)
|
|
6080
|
+
continue;
|
|
6081
|
+
v.chev = {
|
|
6082
|
+
iconEnd: inner.right - (icon.left + icon.width / 2),
|
|
6083
|
+
chevEnd: rail.right - (chev.left + chev.width / 2),
|
|
6084
|
+
dy: icon.top + icon.height / 2 - inner.top - (chev.top + chev.height / 2 - rail.top),
|
|
6085
|
+
scale: icon.width / chev.width
|
|
6086
|
+
};
|
|
6087
|
+
if (!v.titleEl)
|
|
6088
|
+
continue;
|
|
6089
|
+
const moved = v.railLabel.style.transform;
|
|
6090
|
+
v.railLabel.style.transform = "none";
|
|
6091
|
+
const label = v.railLabel.getBoundingClientRect();
|
|
6092
|
+
v.railLabel.style.transform = moved;
|
|
6093
|
+
const title = v.titleEl.getBoundingClientRect();
|
|
6094
|
+
v.places = {
|
|
6095
|
+
title: title.left + title.width / 2 - inner.left,
|
|
6096
|
+
label: label.left + label.width / 2 - rail.left,
|
|
6097
|
+
dy: title.top + title.height / 2 - inner.top - (label.top + label.height / 2 - rail.top)
|
|
6098
|
+
};
|
|
6099
|
+
}
|
|
3486
6100
|
};
|
|
3487
|
-
|
|
6101
|
+
measure();
|
|
3488
6102
|
const rail = (id) => {
|
|
3489
6103
|
const v = views.get(id);
|
|
3490
6104
|
return !row && v.head ? headerSize(v) : railPx;
|
|
@@ -3497,47 +6111,105 @@ function split(container, options) {
|
|
|
3497
6111
|
};
|
|
3498
6112
|
const target = () => solve({ panes: specs, sizes, collapsed, total: total(), rail });
|
|
3499
6113
|
const current = () => solve({ panes: specs, sizes, collapsed, total: total(), rail, fixed: fixed() });
|
|
6114
|
+
const SIDES = { row: ["right", "left"], column: ["bottom", "top"] };
|
|
6115
|
+
const sides = (v) => {
|
|
6116
|
+
const [a, b] = SIDES[direction];
|
|
6117
|
+
return v.edge === "start" ? [a, b] : [b, a];
|
|
6118
|
+
};
|
|
6119
|
+
const renderFold = (v, size) => {
|
|
6120
|
+
const id = v.spec.id;
|
|
6121
|
+
const isCollapsed = collapsed[id];
|
|
6122
|
+
const folding = isCollapsed || v.folding;
|
|
6123
|
+
const r = rail(id);
|
|
6124
|
+
const open = folding ? Math.max(v.open, r + 1) : size;
|
|
6125
|
+
const p = folding ? foldProgress(size, open, r) : 0;
|
|
6126
|
+
v.faces = foldFaces(v.faces, p);
|
|
6127
|
+
const m = marginOf(v);
|
|
6128
|
+
const settled = !springs.has(id);
|
|
6129
|
+
const content = isCollapsed && settled ? 1 : v.faces.content;
|
|
6130
|
+
put(v.rail ? v.inner : v.body, "will-change", v.folding ? "opacity, filter" : "");
|
|
6131
|
+
if (!v.rail) {
|
|
6132
|
+
put(v.inner, "height", folding ? `${open - m * 2}px` : "");
|
|
6133
|
+
put(v.inner, "bottom", folding ? "auto" : "");
|
|
6134
|
+
fade(v.body, content, CONTENT_BLUR);
|
|
6135
|
+
if (v.toggleIcon)
|
|
6136
|
+
put(v.toggleIcon, "transform", p ? `rotate(${(-90 * p).toFixed(2)}deg)` : "");
|
|
6137
|
+
hide(v.body, isCollapsed && settled);
|
|
6138
|
+
return;
|
|
6139
|
+
}
|
|
6140
|
+
const [side, far] = sides(v);
|
|
6141
|
+
put(v.inner, row ? "width" : "height", folding ? `${open - m * 2}px` : "");
|
|
6142
|
+
put(v.inner, side, folding ? "0" : "");
|
|
6143
|
+
put(v.inner, far, folding ? "auto" : "");
|
|
6144
|
+
fade(v.inner, content, CONTENT_BLUR);
|
|
6145
|
+
hide(v.clip, isCollapsed && settled);
|
|
6146
|
+
const morph = row && Boolean(v.toggleIcon) && !v.el.hasAttribute("data-nested");
|
|
6147
|
+
v.el.toggleAttribute("data-morph", morph);
|
|
6148
|
+
const label = v.railLabel;
|
|
6149
|
+
const travel = morph && style.title === "travel" && Boolean(v.titleEl) && label.textContent === v.titleEl.textContent;
|
|
6150
|
+
v.el.toggleAttribute("data-travel", travel && folding);
|
|
6151
|
+
if (v.countEl)
|
|
6152
|
+
put(v.countEl, "opacity", travel && folding && p > 0 ? Math.max(0, 1 - p / COUNT_CLEAR).toFixed(3) : "");
|
|
6153
|
+
const k = 1 - p;
|
|
6154
|
+
if (travel && !folding) {
|
|
6155
|
+
put(label, "opacity", "0");
|
|
6156
|
+
put(label, "filter", "");
|
|
6157
|
+
put(label, "transform", "");
|
|
6158
|
+
put(label, "color", "");
|
|
6159
|
+
} else if (travel) {
|
|
6160
|
+
const t = titleTravel(v.places, open, r, v.edge, p);
|
|
6161
|
+
put(label, "opacity", "");
|
|
6162
|
+
put(label, "filter", "");
|
|
6163
|
+
put(label, "transform", k === 0 ? "" : `translate(${t.x.toFixed(2)}px,${t.y.toFixed(2)}px) rotate(${t.turn.toFixed(2)}deg)`);
|
|
6164
|
+
put(label, "color", k === 0 ? "" : `color-mix(in srgb,var(--ku-text) ${(k * 100).toFixed(1)}%,var(--ku-label))`);
|
|
6165
|
+
} else {
|
|
6166
|
+
put(label, "transform", "");
|
|
6167
|
+
put(label, "color", "");
|
|
6168
|
+
}
|
|
6169
|
+
if (morph) {
|
|
6170
|
+
put(v.rail, "opacity", "1");
|
|
6171
|
+
if (!travel)
|
|
6172
|
+
fade(label, v.faces.rail, LABEL_BLUR);
|
|
6173
|
+
const c = v.chev;
|
|
6174
|
+
const dx = c.chevEnd - c.iconEnd + (v.edge === "end" ? open - r : 0);
|
|
6175
|
+
const move = `translate(${(dx * k).toFixed(2)}px,${(c.dy * k).toFixed(2)}px)`;
|
|
6176
|
+
const size = `scale(${(1 + (c.scale - 1) * k).toFixed(4)})`;
|
|
6177
|
+
if (style.chevron === "turn")
|
|
6178
|
+
put(v.turn, "transform", k === 0 ? "" : `${move} rotate(${chevronTurn(p, v.edge).toFixed(2)}deg) ${size}`);
|
|
6179
|
+
else {
|
|
6180
|
+
const turn = chevronFlip(v.flip.x, v.edge);
|
|
6181
|
+
const over = v.flip.resting ? turn === 0 ? "" : "scaleX(-1)" : `perspective(${FLIP_DEPTH}px) rotateY(${turn.toFixed(2)}deg)`;
|
|
6182
|
+
put(v.turn, "transform", k === 0 && !over ? "" : `${move} ${size} ${over}`);
|
|
6183
|
+
}
|
|
6184
|
+
} else {
|
|
6185
|
+
const a = v.faces.rail;
|
|
6186
|
+
put(v.rail, "opacity", a >= 1 ? "1" : a.toFixed(3));
|
|
6187
|
+
put(label, "opacity", "");
|
|
6188
|
+
put(label, "filter", a >= 1 || a <= 0 ? "" : `blur(${(LABEL_BLUR * (1 - a)).toFixed(2)}px)`);
|
|
6189
|
+
put(v.turn, "transform", "");
|
|
6190
|
+
}
|
|
6191
|
+
};
|
|
3500
6192
|
let lastReport = "";
|
|
3501
6193
|
const render = (shown) => {
|
|
3502
6194
|
for (const spec of specs) {
|
|
3503
6195
|
const v = views.get(spec.id);
|
|
3504
6196
|
const size = Math.max(0, shown[spec.id] ?? 0);
|
|
3505
6197
|
v.shown = size;
|
|
3506
|
-
v.el
|
|
3507
|
-
v.el
|
|
3508
|
-
|
|
3509
|
-
|
|
3510
|
-
|
|
3511
|
-
|
|
3512
|
-
v.el.setAttribute("aria-hidden", String(isCollapsed && !moving));
|
|
3513
|
-
const r = rail(spec.id);
|
|
3514
|
-
const open = Math.max(v.open, r + 1);
|
|
3515
|
-
const m = marginOf(v);
|
|
3516
|
-
if (moving || isCollapsed) {
|
|
3517
|
-
const p = Math.min(1, Math.max(0, (open - size) / (open - r)));
|
|
3518
|
-
if (v.rail) {
|
|
3519
|
-
const visible = Math.max(0, (size - r) / (open - r) * open);
|
|
3520
|
-
const edge = edgeOf(specs, spec.id);
|
|
3521
|
-
const side = row ? edge === "start" ? "right" : "left" : edge === "start" ? "bottom" : "top";
|
|
3522
|
-
v.clip.style.cssText = `${row ? "width" : "height"}:${Math.max(0, visible - m * 2)}px;${side}:0;${row ? side === "right" ? "left:auto" : "right:auto" : side === "bottom" ? "top:auto" : "bottom:auto"}`;
|
|
3523
|
-
v.inner.style.cssText = `${row ? "width" : "height"}:${open - m * 2}px;${side}:0;${row ? side === "right" ? "left:auto" : "right:auto" : side === "bottom" ? "top:auto" : "bottom:auto"}`;
|
|
3524
|
-
v.rail.style.opacity = String(Math.max(0, (p - 0.35) / 0.65));
|
|
3525
|
-
} else {
|
|
3526
|
-
v.clip.style.cssText = "";
|
|
3527
|
-
v.inner.style.cssText = `height:${open - m * 2}px;bottom:auto`;
|
|
3528
|
-
}
|
|
3529
|
-
} else {
|
|
3530
|
-
v.clip.style.cssText = "";
|
|
3531
|
-
v.inner.style.cssText = "";
|
|
3532
|
-
if (v.rail)
|
|
3533
|
-
v.rail.style.opacity = "0";
|
|
3534
|
-
}
|
|
6198
|
+
put(v.el, "flex-basis", `${size}px`);
|
|
6199
|
+
put(v.el, row ? "width" : "height", `${size}px`);
|
|
6200
|
+
v.el.toggleAttribute("data-collapsed", collapsed[spec.id]);
|
|
6201
|
+
v.el.toggleAttribute("data-settled", !springs.has(spec.id));
|
|
6202
|
+
if (spec.collapsible)
|
|
6203
|
+
renderFold(v, size);
|
|
3535
6204
|
}
|
|
3536
6205
|
dividers.forEach((d, i) => {
|
|
3537
6206
|
const a = specs[i];
|
|
3538
6207
|
const b = specs[i + 1];
|
|
3539
|
-
|
|
3540
|
-
d.
|
|
6208
|
+
const now = String(Math.round(views.get(a.id).shown));
|
|
6209
|
+
if (d.getAttribute("aria-valuenow") !== now)
|
|
6210
|
+
d.setAttribute("aria-valuenow", now);
|
|
6211
|
+
if (!d.hasAttribute("aria-controls"))
|
|
6212
|
+
d.setAttribute("aria-controls", `${a.id} ${b.id}`);
|
|
3541
6213
|
});
|
|
3542
6214
|
const layout = snapshot();
|
|
3543
6215
|
const report = JSON.stringify(layout);
|
|
@@ -3570,51 +6242,76 @@ function split(container, options) {
|
|
|
3570
6242
|
const loop = frameLoop(win, (dt) => {
|
|
3571
6243
|
for (const [id, s] of springs) {
|
|
3572
6244
|
s.step(dt);
|
|
3573
|
-
if (s.resting)
|
|
6245
|
+
if (s.resting) {
|
|
3574
6246
|
springs.delete(id);
|
|
6247
|
+
views.get(id).folding = false;
|
|
6248
|
+
}
|
|
6249
|
+
}
|
|
6250
|
+
let flipping = false;
|
|
6251
|
+
for (const v of views.values()) {
|
|
6252
|
+
v.flip.step(dt);
|
|
6253
|
+
if (!v.flip.resting)
|
|
6254
|
+
flipping = true;
|
|
3575
6255
|
}
|
|
3576
6256
|
const shown = current();
|
|
3577
|
-
|
|
3578
|
-
if (springs.size === 0) {
|
|
6257
|
+
if (springs.size === 0)
|
|
3579
6258
|
rememberOpen(shown);
|
|
3580
|
-
|
|
3581
|
-
|
|
3582
|
-
return springs.size > 0;
|
|
6259
|
+
render(shown);
|
|
6260
|
+
return springs.size > 0 || flipping;
|
|
3583
6261
|
});
|
|
3584
6262
|
const animate = (motion = SETTLE) => {
|
|
3585
6263
|
const to = target();
|
|
6264
|
+
for (const v of views.values())
|
|
6265
|
+
if (v.folding && !collapsed[v.spec.id])
|
|
6266
|
+
v.open = to[v.spec.id];
|
|
3586
6267
|
if (reducedMotion(win)) {
|
|
3587
6268
|
springs.clear();
|
|
3588
|
-
|
|
6269
|
+
const arriving = [...views.values()].filter((v) => v.folding).map((v) => collapsed[v.spec.id] ? v.rail : v.rail ? v.inner : v.body);
|
|
6270
|
+
for (const v of views.values()) {
|
|
6271
|
+
v.folding = false;
|
|
6272
|
+
v.flip.snap(collapsed[v.spec.id] ? 1 : 0);
|
|
6273
|
+
}
|
|
3589
6274
|
rememberOpen(to);
|
|
6275
|
+
render(to);
|
|
6276
|
+
for (const el of arriving)
|
|
6277
|
+
el?.animate([{ opacity: 0 }, { opacity: 1 }], {
|
|
6278
|
+
duration: 160,
|
|
6279
|
+
easing: "cubic-bezier(.2,.8,.2,1)"
|
|
6280
|
+
});
|
|
3590
6281
|
return;
|
|
3591
6282
|
}
|
|
3592
6283
|
for (const spec of specs) {
|
|
3593
|
-
const
|
|
6284
|
+
const v = views.get(spec.id);
|
|
3594
6285
|
const goal = to[spec.id];
|
|
3595
6286
|
const s = springs.get(spec.id);
|
|
3596
6287
|
if (s) {
|
|
3597
6288
|
s.omega = motion.omega;
|
|
3598
6289
|
s.zeta = motion.zeta;
|
|
3599
6290
|
s.set(goal);
|
|
3600
|
-
} else if (Math.abs(goal -
|
|
3601
|
-
springs.set(spec.id, new Spring(
|
|
3602
|
-
}
|
|
6291
|
+
} else if (Math.abs(goal - v.shown) > 0.5) {
|
|
6292
|
+
springs.set(spec.id, new Spring(v.shown, motion.omega, motion.zeta, 0.2).set(goal));
|
|
6293
|
+
} else
|
|
6294
|
+
v.folding = false;
|
|
3603
6295
|
}
|
|
3604
6296
|
loop.wake();
|
|
3605
6297
|
};
|
|
6298
|
+
const fold = (id, value) => {
|
|
6299
|
+
const v = views.get(id);
|
|
6300
|
+
if (value && !v.folding)
|
|
6301
|
+
v.open = v.shown;
|
|
6302
|
+
collapsed[id] = value;
|
|
6303
|
+
v.folding = true;
|
|
6304
|
+
v.flip.set(value ? 1 : 0);
|
|
6305
|
+
};
|
|
6306
|
+
const release = (id) => {
|
|
6307
|
+
springs.delete(id);
|
|
6308
|
+
views.get(id).folding = false;
|
|
6309
|
+
};
|
|
3606
6310
|
function setCollapsed(id, value, save = true) {
|
|
3607
6311
|
const spec = specs.find((p) => p.id === id);
|
|
3608
6312
|
if (!spec?.collapsible || collapsed[id] === value)
|
|
3609
6313
|
return;
|
|
3610
|
-
|
|
3611
|
-
if (value && !springs.has(id))
|
|
3612
|
-
v.open = v.shown;
|
|
3613
|
-
if (!value) {
|
|
3614
|
-
collapsed[id] = false;
|
|
3615
|
-
v.open = target()[id];
|
|
3616
|
-
}
|
|
3617
|
-
collapsed[id] = value;
|
|
6314
|
+
fold(id, value);
|
|
3618
6315
|
if (save)
|
|
3619
6316
|
persist();
|
|
3620
6317
|
animate(COLLAPSE);
|
|
@@ -3636,7 +6333,8 @@ function split(container, options) {
|
|
|
3636
6333
|
d.focus({ preventScroll: true });
|
|
3637
6334
|
const shown = current();
|
|
3638
6335
|
for (const id of [a.id, b.id])
|
|
3639
|
-
|
|
6336
|
+
if (!collapsed[id])
|
|
6337
|
+
release(id);
|
|
3640
6338
|
start = { ...shown };
|
|
3641
6339
|
for (const id of [a.id, b.id])
|
|
3642
6340
|
if (collapsed[id])
|
|
@@ -3670,7 +6368,7 @@ function split(container, options) {
|
|
|
3670
6368
|
return;
|
|
3671
6369
|
setSizes(result.sizes);
|
|
3672
6370
|
for (const id of [a.id, b.id])
|
|
3673
|
-
|
|
6371
|
+
release(id);
|
|
3674
6372
|
const shown = current();
|
|
3675
6373
|
render(shown);
|
|
3676
6374
|
rememberOpen(shown);
|
|
@@ -3733,17 +6431,21 @@ function split(container, options) {
|
|
|
3733
6431
|
}
|
|
3734
6432
|
function reset(...ids) {
|
|
3735
6433
|
const which = ids.length ? ids : specs.map((p) => p.id);
|
|
6434
|
+
let folds = false;
|
|
3736
6435
|
for (const id of which) {
|
|
3737
6436
|
delete sizes[id];
|
|
3738
6437
|
const spec = specs.find((p) => p.id === id);
|
|
3739
|
-
|
|
3740
|
-
|
|
6438
|
+
const shut = Boolean(spec?.collapsed);
|
|
6439
|
+
if (spec?.collapsible && collapsed[id] !== shut) {
|
|
6440
|
+
fold(id, shut);
|
|
6441
|
+
folds = true;
|
|
6442
|
+
}
|
|
3741
6443
|
}
|
|
3742
6444
|
if (which.some((id) => flex.has(id)))
|
|
3743
6445
|
for (const id of flex)
|
|
3744
6446
|
delete sizes[id];
|
|
3745
6447
|
persist();
|
|
3746
|
-
animate();
|
|
6448
|
+
animate(folds ? COLLAPSE : SETTLE);
|
|
3747
6449
|
}
|
|
3748
6450
|
{
|
|
3749
6451
|
const first = target();
|
|
@@ -3752,18 +6454,29 @@ function split(container, options) {
|
|
|
3752
6454
|
views.get(spec.id).open = collapsed[spec.id] ? solve({ panes: specs, sizes, collapsed: open, total: total(), rail })[spec.id] : first[spec.id];
|
|
3753
6455
|
}
|
|
3754
6456
|
render(first);
|
|
6457
|
+
measureChevrons();
|
|
6458
|
+
render(first);
|
|
3755
6459
|
}
|
|
3756
|
-
const relayout = () => {
|
|
3757
|
-
|
|
3758
|
-
|
|
6460
|
+
const relayout = (remeasure) => {
|
|
6461
|
+
if (remeasure)
|
|
6462
|
+
measure();
|
|
6463
|
+
if (springs.size) {
|
|
6464
|
+
if (remeasure)
|
|
6465
|
+
measureChevrons();
|
|
3759
6466
|
return loop.wake();
|
|
6467
|
+
}
|
|
3760
6468
|
const shown = target();
|
|
3761
|
-
render(shown);
|
|
3762
6469
|
rememberOpen(shown);
|
|
6470
|
+
render(shown);
|
|
6471
|
+
if (remeasure) {
|
|
6472
|
+
measureChevrons();
|
|
6473
|
+
render(shown);
|
|
6474
|
+
}
|
|
3763
6475
|
};
|
|
3764
|
-
const ro = new ResizeObserver(relayout);
|
|
6476
|
+
const ro = new ResizeObserver(() => relayout(!measured));
|
|
3765
6477
|
ro.observe(root);
|
|
3766
|
-
|
|
6478
|
+
const restyled = () => relayout(true);
|
|
6479
|
+
root.addEventListener("knobs-restyle", restyled);
|
|
3767
6480
|
const paneApi = (id) => {
|
|
3768
6481
|
const v = views.get(id);
|
|
3769
6482
|
if (!v)
|
|
@@ -3786,9 +6499,9 @@ function split(container, options) {
|
|
|
3786
6499
|
v.titleEl.textContent = text;
|
|
3787
6500
|
if (v.countEl && count !== undefined)
|
|
3788
6501
|
v.countEl.textContent = String(count);
|
|
3789
|
-
|
|
3790
|
-
|
|
3791
|
-
|
|
6502
|
+
if (v.railLabel)
|
|
6503
|
+
v.railLabel.textContent = text;
|
|
6504
|
+
measureChevrons(v);
|
|
3792
6505
|
}
|
|
3793
6506
|
};
|
|
3794
6507
|
};
|
|
@@ -3823,7 +6536,7 @@ function split(container, options) {
|
|
|
3823
6536
|
unregisterHints?.();
|
|
3824
6537
|
loop.stop();
|
|
3825
6538
|
ro.disconnect();
|
|
3826
|
-
root.removeEventListener("knobs-restyle",
|
|
6539
|
+
root.removeEventListener("knobs-restyle", restyled);
|
|
3827
6540
|
host?.removeAttribute("data-nested");
|
|
3828
6541
|
root.remove();
|
|
3829
6542
|
}
|
|
@@ -3894,7 +6607,7 @@ function menu(anchor, items, options = {}) {
|
|
|
3894
6607
|
const lead = doc.createElement("span");
|
|
3895
6608
|
lead.className = "knobs-menu-lead";
|
|
3896
6609
|
if (e.checked)
|
|
3897
|
-
lead.innerHTML = e.radio ? RADIO :
|
|
6610
|
+
lead.innerHTML = e.radio ? RADIO : CHECK2;
|
|
3898
6611
|
else if (e.icon) {
|
|
3899
6612
|
if (e.icon.trimStart().startsWith("<"))
|
|
3900
6613
|
lead.innerHTML = e.icon;
|
|
@@ -4232,7 +6945,7 @@ function contextMenu(target, items, options = {}) {
|
|
|
4232
6945
|
}
|
|
4233
6946
|
};
|
|
4234
6947
|
}
|
|
4235
|
-
var
|
|
6948
|
+
var CHECK2 = '<svg viewBox="0 0 16 16" aria-hidden="true" fill="none" stroke="currentColor" stroke-width="1.7" stroke-linecap="round" stroke-linejoin="round"><path d="M3.5 8.5 6.5 11.5 12.5 4.5"/></svg>';
|
|
4236
6949
|
var RADIO = '<svg viewBox="0 0 16 16" aria-hidden="true"><circle cx="8" cy="8" r="3" fill="currentColor"/></svg>';
|
|
4237
6950
|
|
|
4238
6951
|
// src/ui/timeline-model.ts
|
|
@@ -4623,7 +7336,7 @@ function timeline(container, options) {
|
|
|
4623
7336
|
renderHead();
|
|
4624
7337
|
};
|
|
4625
7338
|
const syncPlay = () => {
|
|
4626
|
-
playBtn.innerHTML = playing ? PAUSE :
|
|
7339
|
+
playBtn.innerHTML = playing ? PAUSE : PLAY2;
|
|
4627
7340
|
playBtn.setAttribute("aria-label", playing ? "Pause" : "Play");
|
|
4628
7341
|
root.toggleAttribute("data-playing", playing);
|
|
4629
7342
|
};
|
|
@@ -5234,7 +7947,7 @@ function timeline(container, options) {
|
|
|
5234
7947
|
};
|
|
5235
7948
|
return api;
|
|
5236
7949
|
}
|
|
5237
|
-
var
|
|
7950
|
+
var PLAY2 = '<svg viewBox="0 0 16 16" aria-hidden="true"><path d="M5 3.6v8.8c0 .5.5.8.9.5l6.6-4.4c.4-.3.4-.8 0-1L5.9 3.1c-.4-.3-.9 0-.9.5Z" fill="currentColor"/></svg>';
|
|
5238
7951
|
var PAUSE = '<svg viewBox="0 0 16 16" aria-hidden="true"><rect x="4" y="3.2" width="2.8" height="9.6" rx="1" fill="currentColor"/><rect x="9.2" y="3.2" width="2.8" height="9.6" rx="1" fill="currentColor"/></svg>';
|
|
5239
7952
|
|
|
5240
7953
|
// src/ui/tree-model.ts
|
|
@@ -5470,7 +8183,7 @@ function virtualWindow(scrollTop, height, rowHeight, count, overscan = 6) {
|
|
|
5470
8183
|
}
|
|
5471
8184
|
|
|
5472
8185
|
// src/ui/tree.ts
|
|
5473
|
-
var MOVE = springMotion({ duration: 0.
|
|
8186
|
+
var MOVE = springMotion({ duration: 0.3, bounce: 0 });
|
|
5474
8187
|
var EDGE = 28;
|
|
5475
8188
|
function tree(container, options) {
|
|
5476
8189
|
const doc = container.ownerDocument;
|
|
@@ -5493,7 +8206,7 @@ function tree(container, options) {
|
|
|
5493
8206
|
let index = indexTree(items, access);
|
|
5494
8207
|
const renamed = new Map;
|
|
5495
8208
|
const root = doc.createElement("div");
|
|
5496
|
-
root.className = "knobs-tree";
|
|
8209
|
+
root.className = "knobs-tree knobs-scroll-fade";
|
|
5497
8210
|
root.tabIndex = 0;
|
|
5498
8211
|
root.setAttribute("role", "tree");
|
|
5499
8212
|
root.setAttribute("aria-multiselectable", "true");
|
|
@@ -5509,6 +8222,7 @@ function tree(container, options) {
|
|
|
5509
8222
|
empty.hidden = true;
|
|
5510
8223
|
root.append(space, line, empty);
|
|
5511
8224
|
container.append(root);
|
|
8225
|
+
const scrollbar = overlayScrollbar(root, space);
|
|
5512
8226
|
let h = 26;
|
|
5513
8227
|
let indent = 14;
|
|
5514
8228
|
let inset = 8;
|
|
@@ -5559,7 +8273,7 @@ function tree(container, options) {
|
|
|
5559
8273
|
const icon = doc.createElement("span");
|
|
5560
8274
|
icon.className = "knobs-tree-icon";
|
|
5561
8275
|
const label = doc.createElement("span");
|
|
5562
|
-
label.className = "knobs-tree-label";
|
|
8276
|
+
label.className = "knobs-tree-label knobs-scroll-fade-x";
|
|
5563
8277
|
const text = doc.createElement("span");
|
|
5564
8278
|
text.className = "knobs-tree-text";
|
|
5565
8279
|
label.append(text);
|
|
@@ -6121,6 +8835,8 @@ function tree(container, options) {
|
|
|
6121
8835
|
const rule = options.typeahead ?? true;
|
|
6122
8836
|
if (rule === false)
|
|
6123
8837
|
return false;
|
|
8838
|
+
if (!(running && typed !== "") && !/^[\p{L}\p{N}]$/u.test(key))
|
|
8839
|
+
return false;
|
|
6124
8840
|
if (!(running && typed !== "") && options.hints !== false && claimsHintKey(doc, key))
|
|
6125
8841
|
return false;
|
|
6126
8842
|
return rule === true || running && typed !== "" || rule(key);
|
|
@@ -6356,6 +9072,7 @@ function tree(container, options) {
|
|
|
6356
9072
|
loop.stop();
|
|
6357
9073
|
autoScroll.stop();
|
|
6358
9074
|
ro.disconnect();
|
|
9075
|
+
scrollbar.dispose();
|
|
6359
9076
|
dragging?.ghost.remove();
|
|
6360
9077
|
root.remove();
|
|
6361
9078
|
}
|
|
@@ -6370,6 +9087,14 @@ function builtinIcon(id, on) {
|
|
|
6370
9087
|
}
|
|
6371
9088
|
|
|
6372
9089
|
// src/ui/solid.ts
|
|
9090
|
+
var follow = (read, apply) => {
|
|
9091
|
+
const changes = propChanges(untrack(read));
|
|
9092
|
+
createEffect(() => {
|
|
9093
|
+
const changed = changes(read());
|
|
9094
|
+
if (anyChanged(changed))
|
|
9095
|
+
untrack(() => apply(changed));
|
|
9096
|
+
});
|
|
9097
|
+
};
|
|
6373
9098
|
function Split(props) {
|
|
6374
9099
|
if (typeof document === "undefined")
|
|
6375
9100
|
return null;
|
|
@@ -6410,19 +9135,14 @@ function Tree(props) {
|
|
|
6410
9135
|
}
|
|
6411
9136
|
}));
|
|
6412
9137
|
untrack(() => props.onReady?.(t));
|
|
6413
|
-
|
|
6414
|
-
|
|
6415
|
-
|
|
6416
|
-
|
|
6417
|
-
|
|
6418
|
-
|
|
6419
|
-
|
|
6420
|
-
|
|
6421
|
-
});
|
|
6422
|
-
};
|
|
6423
|
-
follow(() => props.items, (items) => t.update(items));
|
|
6424
|
-
follow(() => props.selection, (selection) => selection && t.select(selection));
|
|
6425
|
-
follow(() => props.filter, (filter) => t.filter(filter));
|
|
9138
|
+
follow(() => ({ items: props.items, filter: props.filter, selection: props.selection }), (c) => {
|
|
9139
|
+
if ("items" in c)
|
|
9140
|
+
t.update(c.items);
|
|
9141
|
+
if ("filter" in c)
|
|
9142
|
+
t.filter(c.filter);
|
|
9143
|
+
if (c.selection)
|
|
9144
|
+
t.select(c.selection);
|
|
9145
|
+
});
|
|
6426
9146
|
onCleanup(() => t.dispose());
|
|
6427
9147
|
return host;
|
|
6428
9148
|
}
|
|
@@ -6440,38 +9160,20 @@ function Timeline(props) {
|
|
|
6440
9160
|
onCross: (m) => props.onCross?.(m)
|
|
6441
9161
|
}));
|
|
6442
9162
|
untrack(() => props.onReady?.(t));
|
|
6443
|
-
|
|
6444
|
-
|
|
6445
|
-
|
|
6446
|
-
|
|
6447
|
-
|
|
6448
|
-
|
|
6449
|
-
|
|
6450
|
-
|
|
6451
|
-
|
|
6452
|
-
|
|
6453
|
-
|
|
6454
|
-
|
|
6455
|
-
|
|
6456
|
-
|
|
6457
|
-
return;
|
|
6458
|
-
if (key === "markers" && value === t.markers || key === "ranges" && value === t.ranges)
|
|
6459
|
-
return;
|
|
6460
|
-
t.update({ [key]: value });
|
|
6461
|
-
});
|
|
6462
|
-
});
|
|
6463
|
-
};
|
|
6464
|
-
for (const key of [
|
|
6465
|
-
"time",
|
|
6466
|
-
"playing",
|
|
6467
|
-
"markers",
|
|
6468
|
-
"ranges",
|
|
6469
|
-
"duration",
|
|
6470
|
-
"fps",
|
|
6471
|
-
"units",
|
|
6472
|
-
"speed"
|
|
6473
|
-
])
|
|
6474
|
-
follow(key);
|
|
9163
|
+
follow(() => ({
|
|
9164
|
+
duration: props.duration,
|
|
9165
|
+
fps: props.fps,
|
|
9166
|
+
units: props.units,
|
|
9167
|
+
speed: props.speed,
|
|
9168
|
+
markers: props.markers,
|
|
9169
|
+
ranges: props.ranges,
|
|
9170
|
+
time: props.time,
|
|
9171
|
+
playing: props.playing
|
|
9172
|
+
}), (c) => {
|
|
9173
|
+
const next = timelineChanges(t, c);
|
|
9174
|
+
if (anyChanged(next))
|
|
9175
|
+
t.update(next);
|
|
9176
|
+
});
|
|
6475
9177
|
onCleanup(() => t.dispose());
|
|
6476
9178
|
return host;
|
|
6477
9179
|
}
|
|
@@ -6488,23 +9190,72 @@ function Bento(props) {
|
|
|
6488
9190
|
onActive: (id) => props.onActive?.(id)
|
|
6489
9191
|
}));
|
|
6490
9192
|
untrack(() => props.onReady?.(b));
|
|
6491
|
-
|
|
6492
|
-
|
|
6493
|
-
|
|
6494
|
-
|
|
6495
|
-
|
|
6496
|
-
|
|
6497
|
-
else
|
|
6498
|
-
untrack(() => apply(value));
|
|
6499
|
-
});
|
|
6500
|
-
};
|
|
6501
|
-
follow(() => props.items, (items) => b.update(items));
|
|
6502
|
-
follow(() => props.picks, (picks) => picks && b.pick(picks));
|
|
9193
|
+
follow(() => ({ items: props.items, picks: props.picks }), (c) => {
|
|
9194
|
+
if ("items" in c)
|
|
9195
|
+
b.update(c.items);
|
|
9196
|
+
if (c.picks)
|
|
9197
|
+
b.pick(c.picks);
|
|
9198
|
+
});
|
|
6503
9199
|
onCleanup(() => b.dispose());
|
|
6504
9200
|
return host;
|
|
6505
9201
|
}
|
|
9202
|
+
function Activity(props) {
|
|
9203
|
+
if (typeof document === "undefined")
|
|
9204
|
+
return null;
|
|
9205
|
+
const host = document.createElement("div");
|
|
9206
|
+
host.style.height = "100%";
|
|
9207
|
+
const a = untrack(() => activity(host, {
|
|
9208
|
+
...props,
|
|
9209
|
+
onSelect: (id, event, source) => props.onSelect?.(id, event, source),
|
|
9210
|
+
onActivate: (id, event) => props.onActivate?.(id, event),
|
|
9211
|
+
onToggle: (id, open) => props.onToggle?.(id, open),
|
|
9212
|
+
onPlay: props.onPlay && ((id, event) => props.onPlay?.(id, event))
|
|
9213
|
+
}));
|
|
9214
|
+
untrack(() => props.onReady?.(a));
|
|
9215
|
+
follow(() => ({ events: props.events, newest: props.newest, selected: props.selected }), (c) => {
|
|
9216
|
+
if (c.events || c.newest)
|
|
9217
|
+
a.update({ events: c.events, newest: c.newest });
|
|
9218
|
+
if (c.selected !== undefined && c.selected !== a.selected)
|
|
9219
|
+
a.select(c.selected);
|
|
9220
|
+
});
|
|
9221
|
+
onCleanup(() => a.destroy());
|
|
9222
|
+
return host;
|
|
9223
|
+
}
|
|
9224
|
+
function OptionPicker(props) {
|
|
9225
|
+
if (typeof document === "undefined")
|
|
9226
|
+
return null;
|
|
9227
|
+
const host = document.createElement("div");
|
|
9228
|
+
const owner = getOwner();
|
|
9229
|
+
const p = untrack(() => optionPicker(host, {
|
|
9230
|
+
...props,
|
|
9231
|
+
onSelect: (id, source) => props.onSelect?.(id, source),
|
|
9232
|
+
onChoose: (id) => props.onChoose?.(id),
|
|
9233
|
+
body: (option, element) => {
|
|
9234
|
+
const render = props.children;
|
|
9235
|
+
if (!render)
|
|
9236
|
+
return;
|
|
9237
|
+
return createRoot((dispose) => {
|
|
9238
|
+
insert(element, untrack(() => render(option)));
|
|
9239
|
+
return dispose;
|
|
9240
|
+
}, owner);
|
|
9241
|
+
}
|
|
9242
|
+
}));
|
|
9243
|
+
untrack(() => props.onReady?.(p));
|
|
9244
|
+
follow(() => ({ options: props.options, selected: props.selected, chosen: props.chosen }), (c) => {
|
|
9245
|
+
if ("options" in c)
|
|
9246
|
+
p.set(c.options);
|
|
9247
|
+
if (c.selected != null)
|
|
9248
|
+
p.select(c.selected);
|
|
9249
|
+
if ("chosen" in c)
|
|
9250
|
+
p.choose(c.chosen ?? null);
|
|
9251
|
+
});
|
|
9252
|
+
onCleanup(() => p.destroy());
|
|
9253
|
+
return host;
|
|
9254
|
+
}
|
|
6506
9255
|
export {
|
|
9256
|
+
Activity,
|
|
6507
9257
|
Bento,
|
|
9258
|
+
OptionPicker,
|
|
6508
9259
|
Split,
|
|
6509
9260
|
Timeline,
|
|
6510
9261
|
Tree
|