@hyperframes/inspector 0.0.0-stage → 0.8.152

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 (74) hide show
  1. package/LICENSE +190 -0
  2. package/README.md +104 -2
  3. package/dist/inspection.js +2007 -0
  4. package/dist/live.js +1288 -0
  5. package/dist/service.js +63122 -0
  6. package/dist/types/core/src/audioAutomation.d.ts +202 -0
  7. package/dist/types/core/src/audioFx.d.ts +181 -0
  8. package/dist/types/core/src/audioGain.d.ts +44 -0
  9. package/dist/types/core/src/colorGrading.d.ts +1959 -0
  10. package/dist/types/core/src/colorGradingCurves.d.ts +8 -0
  11. package/dist/types/core/src/colorLuts.d.ts +24 -0
  12. package/dist/types/core/src/compiler/assetPaths.d.ts +1 -0
  13. package/dist/types/core/src/compiler/compositionAssembly.d.ts +144 -0
  14. package/dist/types/core/src/compiler/compositionScoping.d.ts +28 -0
  15. package/dist/types/core/src/compiler/externalScripts.d.ts +8 -0
  16. package/dist/types/core/src/compiler/htmlBundler.d.ts +74 -0
  17. package/dist/types/core/src/compiler/htmlCompiler.d.ts +18 -0
  18. package/dist/types/core/src/compiler/htmlDocument.d.ts +23 -0
  19. package/dist/types/core/src/compiler/importMaps.d.ts +18 -0
  20. package/dist/types/core/src/compiler/inlineSubCompositions.d.ts +151 -0
  21. package/dist/types/core/src/compiler/rewriteSubCompPaths.d.ts +1 -0
  22. package/dist/types/core/src/compiler/scriptRuns.d.ts +31 -0
  23. package/dist/types/core/src/compiler/selectorIdTokens.d.ts +70 -0
  24. package/dist/types/core/src/compiler/standaloneEntry.d.ts +2 -0
  25. package/dist/types/core/src/compiler/staticGuard.d.ts +7 -0
  26. package/dist/types/core/src/compiler/svgIdNamespacing.d.ts +80 -0
  27. package/dist/types/core/src/compiler/svgSelectorAliases.d.ts +30 -0
  28. package/dist/types/core/src/compiler/timingCompiler.d.ts +89 -0
  29. package/dist/types/core/src/generated/runtime-inline.d.ts +6 -0
  30. package/dist/types/core/src/generated/svg-selector-aliases-inline.d.ts +2 -0
  31. package/dist/types/core/src/inline-scripts/pickerApi.d.ts +32 -0
  32. package/dist/types/core/src/inline-scripts/runtimeContract.d.ts +13 -0
  33. package/dist/types/core/src/mediaTiming.d.ts +41 -0
  34. package/dist/types/core/src/playbackRateBounds.d.ts +1 -0
  35. package/dist/types/core/src/runtime/analytics.d.ts +18 -0
  36. package/dist/types/core/src/runtime/applyVariableBindings.d.ts +47 -0
  37. package/dist/types/core/src/runtime/audioAutomationVolume.d.ts +19 -0
  38. package/dist/types/core/src/runtime/authoredTiming.d.ts +16 -0
  39. package/dist/types/core/src/runtime/compositionDimension.d.ts +5 -0
  40. package/dist/types/core/src/runtime/diagnostics.d.ts +3 -0
  41. package/dist/types/core/src/runtime/domRealm.d.ts +67 -0
  42. package/dist/types/core/src/runtime/flattenedRoot.d.ts +9 -0
  43. package/dist/types/core/src/runtime/getVariables.d.ts +42 -0
  44. package/dist/types/core/src/runtime/globals.d.ts +11 -0
  45. package/dist/types/core/src/runtime/inspectionTypes.d.ts +68 -0
  46. package/dist/types/core/src/runtime/playbackRate.d.ts +26 -0
  47. package/dist/types/core/src/runtime/protocol.d.ts +30 -0
  48. package/dist/types/core/src/runtime/proxySrc.d.ts +3 -0
  49. package/dist/types/core/src/runtime/startExpression.d.ts +14 -0
  50. package/dist/types/core/src/runtime/startResolver.d.ts +21 -0
  51. package/dist/types/core/src/runtime/timelineDuration.d.ts +3 -0
  52. package/dist/types/core/src/runtime/timelineInspection.d.ts +46 -0
  53. package/dist/types/core/src/runtime/timelineValues.d.ts +15 -0
  54. package/dist/types/core/src/runtime/types.d.ts +335 -0
  55. package/dist/types/core/src/runtime/variableScope.d.ts +8 -0
  56. package/dist/types/core/src/safePath.d.ts +49 -0
  57. package/dist/types/core/src/sceneParts.d.ts +9 -0
  58. package/dist/types/core/src/speedRamp.d.ts +59 -0
  59. package/dist/types/core/src/tokenSlug.d.ts +12 -0
  60. package/dist/types/inspector/src/elementMap.d.ts +33 -0
  61. package/dist/types/inspector/src/engine.d.ts +35 -0
  62. package/dist/types/inspector/src/live.d.ts +18 -0
  63. package/dist/types/inspector/src/mapInspection.d.ts +76 -0
  64. package/dist/types/inspector/src/outputSchemas.d.ts +12 -0
  65. package/dist/types/inspector/src/pageQueries.d.ts +21 -0
  66. package/dist/types/inspector/src/pageServer.d.ts +2 -0
  67. package/dist/types/inspector/src/requests.d.ts +5 -0
  68. package/dist/types/inspector/src/service.d.ts +98 -0
  69. package/dist/types/inspector/src/sourceDependencies.d.ts +5 -0
  70. package/dist/types/inspector/src/sources.d.ts +49 -0
  71. package/dist/types/inspector/src/timelineReport.d.ts +28 -0
  72. package/dist/types/inspector/src/tools.d.ts +18 -0
  73. package/dist/types/inspector/src/types.d.ts +1 -0
  74. package/package.json +54 -3
@@ -0,0 +1,2007 @@
1
+ "use strict";
2
+ (() => {
3
+ // ../core/src/runtime/timelineValues.ts
4
+ var unknown = () => ({ kind: "unknown", value: null });
5
+ function declared(value) {
6
+ if (typeof value === "number" && Number.isFinite(value)) return { kind: "literal", value };
7
+ if (typeof value === "string") {
8
+ const kind = /^[+\-*/]=/.test(value) ? "relative" : value.includes("random(") ? "expression" : "literal";
9
+ return { kind, value };
10
+ }
11
+ return unknown();
12
+ }
13
+ function animationValues(vars, properties) {
14
+ const startAt = vars.startAt && typeof vars.startAt === "object" ? vars.startAt : {};
15
+ return {
16
+ basis: "authored-tween",
17
+ properties: properties.map((property) => ({
18
+ property,
19
+ from: declared(vars.runBackwards ? vars[property] : startAt[property]),
20
+ to: vars.runBackwards ? unknown() : declared(vars[property])
21
+ })),
22
+ ease: declared(vars.ease)
23
+ };
24
+ }
25
+
26
+ // ../core/src/runtime/globals.ts
27
+ function getDebugSurface() {
28
+ return globalThis;
29
+ }
30
+
31
+ // ../core/src/runtime/diagnostics.ts
32
+ function swallow(label, error) {
33
+ if (typeof window === "undefined") return;
34
+ const w = getDebugSurface();
35
+ const handler = w.__hf?.onSwallowed;
36
+ if (handler) {
37
+ try {
38
+ handler({ label, error });
39
+ } catch (handlerError) {
40
+ void handlerError;
41
+ }
42
+ }
43
+ if (w.__hfDebug || w.__HYPERFRAMES_DEBUG) {
44
+ console.debug(`[hyperframes] ${label} swallowed:`, error);
45
+ }
46
+ }
47
+
48
+ // ../core/src/runtime/timelineDuration.ts
49
+ function readTimelineDurationSeconds(timeline) {
50
+ try {
51
+ if (typeof timeline?.duration !== "function") return null;
52
+ const seconds = Number(timeline.duration());
53
+ return Number.isFinite(seconds) ? seconds : null;
54
+ } catch (err) {
55
+ swallow("runtime.timelineDuration", err);
56
+ return null;
57
+ }
58
+ }
59
+
60
+ // ../core/src/runtime/authoredTiming.ts
61
+ function finiteNumber(value) {
62
+ if (value == null) return null;
63
+ if (typeof value === "string" && value.trim() === "") return null;
64
+ const parsed = typeof value === "number" ? value : Number(value);
65
+ return Number.isFinite(parsed) ? parsed : null;
66
+ }
67
+ function resolveAuthoredTimingWindow(values) {
68
+ const parsedStart = finiteNumber(values.start);
69
+ if (parsedStart == null) return null;
70
+ const start = Math.max(0, parsedStart);
71
+ const publicDuration = finiteNumber(values.duration);
72
+ const preservedDuration = finiteNumber(values.authoredDuration);
73
+ const duration = publicDuration != null && publicDuration > 0 ? publicDuration : preservedDuration != null && preservedDuration > 0 ? preservedDuration : null;
74
+ if (duration != null) return { start, duration, end: start + duration };
75
+ const publicEnd = finiteNumber(values.end);
76
+ const preservedEnd = finiteNumber(values.authoredEnd);
77
+ const end = publicEnd != null && publicEnd > start ? publicEnd : preservedEnd != null && preservedEnd > start ? preservedEnd : null;
78
+ return { start, duration: end == null ? null : end - start, end };
79
+ }
80
+
81
+ // ../parsers/src/compositionContract.ts
82
+ var COMPOSITION_ATTRIBUTES = Object.freeze({
83
+ start: "data-start",
84
+ duration: "data-duration",
85
+ trackIndex: "data-track-index",
86
+ derivedEnd: "data-end",
87
+ legacyTrack: "data-layer"
88
+ });
89
+ var CANONICAL_AUTHORED_TIMING_ATTRIBUTES = Object.freeze([
90
+ COMPOSITION_ATTRIBUTES.start,
91
+ COMPOSITION_ATTRIBUTES.duration,
92
+ COMPOSITION_ATTRIBUTES.trackIndex
93
+ ]);
94
+ var DERIVED_TIMING_ATTRIBUTES = Object.freeze([
95
+ COMPOSITION_ATTRIBUTES.derivedEnd
96
+ ]);
97
+ var LEGACY_TIMING_ATTRIBUTES = Object.freeze([
98
+ COMPOSITION_ATTRIBUTES.derivedEnd,
99
+ COMPOSITION_ATTRIBUTES.legacyTrack
100
+ ]);
101
+ function parseNumeric(value) {
102
+ if (value == null || value.trim() === "") return null;
103
+ const parsed = Number(value);
104
+ return Number.isFinite(parsed) ? parsed : null;
105
+ }
106
+ var REFERENCE_ID_PATTERN = /^[A-Za-z0-9_.:-]+$/;
107
+ function isAsciiDigitAt(value, index) {
108
+ const code = value.charCodeAt(index);
109
+ return code >= 48 && code <= 57;
110
+ }
111
+ function skipDigitsLeft(value, start) {
112
+ let cursor = start;
113
+ while (cursor >= 0 && isAsciiDigitAt(value, cursor)) cursor--;
114
+ return cursor;
115
+ }
116
+ function skipWhitespaceLeft(value, start) {
117
+ let cursor = start;
118
+ while (cursor >= 0 && (value[cursor] ?? "").trim() === "") cursor--;
119
+ return cursor;
120
+ }
121
+ function findMagnitudeStart(value) {
122
+ const last = value.length - 1;
123
+ if (!isAsciiDigitAt(value, last)) return null;
124
+ let cursor = skipDigitsLeft(value, last);
125
+ if (value[cursor] === ".") cursor = skipDigitsLeft(value, cursor - 1);
126
+ return cursor + 1;
127
+ }
128
+ function parseReferenceOffset(value) {
129
+ const magnitudeStart = findMagnitudeStart(value);
130
+ if (magnitudeStart == null) return null;
131
+ const operatorIndex = skipWhitespaceLeft(value, magnitudeStart - 1);
132
+ const operator = value[operatorIndex];
133
+ if (operator !== "+" && operator !== "-") return null;
134
+ const refId = value.slice(0, operatorIndex).trim();
135
+ if (!REFERENCE_ID_PATTERN.test(refId)) return null;
136
+ const magnitude = Number(value.slice(magnitudeStart));
137
+ if (!Number.isFinite(magnitude)) return null;
138
+ return { refId, operator, magnitude };
139
+ }
140
+ function parseStartExpression(raw) {
141
+ const normalized = (raw ?? "").trim();
142
+ if (!normalized) return null;
143
+ const absolute = parseNumeric(normalized);
144
+ if (absolute != null) return { kind: "absolute", value: absolute };
145
+ if (REFERENCE_ID_PATTERN.test(normalized)) {
146
+ return { kind: "reference", refId: normalized, offset: 0 };
147
+ }
148
+ const reference = parseReferenceOffset(normalized);
149
+ if (!reference) return null;
150
+ return {
151
+ kind: "reference",
152
+ refId: reference.refId,
153
+ offset: reference.operator === "-" ? -reference.magnitude : reference.magnitude
154
+ };
155
+ }
156
+
157
+ // ../parsers/src/mediaDuration.ts
158
+ var nonNegative = (raw) => {
159
+ const value = parseNumeric(raw);
160
+ return value !== null && value >= 0 ? value : null;
161
+ };
162
+ function readMediaOffsetSeconds(getAttr) {
163
+ return nonNegative(getAttr("data-playback-start")) ?? nonNegative(getAttr("data-media-start")) ?? 0;
164
+ }
165
+ var MIN_PLAYBACK_RATE = 0.1;
166
+ var MAX_PLAYBACK_RATE = 10;
167
+ function clampPlaybackRate(raw) {
168
+ return Number.isFinite(raw) && raw > 0 ? Math.max(MIN_PLAYBACK_RATE, Math.min(MAX_PLAYBACK_RATE, raw)) : 1;
169
+ }
170
+ function readPlaybackRate(getAttr, fallback = 1) {
171
+ const authored = Number.parseFloat(getAttr("data-playback-rate") ?? "");
172
+ return clampPlaybackRate(Number.isFinite(authored) && authored > 0 ? authored : fallback);
173
+ }
174
+ function readDataDurationSeconds(getAttr) {
175
+ const duration = parseNumeric(getAttr("data-duration"));
176
+ return duration !== null && duration > 0 ? duration : null;
177
+ }
178
+ function readAuthoredDurationSeconds(getAttr, startSeconds) {
179
+ const duration = readDataDurationSeconds(getAttr);
180
+ if (duration !== null) return duration;
181
+ const end = parseNumeric(getAttr("data-end"));
182
+ return end !== null ? end - startSeconds : null;
183
+ }
184
+ var DEFAULT_IMAGE_TIMELINE_DURATION_SECONDS = 3;
185
+ function resolveNaturalDurationSeconds(sourceDurationSeconds, mediaStartSeconds, playbackRate) {
186
+ if (!Number.isFinite(sourceDurationSeconds)) return null;
187
+ const remaining = Math.max(0, sourceDurationSeconds - mediaStartSeconds);
188
+ return remaining / clampPlaybackRate(playbackRate);
189
+ }
190
+ function resolveMediaDuration(input) {
191
+ const { tag, authoredDurationSeconds, sourceDurationSeconds, mediaStartSeconds, playbackRate } = input;
192
+ if (authoredDurationSeconds !== null && authoredDurationSeconds > 0) {
193
+ return { seconds: authoredDurationSeconds, source: "authored" };
194
+ }
195
+ if (tag === "img") {
196
+ return { seconds: DEFAULT_IMAGE_TIMELINE_DURATION_SECONDS, source: "default" };
197
+ }
198
+ if (sourceDurationSeconds === null) {
199
+ return { seconds: null, source: "pending", reason: "source duration not yet probed" };
200
+ }
201
+ const seconds = resolveNaturalDurationSeconds(
202
+ sourceDurationSeconds,
203
+ mediaStartSeconds,
204
+ playbackRate
205
+ );
206
+ if (seconds === null) {
207
+ return { seconds: null, source: "pending", reason: "source reported a non-finite duration" };
208
+ }
209
+ return { seconds, source: "media" };
210
+ }
211
+
212
+ // ../core/src/audioFx.ts
213
+ var HF_AUDIO_FX_ATTR = "data-fx-chain";
214
+ var HF_AUDIO_FX_DATA_KEY = HF_AUDIO_FX_ATTR.slice("data-".length);
215
+ var freq = (key = "frequency", label = "Frequency", def = 1e3, min = 20, max = 2e4) => ({
216
+ kind: "number",
217
+ key,
218
+ label,
219
+ unit: "Hz",
220
+ min,
221
+ max,
222
+ step: 1,
223
+ default: def,
224
+ scale: "log",
225
+ automatable: true
226
+ });
227
+ var qParam = (def = 0.707, hint = "Bandwidth: higher is narrower.") => ({
228
+ kind: "number",
229
+ key: "q",
230
+ label: "Q",
231
+ unit: "",
232
+ min: 0.1,
233
+ max: 20,
234
+ step: 0.01,
235
+ default: def,
236
+ scale: "log",
237
+ automatable: true,
238
+ hint
239
+ });
240
+ var gainDb = (min = -40, max = 40, def = 0) => ({
241
+ kind: "number",
242
+ key: "gain",
243
+ label: "Gain",
244
+ unit: "dB",
245
+ min,
246
+ max,
247
+ step: 0.1,
248
+ default: def,
249
+ automatable: true
250
+ });
251
+ var poles = {
252
+ kind: "enum",
253
+ key: "poles",
254
+ label: "Slope",
255
+ options: [
256
+ { value: "1", label: "6 dB/oct" },
257
+ { value: "2", label: "12 dB/oct" }
258
+ ],
259
+ default: "2",
260
+ hint: "Two poles is the usual biquad; one pole is gentler."
261
+ };
262
+ var HF_AUDIO_FX = [
263
+ {
264
+ id: "gain",
265
+ label: "Gain",
266
+ group: "dynamics",
267
+ description: "Raise or lower the whole signal. Automate it to duck under something else.",
268
+ // Cuts go deep, boosts stay modest: this is a level stage for making room,
269
+ // and a chain that can add 40 dB clips long before it is useful.
270
+ params: [gainDb(-60, 12, 0)],
271
+ web: "gain-node"
272
+ },
273
+ {
274
+ id: "peaking",
275
+ label: "Peaking EQ",
276
+ group: "filter",
277
+ description: "Boost or cut a band, leaving everything either side alone.",
278
+ params: [freq("frequency", "Frequency", 1e3), gainDb(-40, 40, 0), qParam(1)],
279
+ web: "biquad-peaking"
280
+ },
281
+ {
282
+ id: "lowshelf",
283
+ label: "Low Shelf",
284
+ group: "filter",
285
+ description: "Lift or drop everything below the corner frequency.",
286
+ // No Q: the Web Audio spec leaves it unused for shelving filters, so the
287
+ // control moved nothing — and being automatable, a lane drawn on it would
288
+ // have been silently inert.
289
+ params: [freq("frequency", "Frequency", 200, 20, 2e3), gainDb(-40, 40, 0)],
290
+ web: "biquad-lowshelf"
291
+ },
292
+ {
293
+ id: "highshelf",
294
+ label: "High Shelf",
295
+ group: "filter",
296
+ description: "Lift or drop everything above the corner frequency.",
297
+ params: [freq("frequency", "Frequency", 4e3, 500, 2e4), gainDb(-40, 40, 0)],
298
+ web: "biquad-highshelf"
299
+ },
300
+ {
301
+ id: "highpass",
302
+ label: "High-pass",
303
+ group: "filter",
304
+ description: "Remove low frequencies: the usual fix for rumble on a voice.",
305
+ params: [freq("frequency", "Cutoff", 300, 20, 2e4), qParam(0.707), poles],
306
+ web: "biquad-highpass"
307
+ },
308
+ {
309
+ id: "lowpass",
310
+ label: "Low-pass",
311
+ group: "filter",
312
+ description: "Remove high frequencies, which darkens or muffles a track.",
313
+ params: [freq("frequency", "Cutoff", 8e3, 100, 2e4), qParam(0.707), poles],
314
+ web: "biquad-lowpass"
315
+ },
316
+ {
317
+ id: "compressor",
318
+ label: "Compressor",
319
+ group: "dynamics",
320
+ description: "Pull loud parts down so the quiet ones can come up.",
321
+ params: [
322
+ {
323
+ kind: "number",
324
+ key: "threshold",
325
+ label: "Threshold",
326
+ unit: "dB",
327
+ min: -60,
328
+ max: 0,
329
+ step: 0.5,
330
+ default: -24,
331
+ hint: "Level above which the compressor starts working."
332
+ },
333
+ {
334
+ kind: "number",
335
+ key: "ratio",
336
+ label: "Ratio",
337
+ unit: ":1",
338
+ min: 1,
339
+ max: 20,
340
+ step: 0.1,
341
+ default: 4
342
+ },
343
+ {
344
+ kind: "number",
345
+ key: "attack",
346
+ label: "Attack",
347
+ unit: "ms",
348
+ min: 0.01,
349
+ max: 2e3,
350
+ step: 0.1,
351
+ default: 20,
352
+ scale: "log"
353
+ },
354
+ {
355
+ kind: "number",
356
+ key: "release",
357
+ label: "Release",
358
+ unit: "ms",
359
+ min: 0.01,
360
+ max: 9e3,
361
+ step: 1,
362
+ default: 250,
363
+ scale: "log"
364
+ },
365
+ {
366
+ kind: "number",
367
+ key: "knee",
368
+ label: "Knee",
369
+ unit: "",
370
+ min: 1,
371
+ max: 8,
372
+ step: 0.01,
373
+ default: 2.83,
374
+ hint: "1 is a hard corner; higher eases into it."
375
+ },
376
+ {
377
+ kind: "number",
378
+ key: "makeup",
379
+ label: "Makeup",
380
+ unit: "dB",
381
+ min: 0,
382
+ max: 36,
383
+ step: 0.1,
384
+ default: 0
385
+ },
386
+ {
387
+ kind: "number",
388
+ key: "mix",
389
+ label: "Mix",
390
+ unit: "",
391
+ min: 0,
392
+ max: 1,
393
+ step: 0.01,
394
+ default: 1,
395
+ hint: "Below 1 blends the dry signal back in."
396
+ }
397
+ ],
398
+ web: "worklet-compressor"
399
+ },
400
+ {
401
+ id: "limiter",
402
+ label: "Limiter",
403
+ group: "dynamics",
404
+ description: "Peak limiter that follows the signal's level. It has no lookahead, so a fast peak or an inter-sample peak can pass the ceiling; use the true-peak limiter for a delivery limit.",
405
+ params: [
406
+ {
407
+ kind: "number",
408
+ key: "limit",
409
+ label: "Ceiling",
410
+ unit: "dB",
411
+ min: -24,
412
+ max: 0,
413
+ step: 0.1,
414
+ default: -1
415
+ },
416
+ {
417
+ kind: "number",
418
+ key: "attack",
419
+ label: "Attack",
420
+ unit: "ms",
421
+ min: 0.1,
422
+ max: 80,
423
+ step: 0.1,
424
+ default: 5
425
+ },
426
+ {
427
+ kind: "number",
428
+ key: "release",
429
+ label: "Release",
430
+ unit: "ms",
431
+ min: 1,
432
+ max: 8e3,
433
+ step: 1,
434
+ default: 50,
435
+ scale: "log"
436
+ },
437
+ {
438
+ kind: "number",
439
+ key: "level_out",
440
+ label: "Output",
441
+ unit: "dB",
442
+ min: -24,
443
+ max: 24,
444
+ step: 0.1,
445
+ default: 0
446
+ }
447
+ ],
448
+ web: "worklet-limiter"
449
+ },
450
+ {
451
+ id: "truepeak",
452
+ label: "True-peak limiter",
453
+ group: "dynamics",
454
+ description: "Lookahead limiter that holds a 4x estimate of the inter-sample peak at a dBTP ceiling; full-band noise can land up to about 1.7 dB above it. Adds lookahead plus about 0.3 ms of latency.",
455
+ params: [
456
+ {
457
+ kind: "number",
458
+ key: "ceiling",
459
+ label: "Ceiling",
460
+ unit: "dBTP",
461
+ min: -24,
462
+ max: 0,
463
+ step: 0.1,
464
+ default: -1,
465
+ hint: "Target true peak, measured on a 4x oversampled signal. Dense full-band sound can end up to about 1.7 dB above it."
466
+ },
467
+ {
468
+ kind: "number",
469
+ key: "lookahead",
470
+ label: "Lookahead",
471
+ unit: "ms",
472
+ min: 0.5,
473
+ max: 10,
474
+ step: 0.1,
475
+ default: 3,
476
+ hint: "How far ahead the gain starts falling. Longer is smoother and adds the same latency."
477
+ },
478
+ {
479
+ kind: "number",
480
+ key: "release",
481
+ label: "Release",
482
+ unit: "ms",
483
+ min: 10,
484
+ max: 2e3,
485
+ step: 1,
486
+ default: 80,
487
+ scale: "log"
488
+ }
489
+ ],
490
+ web: "worklet-truepeak"
491
+ },
492
+ {
493
+ id: "gate",
494
+ label: "Noise Gate",
495
+ group: "dynamics",
496
+ description: "Silence the track when it drops below the threshold.",
497
+ params: [
498
+ {
499
+ kind: "number",
500
+ key: "threshold",
501
+ label: "Threshold",
502
+ unit: "dB",
503
+ min: -80,
504
+ max: 0,
505
+ step: 0.5,
506
+ default: -35
507
+ },
508
+ {
509
+ kind: "number",
510
+ key: "range",
511
+ label: "Range",
512
+ unit: "dB",
513
+ min: -80,
514
+ max: 0,
515
+ step: 0.5,
516
+ default: -24,
517
+ hint: "How far down the gate pulls when closed."
518
+ },
519
+ {
520
+ kind: "number",
521
+ key: "ratio",
522
+ label: "Ratio",
523
+ unit: ":1",
524
+ min: 1,
525
+ max: 20,
526
+ step: 0.1,
527
+ default: 10
528
+ },
529
+ {
530
+ kind: "number",
531
+ key: "attack",
532
+ label: "Attack",
533
+ unit: "ms",
534
+ min: 0.01,
535
+ max: 9e3,
536
+ step: 0.1,
537
+ default: 1,
538
+ scale: "log"
539
+ },
540
+ {
541
+ kind: "number",
542
+ key: "release",
543
+ label: "Release",
544
+ unit: "ms",
545
+ min: 0.01,
546
+ max: 9e3,
547
+ step: 1,
548
+ default: 100,
549
+ scale: "log"
550
+ },
551
+ {
552
+ kind: "number",
553
+ key: "knee",
554
+ label: "Knee",
555
+ unit: "",
556
+ min: 1,
557
+ max: 8,
558
+ step: 0.01,
559
+ default: 2.83
560
+ }
561
+ ],
562
+ web: "worklet-gate"
563
+ },
564
+ {
565
+ id: "saturate",
566
+ label: "Saturation",
567
+ group: "nonlinear",
568
+ description: "Soft-clip the waveform for warmth or outright distortion.",
569
+ params: [
570
+ {
571
+ kind: "enum",
572
+ key: "type",
573
+ label: "Curve",
574
+ options: [
575
+ { value: "tanh", label: "Tanh" },
576
+ { value: "atan", label: "Arctan" },
577
+ { value: "cubic", label: "Cubic" },
578
+ { value: "exp", label: "Exponential" },
579
+ { value: "alg", label: "Algebraic" },
580
+ { value: "quintic", label: "Quintic" },
581
+ { value: "sin", label: "Sine" },
582
+ { value: "erf", label: "Error function" },
583
+ { value: "hard", label: "Hard clip" }
584
+ ],
585
+ default: "tanh"
586
+ },
587
+ {
588
+ kind: "number",
589
+ key: "threshold",
590
+ label: "Threshold",
591
+ unit: "dB",
592
+ min: -40,
593
+ max: 0,
594
+ step: 0.1,
595
+ default: -6
596
+ },
597
+ {
598
+ kind: "number",
599
+ key: "output",
600
+ automatable: true,
601
+ label: "Output",
602
+ unit: "dB",
603
+ min: -24,
604
+ max: 24,
605
+ step: 0.1,
606
+ default: 0
607
+ },
608
+ {
609
+ kind: "number",
610
+ key: "oversample",
611
+ label: "Oversample",
612
+ unit: "x",
613
+ min: 1,
614
+ max: 8,
615
+ step: 1,
616
+ default: 4,
617
+ hint: "Higher costs more but keeps aliasing down."
618
+ }
619
+ ],
620
+ web: "waveshaper"
621
+ },
622
+ {
623
+ id: "bitcrush",
624
+ label: "Bitcrush",
625
+ group: "nonlinear",
626
+ description: "Drop bit depth and sample rate for a lo-fi, digital sound.",
627
+ params: [
628
+ {
629
+ kind: "number",
630
+ key: "bits",
631
+ label: "Bit depth",
632
+ unit: "bit",
633
+ min: 1,
634
+ max: 32,
635
+ step: 0.1,
636
+ default: 8
637
+ },
638
+ {
639
+ kind: "number",
640
+ key: "samples",
641
+ label: "Sample hold",
642
+ unit: "x",
643
+ min: 1,
644
+ max: 250,
645
+ step: 1,
646
+ default: 1,
647
+ hint: "Repeats each sample N times, a crude downsample."
648
+ },
649
+ {
650
+ kind: "number",
651
+ key: "mix",
652
+ label: "Mix",
653
+ unit: "",
654
+ min: 0,
655
+ max: 1,
656
+ step: 0.01,
657
+ default: 1
658
+ }
659
+ ],
660
+ web: "worklet-bitcrush"
661
+ },
662
+ {
663
+ id: "pitchshift",
664
+ label: "Pitch shift",
665
+ group: "time",
666
+ // The granular algorithm reads from a 100 ms grain, so its output runs a
667
+ // constant ~50 ms behind its input and nothing in the graph subtracts that
668
+ // — there is no latency/pre-roll concept here yet. It is inaudible on its
669
+ // own and audible against picture or against an unshifted track, so it is
670
+ // stated rather than hidden. `semitones: 0` bypasses the node entirely.
671
+ description: "Shifts pitch up or down without changing playback speed. Adds ~50 ms of latency.",
672
+ params: [
673
+ {
674
+ kind: "number",
675
+ key: "semitones",
676
+ label: "Semitones",
677
+ unit: "st",
678
+ min: -12,
679
+ max: 12,
680
+ step: 1,
681
+ default: 0
682
+ },
683
+ {
684
+ kind: "number",
685
+ key: "mix",
686
+ label: "Mix",
687
+ unit: "",
688
+ min: 0,
689
+ max: 1,
690
+ step: 0.01,
691
+ default: 1
692
+ }
693
+ ],
694
+ web: "worklet-pitchshift"
695
+ },
696
+ {
697
+ id: "delay",
698
+ label: "Delay",
699
+ group: "time",
700
+ description: "Repeating echoes behind the dry signal.",
701
+ params: [
702
+ {
703
+ kind: "number",
704
+ key: "time",
705
+ automatable: true,
706
+ label: "Time",
707
+ unit: "ms",
708
+ min: 1,
709
+ max: 5e3,
710
+ step: 1,
711
+ default: 250,
712
+ scale: "log"
713
+ },
714
+ // aecho rejects a decay of exactly 0 ("out of allowed range: (0, 1]"),
715
+ // so the floor is a hair above silence rather than at it.
716
+ {
717
+ kind: "number",
718
+ key: "feedback",
719
+ automatable: true,
720
+ label: "Feedback",
721
+ unit: "",
722
+ min: 0.01,
723
+ max: 0.95,
724
+ step: 0.01,
725
+ default: 0.35
726
+ },
727
+ {
728
+ kind: "number",
729
+ key: "mix",
730
+ automatable: true,
731
+ label: "Mix",
732
+ unit: "",
733
+ min: 0,
734
+ max: 1,
735
+ step: 0.01,
736
+ default: 0.4
737
+ }
738
+ ],
739
+ web: "delay-feedback"
740
+ },
741
+ {
742
+ id: "chorus",
743
+ label: "Chorus",
744
+ group: "time",
745
+ description: "Detuned copies of the signal for width and thickness.",
746
+ params: [
747
+ {
748
+ kind: "number",
749
+ key: "delay",
750
+ automatable: true,
751
+ label: "Delay",
752
+ unit: "ms",
753
+ min: 1,
754
+ max: 100,
755
+ step: 0.1,
756
+ default: 7
757
+ },
758
+ {
759
+ kind: "number",
760
+ key: "depth",
761
+ automatable: true,
762
+ label: "Depth",
763
+ unit: "ms",
764
+ min: 0,
765
+ max: 10,
766
+ step: 0.01,
767
+ default: 2
768
+ },
769
+ {
770
+ kind: "number",
771
+ key: "speed",
772
+ automatable: true,
773
+ label: "Rate",
774
+ unit: "Hz",
775
+ min: 0.01,
776
+ max: 10,
777
+ step: 0.01,
778
+ default: 1
779
+ },
780
+ {
781
+ kind: "number",
782
+ key: "mix",
783
+ automatable: true,
784
+ label: "Mix",
785
+ unit: "",
786
+ min: 0,
787
+ max: 1,
788
+ step: 0.01,
789
+ default: 0.5
790
+ }
791
+ ],
792
+ web: "chorus-lfo"
793
+ },
794
+ {
795
+ id: "phaser",
796
+ label: "Phaser",
797
+ group: "time",
798
+ description: "Sweeping notches moving through the spectrum.",
799
+ params: [
800
+ {
801
+ kind: "number",
802
+ key: "in_gain",
803
+ automatable: true,
804
+ label: "Input",
805
+ unit: "",
806
+ min: 0,
807
+ max: 1,
808
+ step: 0.01,
809
+ default: 0.4
810
+ },
811
+ {
812
+ kind: "number",
813
+ key: "out_gain",
814
+ automatable: true,
815
+ label: "Output",
816
+ unit: "",
817
+ min: 0,
818
+ max: 2,
819
+ step: 0.01,
820
+ default: 0.74
821
+ },
822
+ // aphaser advertises a delay range starting at 0 but rejects it at
823
+ // runtime with "delay is too small"; 0.1 ms is the real floor.
824
+ {
825
+ kind: "number",
826
+ key: "delay",
827
+ label: "Delay",
828
+ unit: "ms",
829
+ min: 0.1,
830
+ max: 5,
831
+ step: 0.1,
832
+ default: 3
833
+ },
834
+ {
835
+ kind: "number",
836
+ key: "decay",
837
+ label: "Decay",
838
+ unit: "",
839
+ min: 0,
840
+ max: 0.99,
841
+ step: 0.01,
842
+ default: 0.4
843
+ },
844
+ {
845
+ kind: "number",
846
+ key: "speed",
847
+ automatable: true,
848
+ label: "Rate",
849
+ unit: "Hz",
850
+ min: 0.1,
851
+ max: 2,
852
+ step: 0.01,
853
+ default: 0.5
854
+ },
855
+ {
856
+ kind: "enum",
857
+ key: "type",
858
+ label: "Waveform",
859
+ options: [
860
+ { value: "0", label: "Triangular" },
861
+ { value: "1", label: "Sinusoidal" }
862
+ ],
863
+ default: "0"
864
+ }
865
+ ],
866
+ web: "allpass-phaser"
867
+ },
868
+ {
869
+ id: "reverb",
870
+ label: "Reverb",
871
+ group: "time",
872
+ description: "Room tail. Both ends convolve the same generated impulse, so preview matches render.",
873
+ params: [
874
+ {
875
+ kind: "number",
876
+ key: "size",
877
+ label: "Room size",
878
+ unit: "",
879
+ min: 0.05,
880
+ max: 1,
881
+ step: 0.01,
882
+ default: 0.7
883
+ },
884
+ {
885
+ kind: "number",
886
+ key: "damping",
887
+ label: "Damping",
888
+ unit: "",
889
+ min: 0,
890
+ max: 1,
891
+ step: 0.01,
892
+ default: 0.5,
893
+ hint: "Higher rolls the top off the tail faster."
894
+ },
895
+ {
896
+ kind: "number",
897
+ key: "wet",
898
+ automatable: true,
899
+ label: "Wet",
900
+ unit: "",
901
+ min: 0,
902
+ max: 1,
903
+ step: 0.01,
904
+ default: 0.35
905
+ },
906
+ {
907
+ kind: "number",
908
+ key: "dry",
909
+ automatable: true,
910
+ label: "Dry",
911
+ unit: "",
912
+ min: 0,
913
+ max: 1,
914
+ step: 0.01,
915
+ default: 0.7
916
+ }
917
+ ],
918
+ web: "convolver"
919
+ }
920
+ ];
921
+ var BY_ID = new Map(HF_AUDIO_FX.map((d) => [d.id, d]));
922
+ var HF_AUDIO_FX_IDS = HF_AUDIO_FX.map((d) => d.id);
923
+
924
+ // ../core/src/audioGain.ts
925
+ var MAX_AUDIO_GAIN_DB = 12;
926
+ function audioDbToGain(db) {
927
+ return 10 ** (db / 20);
928
+ }
929
+ var MAX_AUDIO_GAIN = audioDbToGain(MAX_AUDIO_GAIN_DB);
930
+
931
+ // ../core/src/audioAutomation.ts
932
+ var HF_AUDIO_AUTOMATION_ATTR = "data-automation";
933
+ var HF_AUDIO_AUTOMATION_DATA_KEY = HF_AUDIO_AUTOMATION_ATTR.slice("data-".length);
934
+ var HF_AUDIO_AUTOMATION_VERSION = 1;
935
+ var MAX_AUTOMATION_POINTS = 512;
936
+ var AudioAutomationError = class extends Error {
937
+ constructor(message) {
938
+ super(message);
939
+ this.name = "AudioAutomationError";
940
+ }
941
+ };
942
+ var VOLUME_TARGET = "volume";
943
+ var RATE_TARGET = "rate";
944
+ function parseAutomationTarget(target) {
945
+ if (target === VOLUME_TARGET) return { kind: "volume" };
946
+ if (target === RATE_TARGET) return { kind: "rate" };
947
+ const parts = target.split(".");
948
+ if (parts.length === 3 && parts[0] === "fx" && parts[1] === PRESET_TARGET_KEY) {
949
+ const presetId = parts[2];
950
+ return presetId ? { kind: "preset", presetId } : null;
951
+ }
952
+ if (parts.length !== 3 || parts[0] !== "fx") return null;
953
+ const [, nodeId, param] = parts;
954
+ if (!nodeId || !param) return null;
955
+ return { kind: "fx", nodeId, param };
956
+ }
957
+ var PRESET_TARGET_KEY = "preset";
958
+ var VOLUME_RANGE = {
959
+ min: 0,
960
+ max: MAX_AUDIO_GAIN,
961
+ step: 0.01,
962
+ unit: "",
963
+ label: "Volume",
964
+ scale: "linear",
965
+ default: 1
966
+ };
967
+ var RATE_RANGE = {
968
+ min: MIN_PLAYBACK_RATE,
969
+ max: MAX_PLAYBACK_RATE,
970
+ step: 0.05,
971
+ unit: "x",
972
+ label: "Speed",
973
+ scale: "log",
974
+ default: 1
975
+ };
976
+ function numberOrNull(raw) {
977
+ if (typeof raw === "number") return Number.isFinite(raw) ? raw : null;
978
+ if (typeof raw === "string" && raw.trim() !== "") {
979
+ const n = Number(raw);
980
+ return Number.isFinite(n) ? n : null;
981
+ }
982
+ return null;
983
+ }
984
+ function clampCurve(v) {
985
+ const n = numberOrNull(v);
986
+ if (n === null || n === 0) return 0;
987
+ return Math.min(1, Math.max(-1, n));
988
+ }
989
+ function clampVia(rawX, rawY) {
990
+ const x = numberOrNull(rawX);
991
+ const y = numberOrNull(rawY);
992
+ if (x === null || y === null) return null;
993
+ const inside = (n) => Math.min(0.999, Math.max(1e-3, n));
994
+ const cx = inside(x);
995
+ const cy = inside(y);
996
+ if (Math.abs(cx - cy) < 1e-6) return null;
997
+ return { x: cx, y: cy };
998
+ }
999
+ function shapeFields(p) {
1000
+ const curve = clampCurve(p?.curve);
1001
+ const via = clampVia(p?.viaX, p?.viaY);
1002
+ return {
1003
+ ...curve ? { curve } : {},
1004
+ ...via ? { viaX: via.x, viaY: via.y } : {}
1005
+ };
1006
+ }
1007
+ function cleanPoint(p, range) {
1008
+ const t = numberOrNull(p?.t);
1009
+ const v = numberOrNull(p?.v);
1010
+ if (t === null || v === null) return null;
1011
+ const clamped = range ? Math.min(range.max, Math.max(range.min, v)) : v;
1012
+ return { t: Math.max(0, t), v: clamped, ...shapeFields(p) };
1013
+ }
1014
+ function normalizePoints(points, range) {
1015
+ const clean = points.map((p) => cleanPoint(p, range)).filter((p) => p !== null).sort((a, b) => a.t - b.t);
1016
+ const out = [];
1017
+ for (const p of clean) {
1018
+ if (out.length > 0 && out[out.length - 1].t === p.t) out[out.length - 1] = p;
1019
+ else out.push(p);
1020
+ }
1021
+ return out.slice(0, MAX_AUTOMATION_POINTS);
1022
+ }
1023
+ function normalizeAutomation(automation) {
1024
+ const lanes = [];
1025
+ for (const lane of automation.lanes) {
1026
+ if (!parseAutomationTarget(lane.target)) continue;
1027
+ const range = lane.target === VOLUME_TARGET ? VOLUME_RANGE : lane.target === RATE_TARGET ? RATE_RANGE : null;
1028
+ const points = normalizePoints(lane.points ?? [], range);
1029
+ if (points.length > 0) lanes.push({ target: lane.target, points });
1030
+ }
1031
+ return { version: HF_AUDIO_AUTOMATION_VERSION, lanes };
1032
+ }
1033
+ function parseAutomation(json) {
1034
+ let raw;
1035
+ try {
1036
+ raw = JSON.parse(json);
1037
+ } catch (err) {
1038
+ throw new AudioAutomationError(`Automation is not valid JSON: ${err.message}`);
1039
+ }
1040
+ if (typeof raw !== "object" || raw === null) {
1041
+ throw new AudioAutomationError("Automation must be a JSON object.");
1042
+ }
1043
+ const obj = raw;
1044
+ if (obj.version !== HF_AUDIO_AUTOMATION_VERSION) {
1045
+ throw new AudioAutomationError(`Unsupported automation version: ${String(obj.version)}`);
1046
+ }
1047
+ if (!Array.isArray(obj.lanes)) {
1048
+ throw new AudioAutomationError("Automation is missing a `lanes` array.");
1049
+ }
1050
+ const lanes = obj.lanes.map((l, i) => {
1051
+ if (typeof l !== "object" || l === null) {
1052
+ throw new AudioAutomationError(`Lane ${i} is not an object.`);
1053
+ }
1054
+ const lane = l;
1055
+ if (typeof lane.target !== "string" || !parseAutomationTarget(lane.target)) {
1056
+ throw new AudioAutomationError(`Lane ${i} has an unreadable target: ${String(lane.target)}`);
1057
+ }
1058
+ if (!Array.isArray(lane.points)) {
1059
+ throw new AudioAutomationError(`Lane ${i} is missing a \`points\` array.`);
1060
+ }
1061
+ return { target: lane.target, points: lane.points };
1062
+ });
1063
+ return normalizeAutomation({ version: HF_AUDIO_AUTOMATION_VERSION, lanes });
1064
+ }
1065
+ function applyCurve(x, curve) {
1066
+ if (!curve) return x;
1067
+ return Math.pow(x, Math.pow(2, 2 * curve));
1068
+ }
1069
+ function viaConic(viaX, viaY) {
1070
+ const dx = viaX - 0.5;
1071
+ const dy = viaY - 0.5;
1072
+ const edge = 0.999;
1073
+ const MAX_WEIGHT = 1e6;
1074
+ const needX = dx > 0 ? dx / (edge - viaX) : dx < 0 ? -dx / (viaX - (1 - edge)) : 0;
1075
+ const needY = dy > 0 ? dy / (edge - viaY) : dy < 0 ? -dy / (viaY - (1 - edge)) : 0;
1076
+ const w = Math.min(MAX_WEIGHT, Math.max(1, needX, needY));
1077
+ return { cx: viaX + dx / w, cy: viaY + dy / w, w };
1078
+ }
1079
+ function shapeVia(x, viaX, viaY) {
1080
+ const { cx, cy, w } = viaConic(viaX, viaY);
1081
+ const spread = 2 - 2 * w;
1082
+ const a = x * spread - 1 + 2 * w * cx;
1083
+ const b = -x * spread - 2 * w * cx;
1084
+ const t = conicParam(a, b, x);
1085
+ const rest = 1 - t;
1086
+ const denominator = rest * rest + 2 * w * t * rest + t * t;
1087
+ if (!(denominator > 0)) return x;
1088
+ return (2 * w * cy * t * rest + t * t) / denominator;
1089
+ }
1090
+ function conicParam(a, b, c) {
1091
+ if (Math.abs(a) < 1e-12) return Math.abs(b) < 1e-12 ? c : -c / b;
1092
+ const root = Math.sqrt(Math.max(0, b * b - 4 * a * c));
1093
+ const first = (-b + root) / (2 * a);
1094
+ const second = (-b - root) / (2 * a);
1095
+ const onArc = (t) => t >= -1e-9 && t <= 1 + 1e-9;
1096
+ if (onArc(first)) return Math.min(1, Math.max(0, first));
1097
+ if (onArc(second)) return Math.min(1, Math.max(0, second));
1098
+ return c;
1099
+ }
1100
+ function shapeProgress(x, point) {
1101
+ const via = clampVia(point.viaX, point.viaY);
1102
+ if (via) return shapeVia(Math.min(1, Math.max(0, x)), via.x, via.y);
1103
+ return applyCurve(x, point.curve);
1104
+ }
1105
+ function lerpValue(a, b, x, scale) {
1106
+ if (scale === "log" && a > 0 && b > 0) {
1107
+ return Math.exp(Math.log(a) + (Math.log(b) - Math.log(a)) * x);
1108
+ }
1109
+ return a + (b - a) * x;
1110
+ }
1111
+ function sampleAutomationLane(lane, t, scale = "linear") {
1112
+ const pts = lane.points;
1113
+ if (pts.length === 0) return 0;
1114
+ const first = pts[0];
1115
+ if (t <= first.t) return first.v;
1116
+ const last = pts[pts.length - 1];
1117
+ if (t >= last.t) return last.v;
1118
+ let lo = 0;
1119
+ let hi = pts.length - 1;
1120
+ while (hi - lo > 1) {
1121
+ const mid = lo + hi >> 1;
1122
+ if (pts[mid].t <= t) lo = mid;
1123
+ else hi = mid;
1124
+ }
1125
+ const a = pts[lo];
1126
+ const b = pts[hi];
1127
+ const span = b.t - a.t;
1128
+ if (span <= 0) return b.v;
1129
+ return lerpValue(a.v, b.v, shapeProgress((t - a.t) / span, a), scale);
1130
+ }
1131
+
1132
+ // ../core/src/runtime/audioAutomationVolume.ts
1133
+ var cache = /* @__PURE__ */ new Map();
1134
+ var CACHE_LIMIT = 64;
1135
+ function laneFromAttr(raw, target = VOLUME_TARGET) {
1136
+ const key = `${target}\0${raw}`;
1137
+ const hit = cache.get(key);
1138
+ if (hit !== void 0) return hit;
1139
+ let lane = null;
1140
+ try {
1141
+ lane = parseAutomation(raw).lanes.find((l) => l.target === target) ?? null;
1142
+ } catch {
1143
+ lane = null;
1144
+ }
1145
+ if (cache.size > CACHE_LIMIT) cache.clear();
1146
+ cache.set(key, lane);
1147
+ return lane;
1148
+ }
1149
+
1150
+ // ../core/src/speedRamp.ts
1151
+ var CELLS_PER_SEGMENT = 48;
1152
+ var tables = /* @__PURE__ */ new WeakMap();
1153
+ function buildTable(lane) {
1154
+ const pts = lane.points;
1155
+ const firstRate = pts[0].v;
1156
+ const lastRate = pts[pts.length - 1].v;
1157
+ const ts = [0];
1158
+ const ss = [0];
1159
+ const push = (t) => {
1160
+ const prevT = ts[ts.length - 1];
1161
+ if (t <= prevT) return;
1162
+ const prevRate = sampleAutomationLane(lane, prevT, "log");
1163
+ ts.push(t);
1164
+ ss.push(
1165
+ ss[ss.length - 1] + (prevRate + sampleAutomationLane(lane, t, "log")) / 2 * (t - prevT)
1166
+ );
1167
+ };
1168
+ const first = Math.max(0, pts[0].t);
1169
+ push(first);
1170
+ for (let i = 1; i < pts.length; i += 1) {
1171
+ const a = pts[i - 1].t;
1172
+ const b = pts[i].t;
1173
+ if (b <= 0) continue;
1174
+ for (let k = 1; k <= CELLS_PER_SEGMENT; k += 1)
1175
+ push(Math.max(a, 0) + (b - a) * k / CELLS_PER_SEGMENT);
1176
+ }
1177
+ return { ts: Float64Array.from(ts), ss: Float64Array.from(ss), firstRate, lastRate };
1178
+ }
1179
+ function tableFor(lane) {
1180
+ let table = tables.get(lane);
1181
+ if (!table) {
1182
+ table = buildTable(lane);
1183
+ tables.set(lane, table);
1184
+ }
1185
+ return table;
1186
+ }
1187
+ function usable(spec) {
1188
+ return typeof spec !== "number" && spec.points.length > 0;
1189
+ }
1190
+ function interpolate(xs, ys, x) {
1191
+ let lo = 0;
1192
+ let hi = xs.length - 1;
1193
+ while (hi - lo > 1) {
1194
+ const mid = lo + hi >> 1;
1195
+ if (xs[mid] <= x) lo = mid;
1196
+ else hi = mid;
1197
+ }
1198
+ return ys[lo] + (ys[hi] - ys[lo]) * (x - xs[lo]) / (xs[hi] - xs[lo]);
1199
+ }
1200
+ function timeAtSourceTime(spec, source) {
1201
+ if (typeof spec === "number") return source / spec;
1202
+ if (!usable(spec)) return source;
1203
+ const { ts, ss, firstRate, lastRate } = tableFor(spec);
1204
+ const last = ts.length - 1;
1205
+ if (source <= 0) return source / firstRate;
1206
+ if (source >= ss[last]) return ts[last] + (source - ss[last]) / lastRate;
1207
+ return interpolate(ss, ts, source);
1208
+ }
1209
+ function parseRateLane(raw) {
1210
+ return raw ? laneFromAttr(raw, RATE_TARGET) : null;
1211
+ }
1212
+ function resolveRateSpec(automationAttr, constant) {
1213
+ return parseRateLane(automationAttr) ?? constant;
1214
+ }
1215
+ var SPEED_PRESETS = [
1216
+ {
1217
+ id: "montage",
1218
+ label: "Montage",
1219
+ points: [
1220
+ [0, 0.9],
1221
+ [0.15, 0.3],
1222
+ [0.5, 2.5],
1223
+ [0.85, 0.3],
1224
+ [1, 0.9]
1225
+ ]
1226
+ },
1227
+ {
1228
+ id: "hero",
1229
+ label: "Hero",
1230
+ points: [
1231
+ [0, 1],
1232
+ [0.25, 1.6],
1233
+ [0.5, 0.25],
1234
+ [0.75, 1.6],
1235
+ [1, 1]
1236
+ ]
1237
+ },
1238
+ {
1239
+ id: "bullet",
1240
+ label: "Bullet",
1241
+ points: [
1242
+ [0, 3],
1243
+ [0.4, 3],
1244
+ [0.5, 0.15],
1245
+ [0.6, 3],
1246
+ [1, 3]
1247
+ ]
1248
+ },
1249
+ {
1250
+ id: "jump-cut",
1251
+ label: "Jump cut",
1252
+ points: [
1253
+ [0, 0.5],
1254
+ [0.24, 0.5],
1255
+ [0.26, 3],
1256
+ [0.5, 3],
1257
+ [0.52, 0.5],
1258
+ [0.74, 0.5],
1259
+ [0.76, 3],
1260
+ [1, 3]
1261
+ ]
1262
+ },
1263
+ {
1264
+ id: "flash-in",
1265
+ label: "Flash in",
1266
+ points: [
1267
+ [0, 0.3],
1268
+ [0.6, 0.6],
1269
+ [1, 5]
1270
+ ]
1271
+ },
1272
+ {
1273
+ id: "flash-out",
1274
+ label: "Flash out",
1275
+ points: [
1276
+ [0, 5],
1277
+ [0.4, 0.6],
1278
+ [1, 0.3]
1279
+ ]
1280
+ },
1281
+ {
1282
+ id: "ramp-in",
1283
+ label: "Ramp in",
1284
+ points: [
1285
+ [0, 0.5],
1286
+ [1, 1]
1287
+ ]
1288
+ },
1289
+ {
1290
+ id: "ramp-out",
1291
+ label: "Ramp out",
1292
+ points: [
1293
+ [0, 1],
1294
+ [1, 0.5]
1295
+ ]
1296
+ },
1297
+ {
1298
+ id: "slowmo-middle",
1299
+ label: "Slow-mo middle",
1300
+ points: [
1301
+ [0, 1],
1302
+ [1 / 3, 0.35],
1303
+ [2 / 3, 0.35],
1304
+ [1, 1]
1305
+ ]
1306
+ }
1307
+ ];
1308
+
1309
+ // ../core/src/runtime/domRealm.ts
1310
+ var XHTML_NS = "http://www.w3.org/1999/xhtml";
1311
+ function isElementNode(value) {
1312
+ return value != null && value.nodeType === 1;
1313
+ }
1314
+ function isHtmlElement(value) {
1315
+ return isElementNode(value) && value.namespaceURI === XHTML_NS;
1316
+ }
1317
+ function isHtmlTag(value, tag) {
1318
+ return isHtmlElement(value) && value.localName === tag;
1319
+ }
1320
+ function isVideoElement(value) {
1321
+ return isHtmlTag(value, "video");
1322
+ }
1323
+ function isAudioElement(value) {
1324
+ return isHtmlTag(value, "audio");
1325
+ }
1326
+ function isMediaElement(value) {
1327
+ return isVideoElement(value) || isAudioElement(value);
1328
+ }
1329
+ function isImageElement(value) {
1330
+ return isHtmlTag(value, "img");
1331
+ }
1332
+
1333
+ // ../core/src/runtime/playbackRate.ts
1334
+ function parseStrictFiniteTimingNumber(raw) {
1335
+ return parseNumeric(raw);
1336
+ }
1337
+ function readElementPlaybackRate(el) {
1338
+ return readPlaybackRate(
1339
+ (name) => el.getAttribute(name),
1340
+ isMediaElement(el) ? el.defaultPlaybackRate : 1
1341
+ );
1342
+ }
1343
+ function readElementRateSpec(el) {
1344
+ return resolveRateSpec(el.getAttribute("data-automation"), readElementPlaybackRate(el));
1345
+ }
1346
+ function readMediaStart(el) {
1347
+ return readMediaOffsetSeconds((name) => el.getAttribute(name));
1348
+ }
1349
+ function resolveNaturalMediaTimelineDuration(el, sourceDuration) {
1350
+ return resolveNaturalMediaTimelineDurationFromValues(
1351
+ sourceDuration,
1352
+ readMediaStart(el),
1353
+ readElementRateSpec(el)
1354
+ );
1355
+ }
1356
+ function resolveTimedImageDurationSeconds(el, startSeconds = 0) {
1357
+ if (!isImageElement(el)) return null;
1358
+ if (!el.hasAttribute("data-start") && !el.hasAttribute("data-track-index")) return null;
1359
+ return resolveMediaDuration({
1360
+ tag: "img",
1361
+ authoredDurationSeconds: readAuthoredDurationSeconds(
1362
+ (name) => el.getAttribute(name),
1363
+ startSeconds
1364
+ ),
1365
+ sourceDurationSeconds: null,
1366
+ mediaStartSeconds: 0,
1367
+ playbackRate: 1
1368
+ }).seconds;
1369
+ }
1370
+ function resolveNaturalMediaTimelineDurationFromValues(sourceDuration, mediaStart, playbackRate) {
1371
+ if (typeof playbackRate === "number") {
1372
+ return resolveNaturalDurationSeconds(sourceDuration, mediaStart, playbackRate);
1373
+ }
1374
+ if (!Number.isFinite(sourceDuration)) return null;
1375
+ return timeAtSourceTime(playbackRate, Math.max(0, sourceDuration - mediaStart));
1376
+ }
1377
+
1378
+ // ../core/src/mediaTiming.ts
1379
+ var MEDIA_START_BASIS_ATTR = "data-hf-media-start-basis";
1380
+ function readMediaStartBasis(value) {
1381
+ return value?.trim().toLowerCase() === "global" ? "global" : "local";
1382
+ }
1383
+ function resolveAbsoluteMediaStartSeconds(input) {
1384
+ return readMediaStartBasis(input.basis) === "global" ? input.authoredStart : input.hostStart + input.authoredStart;
1385
+ }
1386
+ function resolveMediaStartSeconds(input) {
1387
+ if (!hasLiteralNestedStart(input)) return input.ordinaryStart();
1388
+ return resolveAbsoluteMediaStartSeconds({
1389
+ authoredStart: input.authoredStart,
1390
+ hostStart: input.hostStart,
1391
+ basis: input.basis
1392
+ });
1393
+ }
1394
+ function isRootGlobalMediaStart(input) {
1395
+ return hasLiteralNestedStart(input) && readMediaStartBasis(input.basis) === "global";
1396
+ }
1397
+ function hasLiteralNestedStart(input) {
1398
+ return !input.hasAutoStart && input.authoredStart != null && input.hostStart !== 0;
1399
+ }
1400
+ function hostInPointSeconds(host) {
1401
+ return readMediaOffsetSeconds((name) => host.getAttribute(name));
1402
+ }
1403
+ function compositionOriginSeconds(hostStart, host) {
1404
+ return hostStart - hostInPointSeconds(host);
1405
+ }
1406
+
1407
+ // ../core/src/runtime/startResolver.ts
1408
+ function createRuntimeStartTimeResolver(params) {
1409
+ const timelineRegistry = params.timelineRegistry ?? {};
1410
+ const includeAuthoredTimingAttrs = params.includeAuthoredTimingAttrs ?? false;
1411
+ const doc = params.documentRef ?? document;
1412
+ const startCache = /* @__PURE__ */ new WeakMap();
1413
+ const durationCache = /* @__PURE__ */ new WeakMap();
1414
+ const visiting = /* @__PURE__ */ new Set();
1415
+ const findReferenceTarget = (refId) => {
1416
+ const byId = doc.getElementById(refId);
1417
+ if (byId) return byId;
1418
+ return Array.from(doc.querySelectorAll("[data-composition-id]")).find(
1419
+ (element) => element.getAttribute("data-composition-id") === refId
1420
+ ) ?? null;
1421
+ };
1422
+ const authoredAttr = (element, name) => includeAuthoredTimingAttrs ? element.getAttribute(name) : null;
1423
+ const resolveAuthoredDuration = (element) => resolveAuthoredTimingWindow({
1424
+ start: 0,
1425
+ duration: element.getAttribute("data-duration"),
1426
+ authoredDuration: authoredAttr(element, "data-hf-authored-duration")
1427
+ })?.duration ?? resolveAuthoredTimingWindow({
1428
+ start: resolveStartForElementInternal(element, 0),
1429
+ end: element.getAttribute("data-end"),
1430
+ authoredEnd: authoredAttr(element, "data-hf-authored-end")
1431
+ })?.duration ?? null;
1432
+ const positive = (value) => value != null && Number.isFinite(value) && value > 0 ? value : null;
1433
+ const resolveRegisteredTimelineDuration = (element) => {
1434
+ const compositionId = element.getAttribute("data-composition-id");
1435
+ return compositionId ? positive(readTimelineDurationSeconds(timelineRegistry[compositionId])) : null;
1436
+ };
1437
+ const resolveDurationForElement = (element) => {
1438
+ const cached = durationCache.get(element);
1439
+ if (cached !== void 0) return cached;
1440
+ const resolved = resolveAuthoredDuration(element) ?? positive(
1441
+ isMediaElement(element) ? resolveNaturalMediaTimelineDuration(element, element.duration) : null
1442
+ ) ?? positive(resolveTimedImageDurationSeconds(element)) ?? resolveRegisteredTimelineDuration(element);
1443
+ durationCache.set(element, resolved);
1444
+ return resolved;
1445
+ };
1446
+ const isCompositionHost = (element) => element.hasAttribute("data-composition-src") || element.hasAttribute("data-composition-id") || element.hasAttribute("data-composition-file");
1447
+ const inheritedTimingHost = (element) => {
1448
+ const parent = element.parentElement;
1449
+ if (!parent || !element.hasAttribute("data-composition-id")) return null;
1450
+ if (parseStartExpression(element.getAttribute("data-start"))) return null;
1451
+ return isCompositionHost(parent) ? parent : null;
1452
+ };
1453
+ const resolveCompositionOrigin = (compositionRoot, fallback) => compositionOriginSeconds(
1454
+ resolveStartForElementInternal(compositionRoot, fallback),
1455
+ inheritedTimingHost(compositionRoot) ?? compositionRoot
1456
+ );
1457
+ const resolveHostOffsetForElement = (element, fallback) => {
1458
+ const compositionRoot = element.hasAttribute("data-composition-id") ? element.parentElement?.closest("[data-composition-id]") : element.closest("[data-composition-id]");
1459
+ return compositionRoot ? resolveCompositionOrigin(compositionRoot, fallback) : 0;
1460
+ };
1461
+ const computeStart = (element, fallback) => {
1462
+ const expression = parseStartExpression(element.getAttribute("data-start"));
1463
+ if (!expression) {
1464
+ const host = inheritedTimingHost(element);
1465
+ return host ? resolveStartForElementInternal(host, fallback) : fallback;
1466
+ }
1467
+ if (expression.kind === "absolute") {
1468
+ return resolveHostOffsetForElement(element, fallback) + Math.max(0, expression.value);
1469
+ }
1470
+ const target = findReferenceTarget(expression.refId);
1471
+ if (!target) return fallback;
1472
+ const targetStart = resolveStartForElementInternal(target, 0);
1473
+ const targetDuration = resolveDurationForElement(target) ?? 0;
1474
+ const floor = Math.min(0, resolveHostOffsetForElement(element, 0));
1475
+ return Math.max(floor, targetStart + targetDuration + expression.offset);
1476
+ };
1477
+ const resolveStartForElementInternal = (element, fallback) => {
1478
+ const cached = startCache.get(element);
1479
+ if (cached !== void 0) {
1480
+ return cached == null ? fallback : cached;
1481
+ }
1482
+ if (visiting.has(element)) {
1483
+ return fallback;
1484
+ }
1485
+ visiting.add(element);
1486
+ try {
1487
+ const resolved = computeStart(element, fallback);
1488
+ startCache.set(element, resolved);
1489
+ return resolved;
1490
+ } finally {
1491
+ visiting.delete(element);
1492
+ }
1493
+ };
1494
+ const mediaStartInput = (element) => {
1495
+ const compositionRoot = element.closest("[data-composition-id]");
1496
+ return {
1497
+ authoredStart: parseStrictFiniteTimingNumber(element.getAttribute("data-start")),
1498
+ hostStart: compositionRoot ? resolveCompositionOrigin(compositionRoot, 0) : 0,
1499
+ hasAutoStart: element.hasAttribute("data-hf-auto-start"),
1500
+ basis: element.getAttribute(MEDIA_START_BASIS_ATTR)
1501
+ };
1502
+ };
1503
+ const resolveMediaStartForElement = (element) => {
1504
+ const input = mediaStartInput(element);
1505
+ return resolveMediaStartSeconds({
1506
+ ...input,
1507
+ ordinaryStart: () => resolveStartForElementInternal(element, input.hostStart)
1508
+ });
1509
+ };
1510
+ const resolveHostSlotsForElement = (element) => {
1511
+ const slots = [];
1512
+ for (let node = element.parentElement; node; node = node.parentElement) {
1513
+ if (!isCompositionHost(node) || inheritedTimingHost(node)) continue;
1514
+ if (hostInPointSeconds(node) <= 0) continue;
1515
+ const start = resolveStartForElementInternal(node, 0);
1516
+ const duration = resolveAuthoredDuration(node);
1517
+ slots.push({ start, end: duration ? start + duration : Infinity });
1518
+ }
1519
+ return slots;
1520
+ };
1521
+ const isRootGlobalMediaStartForElement = (element) => isMediaElement(element) && isRootGlobalMediaStart(mediaStartInput(element));
1522
+ return {
1523
+ resolveStartForElement: (element, fallback = 0) => resolveStartForElementInternal(element, Math.max(0, fallback)),
1524
+ resolveDurationForElement: (element) => resolveDurationForElement(element),
1525
+ resolveMediaStartForElement,
1526
+ resolveHostStartForElement: (element) => resolveHostOffsetForElement(element, 0),
1527
+ resolveHostSlotsForElement,
1528
+ isRootGlobalMediaStartForElement
1529
+ };
1530
+ }
1531
+
1532
+ // ../core/src/runtime/compositionDimension.ts
1533
+ function findRootCompositionElement(doc = document) {
1534
+ const explicitRoot = doc.querySelector('[data-composition-id][data-root="true"]');
1535
+ if (isHtmlElement(explicitRoot)) return explicitRoot;
1536
+ return doc.querySelector("[data-composition-id]");
1537
+ }
1538
+
1539
+ // ../core/src/runtime/timelineInspection.ts
1540
+ function classify(interval, q) {
1541
+ const { start, end } = interval;
1542
+ const near = (a, b) => Math.abs(a - b) <= 1e-7;
1543
+ const before = (a, b) => a < b && !near(a, b);
1544
+ const beforeOrAt = (a, b) => a < b || near(a, b);
1545
+ if (q.boundary != null) {
1546
+ const t = q.boundary;
1547
+ if (near(start, end) && near(start, t)) return "instant";
1548
+ if (near(start, t)) return "starts";
1549
+ if (near(end, t)) return "ends";
1550
+ return start < t && end > t ? "crosses" : null;
1551
+ }
1552
+ if (q.at != null)
1553
+ return near(start, end) ? near(start, q.at) ? "instant" : null : beforeOrAt(start, q.at) && before(q.at, end) ? "active" : null;
1554
+ if (q.range)
1555
+ return near(start, end) ? beforeOrAt(q.range[0], start) && before(start, q.range[1]) ? "instant" : null : before(start, q.range[1]) && before(q.range[0], end) ? "intersects" : null;
1556
+ return near(start, end) ? "instant" : "scheduled";
1557
+ }
1558
+ function cycles(a, input, warnings) {
1559
+ const d = a.duration(), repeat = a.repeat?.() ?? 0, gap = a.repeatDelay?.() ?? 0;
1560
+ if (repeat < 0 || repeat > 1e3) {
1561
+ warnings.add("Infinite or >1000 repeats are not expanded; timing incomplete.");
1562
+ return [];
1563
+ }
1564
+ const result = [];
1565
+ for (let cycle = 0; cycle <= repeat; cycle++)
1566
+ for (const interval of input) {
1567
+ const start = Math.max(0, interval.start), end = Math.min(d, interval.end);
1568
+ if (end < start) continue;
1569
+ const reverse = !!a.yoyo?.() && cycle % 2 === 1;
1570
+ result.push({
1571
+ start: cycle * (d + gap) + (reverse ? d - end : start),
1572
+ end: cycle * (d + gap) + (reverse ? d - start : end)
1573
+ });
1574
+ if (result.length >= 1e4) {
1575
+ warnings.add("Occurrence expansion limit reached; timing incomplete.");
1576
+ return result;
1577
+ }
1578
+ }
1579
+ return result;
1580
+ }
1581
+ function globalIntervals(a, root, warnings, activity = [{ start: 0, end: a.duration() }]) {
1582
+ let intervals = cycles(a, activity, warnings);
1583
+ let node = a;
1584
+ const seen = /* @__PURE__ */ new Set();
1585
+ while (node !== root) {
1586
+ if (seen.has(node) || !node.parent) {
1587
+ warnings.add("Animation is not attached to a registered root; global time unknown.");
1588
+ return [];
1589
+ }
1590
+ seen.add(node);
1591
+ const scale = node.timeScale?.() ?? 1;
1592
+ if (node.paused?.() || !scale) {
1593
+ warnings.add("Paused or zero-speed nested animation excluded; activation is unknown.");
1594
+ return [];
1595
+ }
1596
+ const total = node.totalDuration?.() ?? node.duration();
1597
+ intervals = intervals.map((i) => {
1598
+ const x = node.startTime() + (scale < 0 ? (total - i.end) / -scale : i.start / scale);
1599
+ const y = node.startTime() + (scale < 0 ? (total - i.start) / -scale : i.end / scale);
1600
+ return { start: x, end: y };
1601
+ });
1602
+ node = node.parent;
1603
+ intervals = cycles(node, intervals, warnings);
1604
+ }
1605
+ return intervals;
1606
+ }
1607
+ function inspectTimelineRuntime(q, roots, scope, describe, origin, reserved2, registry) {
1608
+ const warnings = /* @__PURE__ */ new Set();
1609
+ const events = [];
1610
+ const seen = /* @__PURE__ */ new Set();
1611
+ const resolver = createRuntimeStartTimeResolver({
1612
+ timelineRegistry: registry,
1613
+ documentRef: scope.ownerDocument
1614
+ });
1615
+ const rootElement = findRootCompositionElement(scope.ownerDocument);
1616
+ const clocks = /* @__PURE__ */ new Map();
1617
+ for (const [id, timeline] of Object.entries(registry)) {
1618
+ const host = scope.ownerDocument.querySelector(`[data-composition-id="${CSS.escape(id)}"]`);
1619
+ if (host) clocks.set(timeline, host);
1620
+ }
1621
+ const clockFor = (animation2, fallback) => {
1622
+ const visited = /* @__PURE__ */ new Set();
1623
+ let node = animation2;
1624
+ while (node && !visited.has(node)) {
1625
+ if (clocks.has(node)) return node;
1626
+ visited.add(node);
1627
+ node = node.parent;
1628
+ }
1629
+ return fallback;
1630
+ };
1631
+ const toProjectTime = (clock, intervals) => {
1632
+ const host = clocks.get(clock);
1633
+ if (!host || host === rootElement) return intervals;
1634
+ const start = resolver.resolveStartForElement(host, 0);
1635
+ const offset = readMediaStart(host);
1636
+ const rate = readElementRateSpec(host);
1637
+ if (!Number.isFinite(start)) {
1638
+ warnings.add("Composition start could not be resolved; timing incomplete.");
1639
+ return [];
1640
+ }
1641
+ return intervals.filter((interval) => interval.end >= offset).map((interval) => ({
1642
+ start: start + timeAtSourceTime(rate, Math.max(0, interval.start - offset)),
1643
+ end: start + timeAtSourceTime(rate, interval.end - offset)
1644
+ }));
1645
+ };
1646
+ const component = (el) => describe(q.component ? scope : el.closest("[data-composition-id]") ?? scope);
1647
+ for (const root of roots)
1648
+ for (const a of root.getChildren?.(true, true, false) ?? []) {
1649
+ if (events.length >= 1e4) {
1650
+ warnings.add("Global event limit reached; narrow component/time query. Timing incomplete.");
1651
+ break;
1652
+ }
1653
+ if (seen.has(a)) continue;
1654
+ seen.add(a);
1655
+ const clock = clockFor(a, root);
1656
+ const internal = a.timeline;
1657
+ if (internal && a._ease && [0, 0.25, 0.5, 0.75, 1].some((t) => Math.abs(a._ease(t) - t) > 1e-7)) {
1658
+ warnings.add("Nonlinear internal timeline easing is not mapped; timing incomplete.");
1659
+ continue;
1660
+ }
1661
+ const pieces = internal?.getChildren?.(true, true, false) ?? [a];
1662
+ for (const piece of pieces) {
1663
+ const targets = (piece.targets?.() ?? []).filter(isElementNode);
1664
+ if (!targets.some((el) => scope === el || scope.contains(el))) continue;
1665
+ const properties = Object.keys(piece.vars ?? {}).filter(
1666
+ (k) => !reserved2.has(k) && !k.startsWith("on")
1667
+ );
1668
+ const values = animationValues(piece.vars ?? {}, properties);
1669
+ const localIntervals = internal ? globalIntervals(
1670
+ a,
1671
+ clock,
1672
+ warnings,
1673
+ globalIntervals(piece, internal, warnings).map((i) => {
1674
+ const scale = internal.duration() ? a.duration() / internal.duration() : 1;
1675
+ return { start: i.start * scale, end: i.end * scale };
1676
+ })
1677
+ ) : globalIntervals(a, clock, warnings);
1678
+ const intervals = toProjectTime(clock, localIntervals);
1679
+ for (const interval of intervals) {
1680
+ if (events.length >= 1e4) {
1681
+ warnings.add("Global event limit reached; timing incomplete.");
1682
+ break;
1683
+ }
1684
+ const relation = classify(interval, q);
1685
+ if (!relation) continue;
1686
+ for (const owner of new Set(
1687
+ targets.filter((el) => scope === el || scope.contains(el)).map((el) => q.component ? scope : el.closest("[data-composition-id]") ?? scope)
1688
+ )) {
1689
+ const stack = origin(a);
1690
+ const existing = events.find(
1691
+ (e) => e.kind === "animation" && e.stack === stack && stack !== null && e.start === interval.start && e.end === interval.end && e.component?.ref === describe(owner).ref && e.properties.join() === properties.join() && JSON.stringify(e.values) === JSON.stringify(values)
1692
+ );
1693
+ if (existing) {
1694
+ for (const target of targets.map(describe))
1695
+ if (!existing.targets.some((t) => t.ref === target.ref))
1696
+ existing.targets.push(target);
1697
+ } else
1698
+ events.push({
1699
+ ...interval,
1700
+ kind: "animation",
1701
+ relation,
1702
+ component: describe(owner),
1703
+ targets: targets.map(describe),
1704
+ properties,
1705
+ values,
1706
+ stack
1707
+ });
1708
+ }
1709
+ }
1710
+ }
1711
+ }
1712
+ for (const el of [
1713
+ scope,
1714
+ ...scope.querySelectorAll("[data-start],[data-duration],[data-end],[data-composition-id]")
1715
+ ]) {
1716
+ if (!el.matches("[data-start],[data-duration],[data-end],[data-composition-id]")) continue;
1717
+ const media = el.tagName === "AUDIO" || el.tagName === "VIDEO";
1718
+ const start = media ? resolver.resolveMediaStartForElement(el) : resolver.resolveStartForElement(el, Number.NaN);
1719
+ const duration = resolver.resolveDurationForElement(el);
1720
+ if (!Number.isFinite(start) || duration == null || !Number.isFinite(duration) || duration < 0) {
1721
+ warnings.add("Some HF item timing could not be resolved.");
1722
+ continue;
1723
+ }
1724
+ const interval = { start, end: start + duration };
1725
+ const relation = classify(interval, q);
1726
+ if (!relation) continue;
1727
+ events.push({
1728
+ ...interval,
1729
+ kind: "item",
1730
+ relation,
1731
+ component: component(el),
1732
+ targets: [describe(el)],
1733
+ properties: [],
1734
+ stack: null
1735
+ });
1736
+ }
1737
+ events.sort((a, b) => a.start - b.start || a.end - b.end);
1738
+ return { status: "found", candidates: [describe(scope)], events, warnings: [...warnings] };
1739
+ }
1740
+
1741
+ // ../core/src/runtime/inspection.ts
1742
+ var animation = (value) => !!value && typeof value === "object" && "duration" in value && typeof value.duration === "function" && "startTime" in value && typeof value.startTime === "function";
1743
+ var reserved = /* @__PURE__ */ new Set([
1744
+ "duration",
1745
+ "ease",
1746
+ "delay",
1747
+ "repeat",
1748
+ "repeatDelay",
1749
+ "yoyo",
1750
+ "stagger",
1751
+ "paused",
1752
+ "immediateRender",
1753
+ "overwrite",
1754
+ "lazy",
1755
+ "parent",
1756
+ "data",
1757
+ "id",
1758
+ "inherit",
1759
+ "runBackwards",
1760
+ "startAt",
1761
+ "keyframes",
1762
+ "reversed",
1763
+ "defaults"
1764
+ ]);
1765
+ function installInspection(win, generation, limit = 2e4) {
1766
+ const records = /* @__PURE__ */ new WeakMap();
1767
+ const attachments = /* @__PURE__ */ new WeakMap();
1768
+ const refs = /* @__PURE__ */ new Map();
1769
+ const elementRefs = /* @__PURE__ */ new WeakMap();
1770
+ const restorers = [];
1771
+ const hooked = /* @__PURE__ */ new WeakSet();
1772
+ let count = 0;
1773
+ let depth = 0;
1774
+ let gsap;
1775
+ let disposed = false;
1776
+ const ref = (el) => {
1777
+ let value2 = elementRefs.get(el);
1778
+ if (!value2) {
1779
+ value2 = `hfi:${generation}:${refs.size + 1}`;
1780
+ refs.set(value2, new WeakRef(el));
1781
+ elementRefs.set(el, value2);
1782
+ }
1783
+ return value2;
1784
+ };
1785
+ const describe = (el) => ({
1786
+ ref: ref(el),
1787
+ tag: el.tagName.toLowerCase(),
1788
+ id: el.id,
1789
+ hfId: el.getAttribute("data-hf-id"),
1790
+ classes: [...el.classList],
1791
+ composition: el.closest("[data-composition-id]")?.getAttribute("data-composition-id") ?? null
1792
+ });
1793
+ function record(value2, stack) {
1794
+ if (animation(value2) && !records.has(value2) && count < limit) {
1795
+ records.set(value2, stack);
1796
+ count++;
1797
+ }
1798
+ }
1799
+ function wrap(owner, name, timeline) {
1800
+ const original = owner[name];
1801
+ if (typeof original !== "function") return;
1802
+ const method = original;
1803
+ const wrapped = function(...args) {
1804
+ const outer = depth === 0;
1805
+ const stack = outer ? new Error().stack ?? "" : "";
1806
+ const self = animation(this) ? this : void 0;
1807
+ const before = outer && timeline ? new Set(self?.getChildren?.(false, true, true) ?? []) : void 0;
1808
+ depth++;
1809
+ let result;
1810
+ try {
1811
+ result = method.apply(this, args);
1812
+ } finally {
1813
+ depth--;
1814
+ }
1815
+ if (outer) {
1816
+ if (name === "add" && animation(args[0])) attachments.set(args[0], stack);
1817
+ else if (before) {
1818
+ for (const child of self?.getChildren?.(false, true, true) ?? [])
1819
+ if (!before.has(child)) record(child, stack);
1820
+ } else record(result, stack);
1821
+ }
1822
+ return result;
1823
+ };
1824
+ owner[name] = wrapped;
1825
+ restorers.push(() => {
1826
+ if (owner[name] === wrapped) owner[name] = original;
1827
+ });
1828
+ }
1829
+ function hook(value2) {
1830
+ if (disposed || hooked.has(value2) || !value2.core?.Timeline || !value2.getTweensOf) return;
1831
+ hooked.add(value2);
1832
+ gsap = value2;
1833
+ for (const name of ["to", "from", "fromTo", "set", "timeline", "delayedCall"])
1834
+ wrap(value2, name, false);
1835
+ for (const name of ["to", "from", "fromTo", "set", "call", "add"])
1836
+ wrap(value2.core.Timeline.prototype, name, true);
1837
+ }
1838
+ const descriptor = Object.getOwnPropertyDescriptor(win, "gsap");
1839
+ let value = win.gsap;
1840
+ if (value) hook(value);
1841
+ if (!descriptor || descriptor.configurable) {
1842
+ Object.defineProperty(win, "gsap", {
1843
+ configurable: true,
1844
+ enumerable: true,
1845
+ get: () => value,
1846
+ set: (next) => {
1847
+ value = next;
1848
+ hook(next);
1849
+ }
1850
+ });
1851
+ restorers.push(() => {
1852
+ if (descriptor)
1853
+ Object.defineProperty(win, "gsap", { ...descriptor, ...descriptor.get ? {} : { value } });
1854
+ else
1855
+ Object.defineProperty(win, "gsap", {
1856
+ configurable: true,
1857
+ enumerable: true,
1858
+ writable: true,
1859
+ value
1860
+ });
1861
+ });
1862
+ }
1863
+ const api = {
1864
+ query(q) {
1865
+ if (value) hook(value);
1866
+ const results = q.targets.map((target) => {
1867
+ const base = {
1868
+ target,
1869
+ status: "missing",
1870
+ candidates: [],
1871
+ elements: [],
1872
+ animations: [],
1873
+ css: [],
1874
+ truncated: false
1875
+ };
1876
+ let elements;
1877
+ try {
1878
+ if ("ref" in target) {
1879
+ const el = refs.get(target.ref)?.deref();
1880
+ if (!el?.isConnected) return { ...base, status: "stale-ref" };
1881
+ elements = [el];
1882
+ } else {
1883
+ const roots = target.within ? [...document.querySelectorAll(target.within)] : [document];
1884
+ const selector = "selector" in target ? target.selector : `[data-hf-id="${CSS.escape(target.hfId)}"]`;
1885
+ elements = [
1886
+ ...new Set(
1887
+ roots.flatMap((root) => [
1888
+ ...isElementNode(root) && root.matches(selector) ? [root] : [],
1889
+ ...root.querySelectorAll(selector)
1890
+ ])
1891
+ )
1892
+ ];
1893
+ }
1894
+ } catch {
1895
+ return { ...base, status: "invalid-selector" };
1896
+ }
1897
+ if (elements.length === 0)
1898
+ return {
1899
+ ...base,
1900
+ status: "missing",
1901
+ candidates: elements.slice(0, 20).map(describe),
1902
+ truncated: elements.length > 20
1903
+ };
1904
+ const selected = /* @__PURE__ */ new Set();
1905
+ let truncated = false;
1906
+ const add = (el) => {
1907
+ if (selected.has(el)) return;
1908
+ if (selected.size >= 1001) {
1909
+ truncated = true;
1910
+ return;
1911
+ }
1912
+ selected.add(el);
1913
+ };
1914
+ for (const element of elements) {
1915
+ add(element);
1916
+ if (q.descendants) {
1917
+ for (const child of element.querySelectorAll("*")) {
1918
+ add(child);
1919
+ if (truncated) break;
1920
+ }
1921
+ }
1922
+ if (truncated) break;
1923
+ }
1924
+ const scope = [...selected];
1925
+ const animations = (gsap?.getTweensOf?.(scope) ?? []).map((tw) => {
1926
+ const attachmentStacks = [];
1927
+ const visited = /* @__PURE__ */ new Set();
1928
+ for (let parent = tw; parent && !visited.has(parent); parent = parent.parent) {
1929
+ visited.add(parent);
1930
+ const stack = attachments.get(parent);
1931
+ if (stack) attachmentStacks.push(stack);
1932
+ }
1933
+ return {
1934
+ stack: records.get(tw) ?? null,
1935
+ properties: Object.keys(tw.vars ?? {}).filter(
1936
+ (key) => !reserved.has(key) && !key.startsWith("on")
1937
+ ),
1938
+ targets: (tw.targets?.() ?? []).filter(isElementNode).slice(0, 50).map(describe),
1939
+ localStart: tw.startTime(),
1940
+ duration: tw.duration(),
1941
+ attachmentStacks
1942
+ };
1943
+ });
1944
+ const css = q.cssRules.filter((rule) => {
1945
+ try {
1946
+ return scope.some((el) => el.matches(rule.selector));
1947
+ } catch {
1948
+ return false;
1949
+ }
1950
+ }).map((rule) => rule.id);
1951
+ return {
1952
+ ...base,
1953
+ status: "found",
1954
+ candidates: elements.slice(0, 1001).map(describe),
1955
+ elements: scope.map(describe),
1956
+ animations,
1957
+ css,
1958
+ truncated
1959
+ };
1960
+ });
1961
+ return { results, recorded: count, recordLimitReached: count >= limit, hooked: !!gsap };
1962
+ },
1963
+ timeline(q) {
1964
+ let scope = document.documentElement;
1965
+ if (q.component) {
1966
+ const found = api.query({ targets: [q.component], descendants: false, cssRules: [] }).results[0];
1967
+ if (!found) return { status: "missing", candidates: [], events: [], warnings: [] };
1968
+ if (found.status !== "found")
1969
+ return { status: found.status, candidates: found.candidates, events: [], warnings: [] };
1970
+ const [only, ...others] = found.candidates;
1971
+ if (!only || others.length > 0)
1972
+ return { status: "ambiguous", candidates: found.candidates, events: [], warnings: [] };
1973
+ const el = refs.get(only.ref)?.deref();
1974
+ if (!el) return { status: "stale-ref", candidates: [], events: [], warnings: [] };
1975
+ scope = el;
1976
+ }
1977
+ const registered = Object.values(win.__timelines ?? {});
1978
+ const roots = registered.filter(
1979
+ (root) => !registered.some(
1980
+ (other) => other !== root && (other.getChildren?.(true, false, true) ?? []).includes(root)
1981
+ )
1982
+ );
1983
+ return inspectTimelineRuntime(
1984
+ q,
1985
+ roots,
1986
+ scope,
1987
+ describe,
1988
+ (a) => records.get(a) ?? null,
1989
+ reserved,
1990
+ win.__timelines ?? {}
1991
+ );
1992
+ },
1993
+ dispose() {
1994
+ disposed = true;
1995
+ for (const restore of restorers.reverse()) restore();
1996
+ refs.clear();
1997
+ delete win.__hfInspection;
1998
+ }
1999
+ };
2000
+ win.__hfInspection = api;
2001
+ return api;
2002
+ }
2003
+
2004
+ // src/liveRuntime.ts
2005
+ var live = window.__hfLiveInspection;
2006
+ installInspection(window, live.generation);
2007
+ })();