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