kuinetic 0.1.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (68) hide show
  1. package/LICENSE +21 -0
  2. package/README.md +99 -0
  3. package/dist/esm/chunk-LWS4OSLX.mjs +730 -0
  4. package/dist/esm/chunk-QZIJ7WZI.mjs +3312 -0
  5. package/dist/esm/chunk-R3TGDJKA.mjs +1264 -0
  6. package/dist/esm/core/index.mjs +34 -0
  7. package/dist/esm/effects/index.mjs +27 -0
  8. package/dist/esm/index.mjs +52 -0
  9. package/dist/kuinetic.all.js +5291 -0
  10. package/dist/kuinetic.css +2338 -0
  11. package/dist/kuinetic.js +5281 -0
  12. package/dist/types/core/activation.d.ts +21 -0
  13. package/dist/types/core/animator.d.ts +216 -0
  14. package/dist/types/core/attrs.d.ts +15 -0
  15. package/dist/types/core/capabilities.d.ts +19 -0
  16. package/dist/types/core/channels.d.ts +25 -0
  17. package/dist/types/core/compile.d.ts +49 -0
  18. package/dist/types/core/dom-watcher.d.ts +35 -0
  19. package/dist/types/core/effect-context.d.ts +49 -0
  20. package/dist/types/core/element-config.d.ts +52 -0
  21. package/dist/types/core/flip.d.ts +80 -0
  22. package/dist/types/core/gesture.d.ts +99 -0
  23. package/dist/types/core/index.d.ts +14 -0
  24. package/dist/types/core/instances.d.ts +98 -0
  25. package/dist/types/core/js-effect-preparer.d.ts +46 -0
  26. package/dist/types/core/js-params.d.ts +124 -0
  27. package/dist/types/core/owned-styles.d.ts +44 -0
  28. package/dist/types/core/params.d.ts +65 -0
  29. package/dist/types/core/parse.d.ts +22 -0
  30. package/dist/types/core/path-morph.d.ts +83 -0
  31. package/dist/types/core/play.d.ts +48 -0
  32. package/dist/types/core/registry.d.ts +38 -0
  33. package/dist/types/core/reporter.d.ts +35 -0
  34. package/dist/types/core/scroll-scheduler.d.ts +153 -0
  35. package/dist/types/core/spring.d.ts +83 -0
  36. package/dist/types/core/stagger.d.ts +19 -0
  37. package/dist/types/core/style-plan.d.ts +56 -0
  38. package/dist/types/core/types.d.ts +239 -0
  39. package/dist/types/effects/catalog/ambient.d.ts +18 -0
  40. package/dist/types/effects/catalog/core.d.ts +19 -0
  41. package/dist/types/effects/catalog/feedback.d.ts +21 -0
  42. package/dist/types/effects/catalog/index.d.ts +21 -0
  43. package/dist/types/effects/catalog/interaction-shared.d.ts +56 -0
  44. package/dist/types/effects/catalog/interaction.d.ts +22 -0
  45. package/dist/types/effects/catalog/media.d.ts +13 -0
  46. package/dist/types/effects/catalog/numbers-shared.d.ts +103 -0
  47. package/dist/types/effects/catalog/numbers.d.ts +31 -0
  48. package/dist/types/effects/catalog/shared.d.ts +3 -0
  49. package/dist/types/effects/catalog/text-shared.d.ts +180 -0
  50. package/dist/types/effects/catalog/text.d.ts +17 -0
  51. package/dist/types/effects/forms/index.d.ts +14 -0
  52. package/dist/types/effects/forms/primitives.d.ts +32 -0
  53. package/dist/types/effects/gestures/index.d.ts +19 -0
  54. package/dist/types/effects/gestures/primitives.d.ts +2 -0
  55. package/dist/types/effects/index.d.ts +19 -0
  56. package/dist/types/effects/layout/index.d.ts +12 -0
  57. package/dist/types/effects/layout/presets.d.ts +9 -0
  58. package/dist/types/effects/layout/primitives.d.ts +2 -0
  59. package/dist/types/effects/navigation/index.d.ts +30 -0
  60. package/dist/types/effects/scroll-mechanics/index.d.ts +14 -0
  61. package/dist/types/effects/scroll-mechanics/presets.d.ts +6 -0
  62. package/dist/types/effects/scroll-mechanics/primitives.d.ts +2 -0
  63. package/dist/types/effects/scroll-mechanics/tracker.d.ts +55 -0
  64. package/dist/types/effects/shared.d.ts +26 -0
  65. package/dist/types/effects/svg/index.d.ts +13 -0
  66. package/dist/types/effects/three-d/index.d.ts +20 -0
  67. package/dist/types/index.d.ts +16 -0
  68. package/package.json +78 -0
@@ -0,0 +1,730 @@
1
+ // src/core/registry.ts
2
+ var Registry = class {
3
+ primitives = /* @__PURE__ */ new Map();
4
+ presets = /* @__PURE__ */ new Map();
5
+ /** Sorted-name key → preset that renders that combination as one tested keyframe. */
6
+ combos = /* @__PURE__ */ new Map();
7
+ registerPrimitive(primitive) {
8
+ if (this.primitives.has(primitive.id)) {
9
+ throw new Error(`kuinetic: primitive "${primitive.id}" is already registered`);
10
+ }
11
+ this.primitives.set(primitive.id, namespaceTiming(primitive));
12
+ return this;
13
+ }
14
+ registerPreset(preset) {
15
+ if (this.presets.has(preset.name)) {
16
+ throw new Error(`kuinetic: effect "${preset.name}" is already registered`);
17
+ }
18
+ if (!this.primitives.has(preset.primitive)) {
19
+ throw new Error(
20
+ `kuinetic: effect "${preset.name}" references unknown primitive "${preset.primitive}"`
21
+ );
22
+ }
23
+ this.presets.set(preset.name, preset);
24
+ return this;
25
+ }
26
+ registerPresets(presets) {
27
+ for (const preset of presets) this.registerPreset(preset);
28
+ return this;
29
+ }
30
+ registerPrimitives(primitives) {
31
+ for (const primitive of primitives) this.registerPrimitive(primitive);
32
+ return this;
33
+ }
34
+ /**
35
+ * Declare that a set of effect names has a purpose-built single-keyframe implementation.
36
+ * Checked before channel conflict analysis, so `fade-up` + `blur-in` can resolve to the
37
+ * tested `fade-blur-up` rather than being rejected for both writing `opacity`.
38
+ */
39
+ registerCombo(names, presetName) {
40
+ this.combos.set(comboKey(names), presetName);
41
+ return this;
42
+ }
43
+ resolve(name) {
44
+ const preset = this.presets.get(name);
45
+ if (!preset) return void 0;
46
+ const primitive = this.primitives.get(preset.primitive);
47
+ return { preset, primitive };
48
+ }
49
+ findCombo(names) {
50
+ const comboName = this.combos.get(comboKey(names));
51
+ return comboName ? this.resolve(comboName) : void 0;
52
+ }
53
+ has(name) {
54
+ return this.presets.has(name);
55
+ }
56
+ /** All registered effect names, for docs generation and dev-mode "did you mean" hints. */
57
+ names() {
58
+ return [...this.presets.keys()].sort((a, b) => a.localeCompare(b));
59
+ }
60
+ getPrimitive(id) {
61
+ return this.primitives.get(id);
62
+ }
63
+ };
64
+ var TIMING_PARAMS = ["duration", "delay", "ease"];
65
+ function timingProperty(primitiveId, name) {
66
+ return `--kui-${primitiveId}-${name}`;
67
+ }
68
+ function namespaceTiming(primitive) {
69
+ const parameters = { ...primitive.parameters };
70
+ for (const name of TIMING_PARAMS) {
71
+ const spec = parameters[name];
72
+ if (spec) parameters[name] = { ...spec, cssProperty: timingProperty(primitive.id, name) };
73
+ }
74
+ return { ...primitive, parameters };
75
+ }
76
+ function comboKey(names) {
77
+ return [...names].sort((a, b) => a.localeCompare(b)).join("+");
78
+ }
79
+ function suggest(name, candidates) {
80
+ let best;
81
+ let bestScore = Infinity;
82
+ for (const candidate of candidates) {
83
+ const score = distance(name, candidate);
84
+ if (score < bestScore) {
85
+ bestScore = score;
86
+ best = candidate;
87
+ }
88
+ }
89
+ return bestScore <= Math.max(2, Math.floor(name.length / 3)) ? best : void 0;
90
+ }
91
+ function distance(a, b) {
92
+ const rows = a.length + 1;
93
+ const cols = b.length + 1;
94
+ let prev = Array.from({ length: cols }, (_, i) => i);
95
+ for (let i = 1; i < rows; i++) {
96
+ const curr = [i, ...Array(cols - 1).fill(0)];
97
+ for (let j = 1; j < cols; j++) {
98
+ const cost = a[i - 1] === b[j - 1] ? 0 : 1;
99
+ curr[j] = Math.min(curr[j - 1] + 1, prev[j] + 1, prev[j - 1] + cost);
100
+ }
101
+ prev = curr;
102
+ }
103
+ return prev[cols - 1];
104
+ }
105
+
106
+ // src/core/types.ts
107
+ var CHANNEL = {
108
+ opacity: "opacity",
109
+ translate: "translate",
110
+ scale: "scale",
111
+ rotate: "rotate",
112
+ filter: "filter",
113
+ clip: "clip",
114
+ background: "background",
115
+ color: "color",
116
+ stroke: "stroke",
117
+ text: "text"
118
+ };
119
+ function inertInstance(destroy = () => {
120
+ }) {
121
+ return {
122
+ activate() {
123
+ },
124
+ cancel() {
125
+ },
126
+ finish() {
127
+ },
128
+ finished: Promise.resolve(),
129
+ destroy
130
+ };
131
+ }
132
+
133
+ // src/core/params.ts
134
+ var DANGEROUS = /[;<]|\/\*|url\s*\(|expression\s*\(|@import|image-set\s*\(/i;
135
+ var ABSOLUTE_OR_PROTOCOL_RELATIVE = /^(?:[a-z][a-z0-9+.-]*:|[/\\]{2})/i;
136
+ var MAX_VALUE_LENGTH = 200;
137
+ var NUM = String.raw`-?(?:\d+(?:\.\d+)?|\.\d+)`;
138
+ var LENGTH_UNITS = "px|rem|em|vh|vw|vmin|vmax|ch|ex|cm|mm|in|pt|pc|q|%";
139
+ var PATTERNS = {
140
+ length: new RegExp(`^(?:0|${NUM}(?:${LENGTH_UNITS}))$`, "i"),
141
+ time: new RegExp(`^${NUM}(?:ms|s)$`, "i"),
142
+ number: new RegExp(`^${NUM}$`),
143
+ percentage: new RegExp(`^${NUM}%$`),
144
+ angle: new RegExp(`^${NUM}(?:deg|rad|turn|grad)$`, "i")
145
+ };
146
+ var HEX_COLOR = /^#[0-9a-f]{3,8}$/i;
147
+ var COLOR_FUNCTIONS = /^(?:rgba?|hsla?|okl(?:ch|ab)|l(?:ch|ab)|color)\([^()]*\)$/i;
148
+ var COLOR_KEYWORD = /^[a-z]+$/i;
149
+ var EASING_KEYWORD = /^(?:linear|ease|step-start|step-end|spring|[a-z]+-(?:in|out|in-out))$/i;
150
+ var EASING_FUNCTION = /^(?:cubic-bezier|steps|linear)\([^()]*\)$/i;
151
+ var CALC_TYPES = /* @__PURE__ */ new Set(["length", "percentage", "number"]);
152
+ var CALC_CHARACTER = /^[\d.\s+\-*/%a-z,]$/i;
153
+ var CUSTOM_PROPERTY_NAME = /^--[\w-]+$/;
154
+ function validate(raw, spec) {
155
+ const value = raw.trim();
156
+ const rejection = screen(value, spec);
157
+ if (rejection) return rejection;
158
+ if (spec.type === "keyword") return checkKeyword(value, spec);
159
+ if (spec.type === "text") return { value, ok: true };
160
+ if (isAcceptable(value, spec.type)) return checkNumericConstraints(value, spec);
161
+ return reject(spec, `not a valid ${spec.type}`);
162
+ }
163
+ function screen(value, spec) {
164
+ if (!value) return reject(spec, "empty value");
165
+ if (value.length > MAX_VALUE_LENGTH) {
166
+ return reject(spec, `value exceeds ${MAX_VALUE_LENGTH} characters`);
167
+ }
168
+ if (DANGEROUS.test(value)) return reject(spec, "value contains disallowed CSS syntax");
169
+ return null;
170
+ }
171
+ function isAcceptable(value, type) {
172
+ if (type === "color") return isColor(value);
173
+ if (type === "easing") return EASING_KEYWORD.test(value) || EASING_FUNCTION.test(value);
174
+ const pattern = PATTERNS[type];
175
+ if (!pattern) return false;
176
+ if (pattern.test(value)) return true;
177
+ return CALC_TYPES.has(type) && isSafeCalc(value) && isWellFormedCalc(value);
178
+ }
179
+ function isColor(value) {
180
+ return HEX_COLOR.test(value) || COLOR_FUNCTIONS.test(value) || COLOR_KEYWORD.test(value);
181
+ }
182
+ function checkKeyword(value, spec) {
183
+ if (spec.values?.includes(value)) return { value, ok: true };
184
+ return reject(spec, `expected one of ${spec.values?.join(", ") ?? "(none declared)"}`);
185
+ }
186
+ function isSameOriginPath(value) {
187
+ return !ABSOLUTE_OR_PROTOCOL_RELATIVE.test(value);
188
+ }
189
+ function checkNumericConstraints(value, spec) {
190
+ if (spec.type !== "number") return { value, ok: true };
191
+ const numeric = Number(value);
192
+ if (spec.finite && !Number.isFinite(numeric)) return reject(spec, "expected a finite number");
193
+ if (spec.integer && !Number.isInteger(numeric)) return reject(spec, "expected an integer");
194
+ if (spec.minimum !== void 0 && numeric < spec.minimum) {
195
+ return reject(spec, `expected at least ${spec.minimum}`);
196
+ }
197
+ if (spec.maximum !== void 0 && numeric > spec.maximum) {
198
+ return reject(spec, `expected at most ${spec.maximum}`);
199
+ }
200
+ return { value, ok: true };
201
+ }
202
+ function reject(spec, reason) {
203
+ return { value: spec.default, ok: false, reason };
204
+ }
205
+ function isSafeCalc(value) {
206
+ if (!value.startsWith("calc(") || !value.endsWith(")")) return false;
207
+ const end = value.length - 1;
208
+ let index = "calc(".length;
209
+ while (index < end) {
210
+ index = nextCalcToken(value, index, end);
211
+ if (index < 0) return false;
212
+ }
213
+ return true;
214
+ }
215
+ function nextCalcToken(value, index, end) {
216
+ if (value.startsWith("var(", index)) return consumeVar(value, index, end);
217
+ return CALC_CHARACTER.test(value[index]) ? index + 1 : -1;
218
+ }
219
+ function consumeVar(value, start, end) {
220
+ const close = value.indexOf(")", start + "var(".length);
221
+ if (close < 0 || close >= end) return -1;
222
+ const name = value.slice(start + "var(".length, close);
223
+ return CUSTOM_PROPERTY_NAME.test(name) ? close + 1 : -1;
224
+ }
225
+ function isWellFormedCalc(value) {
226
+ const body = value.slice("calc(".length, -1).trim();
227
+ return body !== "" && !/[+\-*/]$/.test(body);
228
+ }
229
+ function resolveParams(authored, schema, warn) {
230
+ const out = {};
231
+ for (const [key, raw] of Object.entries(authored)) {
232
+ const spec = Object.hasOwn(schema, key) ? schema[key] : void 0;
233
+ if (!spec) {
234
+ warn(`unknown parameter "${key}" (known: ${Object.keys(schema).join(", ") || "none"})`);
235
+ continue;
236
+ }
237
+ if (spec.type === "text") continue;
238
+ const result = validate(raw, spec);
239
+ if (result.ok) out[spec.cssProperty] = result.value;
240
+ else warn(`parameter "${key}": ${result.reason} \u2014 got "${raw}", using default "${spec.default}"`);
241
+ }
242
+ return out;
243
+ }
244
+
245
+ // src/core/js-params.ts
246
+ var ABSOLUTE_BASIS = {
247
+ viewportWidth: 0,
248
+ viewportHeight: 0,
249
+ percentBasis: 0,
250
+ fontSize: 16,
251
+ rootFontSize: 16
252
+ };
253
+ var STATIC_UNITS = {
254
+ px: 1,
255
+ cm: 96 / 2.54,
256
+ mm: 96 / 25.4,
257
+ q: 96 / 101.6,
258
+ in: 96,
259
+ pt: 96 / 72,
260
+ pc: 16
261
+ };
262
+ var BASIS_UNITS = {
263
+ vh: (b) => b.viewportHeight / 100,
264
+ vw: (b) => b.viewportWidth / 100,
265
+ vmin: (b) => Math.min(b.viewportWidth, b.viewportHeight) / 100,
266
+ vmax: (b) => Math.max(b.viewportWidth, b.viewportHeight) / 100,
267
+ "%": (b) => b.percentBasis / 100,
268
+ em: (b) => b.fontSize,
269
+ rem: (b) => b.rootFontSize,
270
+ ch: (b) => b.fontSize * 0.5,
271
+ ex: (b) => b.fontSize * 0.5
272
+ };
273
+ var NUMBER_WITH_UNIT = /^(-?(?:\d+(?:\.\d+)?|\.\d+))([a-z%]*)$/i;
274
+ function readParams(authored, schema, warn) {
275
+ const out = {};
276
+ for (const [name, spec] of Object.entries(schema)) out[name] = spec.default;
277
+ for (const [name, raw] of Object.entries(authored)) {
278
+ const spec = Object.hasOwn(schema, name) ? schema[name] : void 0;
279
+ if (!spec) {
280
+ warn(`unknown parameter "${name}" (known: ${Object.keys(schema).join(", ") || "none"})`);
281
+ continue;
282
+ }
283
+ const result = validate(raw, spec);
284
+ out[name] = result.value;
285
+ if (!result.ok) {
286
+ warn(`parameter "${name}": ${result.reason} \u2014 got "${raw}", using default "${spec.default}"`);
287
+ }
288
+ }
289
+ return out;
290
+ }
291
+ function toMilliseconds(value, fallback = 0) {
292
+ const parts = NUMBER_WITH_UNIT.exec(value.trim());
293
+ if (!parts) return fallback;
294
+ const amount = Number.parseFloat(parts[1]);
295
+ const unit = parts[2].toLowerCase();
296
+ if (unit === "ms") return amount;
297
+ if (unit === "s") return amount * 1e3;
298
+ return fallback;
299
+ }
300
+ function toPixels(value, basis, fallback = 0) {
301
+ const parts = NUMBER_WITH_UNIT.exec(value.trim());
302
+ if (!parts) return fallback;
303
+ const amount = Number.parseFloat(parts[1]);
304
+ const unit = parts[2].toLowerCase();
305
+ if (unit === "") return amount === 0 ? 0 : fallback;
306
+ const staticFactor = STATIC_UNITS[unit];
307
+ if (staticFactor !== void 0) return amount * staticFactor;
308
+ const basisFactor = BASIS_UNITS[unit];
309
+ return basisFactor ? amount * basisFactor(basis) : fallback;
310
+ }
311
+ function toNumber(value, fallback = 0) {
312
+ const parts = NUMBER_WITH_UNIT.exec(value.trim());
313
+ if (!parts) return fallback;
314
+ const amount = Number.parseFloat(parts[1]);
315
+ const unit = parts[2];
316
+ if (unit === "") return amount;
317
+ if (unit === "%") return amount / 100;
318
+ return fallback;
319
+ }
320
+ var EASING_SPEC = { type: "easing", default: "", cssProperty: "--kui-ease" };
321
+ function timingMs(raw, label, warn) {
322
+ if (raw === void 0) return void 0;
323
+ const ms = toMilliseconds(raw, Number.NaN);
324
+ if (Number.isFinite(ms)) return ms;
325
+ warn(`${label} "${raw}" is not a valid time \u2014 ignored`);
326
+ return void 0;
327
+ }
328
+ function timingEasing(raw, warn) {
329
+ if (raw === void 0) return void 0;
330
+ const result = validate(raw, EASING_SPEC);
331
+ if (result.ok) return result.value;
332
+ warn(`easing "${raw}": ${result.reason} \u2014 ignored`);
333
+ return void 0;
334
+ }
335
+ function readEffectTiming(spec, warn) {
336
+ return {
337
+ durationMs: timingMs(spec.duration, "duration", warn),
338
+ delayMs: timingMs(spec.delay, "delay", warn),
339
+ easing: timingEasing(spec.easing, warn)
340
+ };
341
+ }
342
+ function effectDurationMs(params, fallback) {
343
+ return params.timing.durationMs ?? params.ms("duration", fallback);
344
+ }
345
+ function createParams(values, timing = {}) {
346
+ return {
347
+ text: (name, fallback = "") => values[name] ?? fallback,
348
+ ms: (name, fallback = 0) => toMilliseconds(values[name] ?? "", fallback),
349
+ num: (name, fallback = 0) => toNumber(values[name] ?? "", fallback),
350
+ is: (name, value = "true") => (values[name] ?? "") === value,
351
+ timing
352
+ };
353
+ }
354
+ function readEffectParams(authored, schema, warn, timing = {}) {
355
+ return createParams(readParams(authored, schema, warn), timing);
356
+ }
357
+
358
+ // src/core/scroll-scheduler.ts
359
+ function defaultDeps() {
360
+ const raf = globalThis.requestAnimationFrame;
361
+ if (typeof raf !== "function") {
362
+ return {
363
+ requestFrame: (callback) => globalThis.setTimeout(callback, 16),
364
+ cancelFrame: (handle) => globalThis.clearTimeout(handle)
365
+ };
366
+ }
367
+ return {
368
+ requestFrame: (callback) => raf(callback),
369
+ cancelFrame: (handle) => globalThis.cancelAnimationFrame(handle)
370
+ };
371
+ }
372
+ function createScrollScheduler(deps = defaultDeps()) {
373
+ const entries = /* @__PURE__ */ new Map();
374
+ let epoch = 0;
375
+ let pending = null;
376
+ function schedule(entry) {
377
+ if (entry) entry.dirty = true;
378
+ else for (const other of entries.values()) other.dirty = true;
379
+ if (pending !== null) return;
380
+ pending = deps.requestFrame(runFrame);
381
+ }
382
+ function runFrame() {
383
+ pending = null;
384
+ for (const entry of entries.values()) {
385
+ if (!entry.dirty || entry.subscribers.size === 0) continue;
386
+ entry.dirty = false;
387
+ const frame = { metrics: entry.root.metrics(), epoch };
388
+ for (const subscriber of [...entry.subscribers]) subscriber(frame);
389
+ }
390
+ }
391
+ function attach(root) {
392
+ const existing = entries.get(root.key);
393
+ if (existing) return existing;
394
+ const entry = { root, subscribers: /* @__PURE__ */ new Set(), detach: [], dirty: false };
395
+ entry.detach.push(root.onScroll(() => schedule(entry)));
396
+ entry.detach.push(
397
+ root.onResize(() => {
398
+ epoch++;
399
+ schedule();
400
+ })
401
+ );
402
+ entries.set(root.key, entry);
403
+ return entry;
404
+ }
405
+ function release(entry) {
406
+ if (entry.subscribers.size > 0) return;
407
+ for (const detach of entry.detach) detach();
408
+ entries.delete(entry.root.key);
409
+ }
410
+ return {
411
+ subscribe(root, onFrame) {
412
+ const entry = attach(root);
413
+ entry.subscribers.add(onFrame);
414
+ schedule(entry);
415
+ return () => {
416
+ entry.subscribers.delete(onFrame);
417
+ release(entry);
418
+ };
419
+ },
420
+ invalidate() {
421
+ epoch++;
422
+ schedule();
423
+ },
424
+ rootCount: () => entries.size,
425
+ destroy() {
426
+ if (pending !== null) deps.cancelFrame(pending);
427
+ pending = null;
428
+ for (const entry of entries.values()) {
429
+ entry.subscribers.clear();
430
+ for (const detach of entry.detach) detach();
431
+ }
432
+ entries.clear();
433
+ }
434
+ };
435
+ }
436
+ function windowScrollRoot(win) {
437
+ return {
438
+ key: "window",
439
+ metrics: () => ({
440
+ scrollTop: win.scrollY,
441
+ scrollLeft: win.scrollX,
442
+ viewportWidth: win.innerWidth,
443
+ viewportHeight: win.innerHeight,
444
+ viewportTop: 0,
445
+ viewportLeft: 0
446
+ }),
447
+ onScroll: (handler) => listen(win, "scroll", handler),
448
+ onResize: (handler) => listen(win, "resize", handler)
449
+ };
450
+ }
451
+ function elementScrollRoot(el, win) {
452
+ return {
453
+ key: `el:${rootId(el)}`,
454
+ metrics: () => {
455
+ const rect = el.getBoundingClientRect();
456
+ return {
457
+ scrollTop: el.scrollTop,
458
+ scrollLeft: el.scrollLeft,
459
+ viewportWidth: el.clientWidth,
460
+ viewportHeight: el.clientHeight,
461
+ viewportTop: rect.top,
462
+ viewportLeft: rect.left
463
+ };
464
+ },
465
+ onScroll: (handler) => listen(el, "scroll", handler),
466
+ // Window resize alone misses the cases that actually invalidate a nested scroller: images
467
+ // loading, fonts swapping, a container changing size, or content being inserted.
468
+ onResize: (handler) => observeSize(el, win, handler)
469
+ };
470
+ }
471
+ function listen(target, type, handler) {
472
+ target.addEventListener(type, handler, { passive: true });
473
+ return () => target.removeEventListener(type, handler);
474
+ }
475
+ function observeSize(el, win, handler) {
476
+ const stopWindow = listen(win, "resize", handler);
477
+ if (typeof ResizeObserver === "undefined") return stopWindow;
478
+ const observer = new ResizeObserver(handler);
479
+ observer.observe(el);
480
+ return () => {
481
+ stopWindow();
482
+ observer.disconnect();
483
+ };
484
+ }
485
+ var nextRootId = 0;
486
+ var rootIds = /* @__PURE__ */ new WeakMap();
487
+ function rootId(el) {
488
+ const existing = rootIds.get(el);
489
+ if (existing) return existing;
490
+ const id = String(++nextRootId);
491
+ rootIds.set(el, id);
492
+ return id;
493
+ }
494
+ function createRootResolver(options) {
495
+ const { win } = options;
496
+ const isScrollable = options.isScrollable ?? defaultIsScrollable(win);
497
+ const windowRoot = windowScrollRoot(win);
498
+ return (el) => {
499
+ for (let node = el.parentElement; node; node = node.parentElement) {
500
+ if (isScrollable(node)) return elementScrollRoot(node, win);
501
+ }
502
+ return windowRoot;
503
+ };
504
+ }
505
+ var SCROLLABLE_OVERFLOW = /* @__PURE__ */ new Set(["auto", "scroll", "overlay"]);
506
+ function defaultIsScrollable(win) {
507
+ return (el) => {
508
+ const style = win.getComputedStyle(el);
509
+ const vertical = SCROLLABLE_OVERFLOW.has(style.overflowY) && el.scrollHeight > el.clientHeight;
510
+ const horizontal = SCROLLABLE_OVERFLOW.has(style.overflowX) && el.scrollWidth > el.clientWidth;
511
+ return vertical || horizontal;
512
+ };
513
+ }
514
+ function clamp01(value) {
515
+ if (Number.isNaN(value)) return 0;
516
+ return Math.min(1, Math.max(0, value));
517
+ }
518
+ function createMeasureCache(measure) {
519
+ let cachedEpoch = -1;
520
+ let cached;
521
+ return {
522
+ read(epoch) {
523
+ if (cachedEpoch !== epoch || cached === void 0) {
524
+ cached = measure();
525
+ cachedEpoch = epoch;
526
+ }
527
+ return cached;
528
+ },
529
+ clear() {
530
+ cachedEpoch = -1;
531
+ cached = void 0;
532
+ }
533
+ };
534
+ }
535
+
536
+ // src/core/instances.ts
537
+ function animationsOf(el) {
538
+ const getAnimations = el.getAnimations;
539
+ return typeof getAnimations === "function" ? getAnimations.call(el) : [];
540
+ }
541
+ function ownedAnimationsOf(el, names) {
542
+ return animationsOf(el).filter((animation) => {
543
+ const name = animation.animationName;
544
+ return typeof name === "string" && names.has(name);
545
+ });
546
+ }
547
+ function flushComputedAnimationName(el) {
548
+ return getComputedStyle(el).animationName;
549
+ }
550
+ function restartCssAnimation(el, ledger) {
551
+ const name = el.style.getPropertyValue("animation-name");
552
+ ledger.set("animation-name", "none");
553
+ flushComputedAnimationName(el);
554
+ ledger.set("animation-name", name);
555
+ }
556
+ function createCssInstance(el, ledger, animationNames) {
557
+ const ownedNames = new Set(animationNames);
558
+ let settle;
559
+ let finished = new Promise((resolve) => {
560
+ settle = resolve;
561
+ });
562
+ let activatedBefore = false;
563
+ function watch(animations) {
564
+ if (animations.length === 0) {
565
+ settle?.();
566
+ return;
567
+ }
568
+ void Promise.all(animations.map((a) => a.finished.catch(() => void 0))).then(() => settle?.());
569
+ }
570
+ return {
571
+ activate() {
572
+ finished = new Promise((resolve) => {
573
+ settle = resolve;
574
+ });
575
+ let animations = ownedAnimationsOf(el, ownedNames);
576
+ if (!activatedBefore) {
577
+ restartCssAnimation(el, ledger);
578
+ ledger.set("animation-play-state", "running");
579
+ animations = ownedAnimationsOf(el, ownedNames);
580
+ } else {
581
+ const stale = animations.filter((a) => a.playState === "finished");
582
+ if (stale.length > 0) {
583
+ for (const animation of stale) animation.reverse();
584
+ } else {
585
+ ledger.set("animation-play-state", "running");
586
+ }
587
+ }
588
+ activatedBefore = true;
589
+ watch(animations);
590
+ },
591
+ cancel() {
592
+ for (const animation of ownedAnimationsOf(el, ownedNames)) animation.cancel();
593
+ settle?.();
594
+ },
595
+ finish() {
596
+ for (const animation of ownedAnimationsOf(el, ownedNames)) animation.finish();
597
+ settle?.();
598
+ },
599
+ get finished() {
600
+ return finished;
601
+ },
602
+ destroy() {
603
+ settle?.();
604
+ }
605
+ };
606
+ }
607
+ function cleanupOf(result) {
608
+ if (result === void 0) return void 0;
609
+ return typeof result === "function" ? result : result.cleanup;
610
+ }
611
+ function deferredInstance(setup) {
612
+ let running;
613
+ let settle;
614
+ let finished = Promise.resolve();
615
+ const teardown = () => {
616
+ cleanupOf(running)?.();
617
+ running = void 0;
618
+ settle?.();
619
+ };
620
+ return createJsInstance({
621
+ activate() {
622
+ running = setup();
623
+ if (typeof running === "function") return;
624
+ const work = running.finished;
625
+ finished = new Promise((resolve) => {
626
+ settle = resolve;
627
+ });
628
+ void work.then(() => settle?.());
629
+ },
630
+ cancel: teardown,
631
+ finish() {
632
+ if (typeof running === "object") running.finish?.();
633
+ settle?.();
634
+ },
635
+ destroy: teardown,
636
+ finished: () => finished
637
+ });
638
+ }
639
+ function deferPrepare(setup) {
640
+ return (...args) => deferredInstance(() => setup(...args));
641
+ }
642
+ function createJsInstance(hooks) {
643
+ let active = false;
644
+ return {
645
+ activate() {
646
+ if (active) return;
647
+ hooks.activate();
648
+ active = true;
649
+ },
650
+ cancel() {
651
+ hooks.cancel?.();
652
+ active = false;
653
+ },
654
+ finish() {
655
+ hooks.finish?.();
656
+ },
657
+ get finished() {
658
+ return hooks.finished?.() ?? Promise.resolve();
659
+ },
660
+ destroy() {
661
+ active = false;
662
+ hooks.destroy();
663
+ }
664
+ };
665
+ }
666
+
667
+ // src/core/owned-styles.ts
668
+ function createStyleLedger(el) {
669
+ const style = el.style;
670
+ const previous = /* @__PURE__ */ new Map();
671
+ function remember(property) {
672
+ if (previous.has(property)) return;
673
+ const existing = style.getPropertyValue(property);
674
+ previous.set(property, existing === "" ? void 0 : existing);
675
+ }
676
+ return {
677
+ set(property, value) {
678
+ remember(property);
679
+ style.setProperty(property, value);
680
+ },
681
+ claim: remember,
682
+ restore() {
683
+ for (const [property, value] of previous) {
684
+ if (value === void 0) style.removeProperty(property);
685
+ else style.setProperty(property, value);
686
+ }
687
+ previous.clear();
688
+ },
689
+ owned: () => [...previous.keys()]
690
+ };
691
+ }
692
+ function createAttributeLedger(el) {
693
+ const previous = /* @__PURE__ */ new Map();
694
+ return {
695
+ set(name, value) {
696
+ if (!previous.has(name)) previous.set(name, el.getAttribute(name));
697
+ el.setAttribute(name, value);
698
+ },
699
+ restore() {
700
+ for (const [name, value] of previous) {
701
+ if (value === null) el.removeAttribute(name);
702
+ else el.setAttribute(name, value);
703
+ }
704
+ previous.clear();
705
+ }
706
+ };
707
+ }
708
+
709
+ export {
710
+ isSameOriginPath,
711
+ resolveParams,
712
+ Registry,
713
+ timingProperty,
714
+ suggest,
715
+ ABSOLUTE_BASIS,
716
+ toPixels,
717
+ readEffectTiming,
718
+ effectDurationMs,
719
+ readEffectParams,
720
+ createScrollScheduler,
721
+ createRootResolver,
722
+ clamp01,
723
+ createMeasureCache,
724
+ createCssInstance,
725
+ deferPrepare,
726
+ createStyleLedger,
727
+ createAttributeLedger,
728
+ CHANNEL,
729
+ inertInstance
730
+ };