gitframes 1.2.35

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 (94) hide show
  1. package/LICENCE +202 -0
  2. package/README.md +77 -0
  3. package/dist/audio/index.d.mts +2 -0
  4. package/dist/audio/index.mjs +6 -0
  5. package/dist/audio-C0YslYEG.d.mts +32 -0
  6. package/dist/audio-C0YslYEG.d.mts.map +1 -0
  7. package/dist/audio-C4kiPbrV.mjs +1866 -0
  8. package/dist/audio-C4kiPbrV.mjs.map +1 -0
  9. package/dist/audio-CiXUMGyk.mjs +578 -0
  10. package/dist/audio-CiXUMGyk.mjs.map +1 -0
  11. package/dist/chunk-GMkBE123.mjs +47 -0
  12. package/dist/client-Dyypr8tt.mjs +66 -0
  13. package/dist/client-Dyypr8tt.mjs.map +1 -0
  14. package/dist/dist-C37594c6.mjs +27195 -0
  15. package/dist/dist-C37594c6.mjs.map +1 -0
  16. package/dist/dist-CMF9eNLB.mjs +4062 -0
  17. package/dist/dist-CMF9eNLB.mjs.map +1 -0
  18. package/dist/dist-cjs--KIl7Gqy.mjs +6 -0
  19. package/dist/dist-cjs-B-_4TVjP.mjs +66 -0
  20. package/dist/dist-cjs-B-_4TVjP.mjs.map +1 -0
  21. package/dist/dist-cjs-B3TIyZ3K.mjs +437 -0
  22. package/dist/dist-cjs-B3TIyZ3K.mjs.map +1 -0
  23. package/dist/dist-cjs-Bsz2eGzl.mjs +9069 -0
  24. package/dist/dist-cjs-Bsz2eGzl.mjs.map +1 -0
  25. package/dist/dist-cjs-BuJ2FXob.mjs +181 -0
  26. package/dist/dist-cjs-BuJ2FXob.mjs.map +1 -0
  27. package/dist/dist-cjs-C-FMsqmo.mjs +1091 -0
  28. package/dist/dist-cjs-C-FMsqmo.mjs.map +1 -0
  29. package/dist/dist-cjs-C4b9TCXd.mjs +48 -0
  30. package/dist/dist-cjs-C4b9TCXd.mjs.map +1 -0
  31. package/dist/dist-cjs-C817PNLc.mjs +51 -0
  32. package/dist/dist-cjs-C817PNLc.mjs.map +1 -0
  33. package/dist/dist-cjs-CBnnS4k8.mjs +43 -0
  34. package/dist/dist-cjs-CBnnS4k8.mjs.map +1 -0
  35. package/dist/dist-cjs-CWbeD0Mu.mjs +64 -0
  36. package/dist/dist-cjs-CWbeD0Mu.mjs.map +1 -0
  37. package/dist/dist-cjs-ChXEJ1ro.mjs +85 -0
  38. package/dist/dist-cjs-ChXEJ1ro.mjs.map +1 -0
  39. package/dist/dist-cjs-CslF8Dju.mjs +153 -0
  40. package/dist/dist-cjs-CslF8Dju.mjs.map +1 -0
  41. package/dist/dist-cjs-D52jYyEm.mjs +98 -0
  42. package/dist/dist-cjs-D52jYyEm.mjs.map +1 -0
  43. package/dist/dist-cjs-DhWYgNfa.mjs +209 -0
  44. package/dist/dist-cjs-DhWYgNfa.mjs.map +1 -0
  45. package/dist/dist-cjs-DssYqayv.mjs +5015 -0
  46. package/dist/dist-cjs-DssYqayv.mjs.map +1 -0
  47. package/dist/dist-cjs-Du6DnSjo.mjs +137 -0
  48. package/dist/dist-cjs-Du6DnSjo.mjs.map +1 -0
  49. package/dist/dist-cjs-b-oBQaxe.mjs +323 -0
  50. package/dist/dist-cjs-b-oBQaxe.mjs.map +1 -0
  51. package/dist/effects/index.d.mts +2 -0
  52. package/dist/effects/index.mjs +4 -0
  53. package/dist/effects-4t4qk-BR.mjs +21130 -0
  54. package/dist/effects-4t4qk-BR.mjs.map +1 -0
  55. package/dist/event-streams-B0YUBtyM.mjs +204 -0
  56. package/dist/event-streams-B0YUBtyM.mjs.map +1 -0
  57. package/dist/fonts/index.d.mts +2 -0
  58. package/dist/fonts/index.mjs +3 -0
  59. package/dist/fonts-Dn18r_ST.mjs +188 -0
  60. package/dist/fonts-Dn18r_ST.mjs.map +1 -0
  61. package/dist/index-B1557AUi.d.mts +928 -0
  62. package/dist/index-B1557AUi.d.mts.map +1 -0
  63. package/dist/index-DSaMXGoA.d.mts +65 -0
  64. package/dist/index-DSaMXGoA.d.mts.map +1 -0
  65. package/dist/index-FICUHo4P.d.mts +69 -0
  66. package/dist/index-FICUHo4P.d.mts.map +1 -0
  67. package/dist/index-YZsTjqut.d.mts +147 -0
  68. package/dist/index-YZsTjqut.d.mts.map +1 -0
  69. package/dist/index.d.mts +736 -0
  70. package/dist/index.d.mts.map +1 -0
  71. package/dist/index.mjs +7135 -0
  72. package/dist/index.mjs.map +1 -0
  73. package/dist/package-5RsA21g-.mjs +6 -0
  74. package/dist/package-5RsA21g-.mjs.map +1 -0
  75. package/dist/react/index.d.mts +3 -0
  76. package/dist/react/index.mjs +4 -0
  77. package/dist/renderer/index.d.mts +2 -0
  78. package/dist/renderer/index.mjs +6 -0
  79. package/dist/renderer-DVi31fSv.mjs +23274 -0
  80. package/dist/renderer-DVi31fSv.mjs.map +1 -0
  81. package/dist/renderers-CLJQRPVU-CMZnHNK0.mjs +49 -0
  82. package/dist/renderers-CLJQRPVU-CMZnHNK0.mjs.map +1 -0
  83. package/dist/signals/index.d.mts +9 -0
  84. package/dist/signals/index.mjs +8 -0
  85. package/dist/signals-BB_kdndF.mjs +227 -0
  86. package/dist/signals-BB_kdndF.mjs.map +1 -0
  87. package/dist/signin-CVUAIuED.mjs +865 -0
  88. package/dist/signin-CVUAIuED.mjs.map +1 -0
  89. package/dist/sso-oidc-D1GnNQYW.mjs +1051 -0
  90. package/dist/sso-oidc-D1GnNQYW.mjs.map +1 -0
  91. package/dist/sts-Bz-e3kNW.mjs +6 -0
  92. package/dist/sts-I4KhuCBs.mjs +1395 -0
  93. package/dist/sts-I4KhuCBs.mjs.map +1 -0
  94. package/package.json +174 -0
@@ -0,0 +1,4062 @@
1
+ import { A as drawCaptionNode, C as SlugGeometry, F as drawParagraphNode, I as drawSignalNode, J as lutStore, L as drawSvgNode, M as drawGifNode, N as drawImageNode, O as clearVideoNodeState, P as drawLottieNode, R as drawVideoNode, S as SlugFontCache, W as inputStore, Y as mediaDecoderCache, at as GetFontAssetUrl, ct as resolveMediaSourceUrl, et as registerHeadlessFont, it as useRenderContext, nt as signalRegistry, st as getEnv, tt as shaderStore } from "./dist-C37594c6.mjs";
2
+ import { nanoid } from "nanoid";
3
+ import z$1, { z } from "zod";
4
+ import { AudioSample, AudioSampleSink, AudioSampleSource, Mp3OutputFormat, Mp4OutputFormat, Output, QUALITY_VERY_HIGH, VideoSample, VideoSampleSource, WebMOutputFormat, canEncodeAudio } from "mediabunny";
5
+ import React, { createContext, memo, useCallback, useContext, useEffect, useMemo, useRef, useState, useSyncExternalStore } from "react";
6
+ import { Fragment, jsx, jsxs } from "react/jsx-runtime";
7
+ import { registerMp3Encoder } from "@mediabunny/mp3-encoder";
8
+
9
+ //#region ../core/dist/index.mjs
10
+ var ProgrammaticSignal = class ProgrammaticSignal$1 {
11
+ gpuBinding;
12
+ label;
13
+ _evaluator;
14
+ _samples;
15
+ _fps;
16
+ _durationFrames;
17
+ _cachedFrame = -1;
18
+ _cachedValue = 0;
19
+ _seekedFrame = -1;
20
+ _subscribers = /* @__PURE__ */ new Set();
21
+ _children = /* @__PURE__ */ new Set();
22
+ get fps() {
23
+ return this._fps;
24
+ }
25
+ constructor(source, options = {}) {
26
+ this._fps = options.fps ?? 24;
27
+ this._durationFrames = options.durationFrames;
28
+ this.gpuBinding = options.gpuBinding;
29
+ this.label = options.label;
30
+ if (typeof source === "number") {
31
+ const constVal = source;
32
+ this._evaluator = () => constVal;
33
+ this._cachedValue = constVal;
34
+ } else if (typeof source === "function") this._evaluator = source;
35
+ else if (isSignal(source)) this._evaluator = (ctx) => Number(source.get(ctx)) || 0;
36
+ else {
37
+ const arr = source;
38
+ this._samples = arr instanceof Float32Array ? arr : new Float32Array(Array.from(arr));
39
+ this._durationFrames = this._samples.length;
40
+ this._cachedValue = this._samples.length > 0 ? this._samples[0] : 0;
41
+ this._evaluator = (ctx) => {
42
+ if (!this._samples || this._samples.length === 0) return 0;
43
+ const compFps = ctx.fps ?? this._fps;
44
+ const targetFrame = compFps !== this._fps && compFps > 0 ? ctx.frame / compFps * this._fps : ctx.frame;
45
+ const idx = Math.max(0, Math.min(this._samples.length - 1, Math.round(targetFrame)));
46
+ return this._samples[idx];
47
+ };
48
+ }
49
+ }
50
+ get value() {
51
+ return this.get();
52
+ }
53
+ get(ctx) {
54
+ registerSignalDependency(this._subscribers);
55
+ frameSignal.get();
56
+ const currentFrame = ctx?.frame ?? (this._seekedFrame >= 0 ? this._seekedFrame : frameSignal.value);
57
+ if (this._cachedFrame === currentFrame) return this._cachedValue;
58
+ const fps = ctx?.fps ?? this._fps;
59
+ const time = ctx?.time ?? (fps > 0 ? currentFrame / fps : 0);
60
+ const duration = ctx?.duration ?? this._durationFrames ?? 100;
61
+ const resolvedCtx = {
62
+ frame: currentFrame,
63
+ fps,
64
+ time,
65
+ duration,
66
+ durationMs: ctx?.durationMs ?? duration / fps * 1e3,
67
+ progress: duration > 0 ? currentFrame / duration : 0,
68
+ deltaTime: ctx?.deltaTime ?? (fps > 0 ? 1 / fps : .0416)
69
+ };
70
+ this._cachedFrame = currentFrame;
71
+ this._cachedValue = this._evaluator(resolvedCtx);
72
+ return this._cachedValue;
73
+ }
74
+ peek() {
75
+ return this._cachedValue;
76
+ }
77
+ registerChild(child) {
78
+ this._children.add(child);
79
+ }
80
+ _derive(evaluator, options, other) {
81
+ const child = new ProgrammaticSignal$1(evaluator, {
82
+ fps: this._fps,
83
+ durationFrames: this._durationFrames,
84
+ ...options
85
+ });
86
+ this._children.add(child);
87
+ if (other instanceof ProgrammaticSignal$1) other.registerChild(child);
88
+ return child;
89
+ }
90
+ seekFrame(frame) {
91
+ this._seekedFrame = frame;
92
+ this._cachedFrame = -1;
93
+ for (const sub of this._subscribers) sub.markDirty();
94
+ for (const child of this._children) child.seekFrame(frame);
95
+ }
96
+ invalidate() {
97
+ this._cachedFrame = -1;
98
+ for (const sub of this._subscribers) sub.markDirty();
99
+ for (const child of this._children) child.invalidate();
100
+ }
101
+ valueOf() {
102
+ return this.get();
103
+ }
104
+ [Symbol.toPrimitive](hint) {
105
+ if (hint === "number") return Number(this.get());
106
+ if (hint === "string") return String(this.get());
107
+ return this.get();
108
+ }
109
+ smooth(windowFrames) {
110
+ const window$1 = Math.max(1, Math.round(windowFrames));
111
+ if (window$1 <= 1) return this;
112
+ return this._derive((ctx) => {
113
+ let sum = 0;
114
+ let count = 0;
115
+ const startF = Math.max(0, ctx.frame - window$1 + 1);
116
+ for (let f = startF; f <= ctx.frame; f++) {
117
+ sum += this.get({
118
+ ...ctx,
119
+ frame: f,
120
+ time: f / ctx.fps
121
+ });
122
+ count++;
123
+ }
124
+ return count > 0 ? sum / count : 0;
125
+ }, { label: `${this.label ?? "sig"}_smoothed(${window$1})` });
126
+ }
127
+ remap(inMin, inMax, outMin, outMax, clamp = false) {
128
+ const inRange = inMax - inMin;
129
+ const outRange = outMax - outMin;
130
+ return this._derive((ctx) => {
131
+ const val = this.get(ctx);
132
+ const remapped = outMin + (inRange !== 0 ? (val - inMin) / inRange : 0) * outRange;
133
+ if (clamp) {
134
+ const lo = Math.min(outMin, outMax);
135
+ const hi = Math.max(outMin, outMax);
136
+ return Math.max(lo, Math.min(hi, remapped));
137
+ }
138
+ return remapped;
139
+ }, { label: `${this.label ?? "sig"}_remap` });
140
+ }
141
+ clamp(min, max) {
142
+ const lo = Math.min(min, max);
143
+ const hi = Math.max(min, max);
144
+ return this._derive((ctx) => Math.max(lo, Math.min(hi, this.get(ctx))), { label: `${this.label ?? "sig"}_clamped` });
145
+ }
146
+ gate(threshold, options = {}) {
147
+ const mode = options.mode ?? "gate";
148
+ const debounce = Math.max(1, options.debounceFrames ?? 2);
149
+ const hold = Math.max(1, options.holdFrames ?? 4);
150
+ const invert = Boolean(options.invert);
151
+ return this._derive((ctx) => {
152
+ const currentF = ctx.frame;
153
+ let out = 0;
154
+ if (mode === "gate") out = this.get(ctx) >= threshold ? 1 : 0;
155
+ else if (mode === "trigger") {
156
+ let lastTrigger = -Infinity;
157
+ const startF = Math.max(0, currentF - 120);
158
+ for (let f = startF; f <= currentF; f++) {
159
+ const v = this.get({
160
+ ...ctx,
161
+ frame: f,
162
+ time: f / ctx.fps
163
+ });
164
+ const prev = f > 0 ? this.get({
165
+ ...ctx,
166
+ frame: f - 1,
167
+ time: (f - 1) / ctx.fps
168
+ }) : 0;
169
+ if (v >= threshold && prev < threshold && f - lastTrigger >= debounce) lastTrigger = f;
170
+ }
171
+ out = currentF >= lastTrigger && currentF < lastTrigger + hold ? 1 : 0;
172
+ } else if (mode === "toggle") {
173
+ let toggleCount = 0;
174
+ let lastTrigger = -Infinity;
175
+ for (let f = 0; f <= currentF; f++) {
176
+ const v = this.get({
177
+ ...ctx,
178
+ frame: f,
179
+ time: f / ctx.fps
180
+ });
181
+ const prev = f > 0 ? this.get({
182
+ ...ctx,
183
+ frame: f - 1,
184
+ time: (f - 1) / ctx.fps
185
+ }) : 0;
186
+ if (v >= threshold && prev < threshold && f - lastTrigger >= debounce) {
187
+ toggleCount++;
188
+ lastTrigger = f;
189
+ }
190
+ }
191
+ out = toggleCount % 2 === 1 ? 1 : 0;
192
+ }
193
+ return invert ? 1 - out : out;
194
+ }, { label: `${this.label ?? "sig"}_gate(${threshold})` });
195
+ }
196
+ velocity(dt = 1) {
197
+ const step = Math.max(1, Math.round(dt));
198
+ return this._derive((ctx) => {
199
+ const curr = this.get(ctx);
200
+ const prevF = Math.max(0, ctx.frame - step);
201
+ const prev = this.get({
202
+ ...ctx,
203
+ frame: prevF,
204
+ time: prevF / ctx.fps
205
+ });
206
+ const deltaSec = step / ctx.fps;
207
+ return deltaSec > 0 ? (curr - prev) / deltaSec : curr - prev;
208
+ }, { label: `${this.label ?? "sig"}_velocity` });
209
+ }
210
+ delay(frames) {
211
+ const shift = Math.max(0, Math.round(frames));
212
+ return this._derive((ctx) => {
213
+ const delayedF = Math.max(0, ctx.frame - shift);
214
+ return this.get({
215
+ ...ctx,
216
+ frame: delayedF,
217
+ time: delayedF / ctx.fps
218
+ });
219
+ }, { label: `${this.label ?? "sig"}_delay(${shift})` });
220
+ }
221
+ spring(stiffness = 150, damping = 12) {
222
+ return this._derive((ctx) => {
223
+ const targetFrame = ctx.frame;
224
+ if (targetFrame <= 0) return this.get(ctx);
225
+ let pos = this.get({
226
+ ...ctx,
227
+ frame: 0,
228
+ time: 0
229
+ });
230
+ let vel = 0;
231
+ const dt = 1 / Math.max(1, ctx.fps);
232
+ for (let f = 1; f <= targetFrame; f++) {
233
+ const targetVal = this.get({
234
+ ...ctx,
235
+ frame: f,
236
+ time: f / ctx.fps
237
+ });
238
+ const displacement = pos - targetVal;
239
+ const force = -stiffness * displacement - damping * vel;
240
+ vel += force * dt;
241
+ pos += vel * dt;
242
+ }
243
+ return pos;
244
+ }, { label: `${this.label ?? "sig"}_spring` });
245
+ }
246
+ add(other) {
247
+ if (typeof other === "number") return this._derive((ctx) => this.get(ctx) + other);
248
+ return this._derive((ctx) => this.get(ctx) + (Number(other.get(ctx)) || 0), void 0, other);
249
+ }
250
+ subtract(other) {
251
+ if (typeof other === "number") return this._derive((ctx) => this.get(ctx) - other);
252
+ return this._derive((ctx) => this.get(ctx) - (Number(other.get(ctx)) || 0), void 0, other);
253
+ }
254
+ multiply(factor) {
255
+ if (typeof factor === "number") return this._derive((ctx) => this.get(ctx) * factor);
256
+ return this._derive((ctx) => this.get(ctx) * (Number(factor.get(ctx)) || 0), void 0, factor);
257
+ }
258
+ invert() {
259
+ return this._derive((ctx) => 1 - this.get(ctx), { label: `${this.label ?? "sig"}_inverted` });
260
+ }
261
+ pow(exponent) {
262
+ return this._derive((ctx) => this.get(ctx) ** exponent);
263
+ }
264
+ map(fn) {
265
+ return this._derive((ctx) => fn(this.get(ctx), ctx));
266
+ }
267
+ toFrameArray(totalFrames, fps) {
268
+ const count = Math.max(1, totalFrames);
269
+ const f = fps ?? this._fps;
270
+ const out = new Float32Array(count);
271
+ for (let i = 0; i < count; i++) out[i] = this.get({
272
+ frame: i,
273
+ fps: f,
274
+ time: i / f,
275
+ duration: count,
276
+ durationMs: count / f * 1e3,
277
+ progress: i / count,
278
+ deltaTime: 1 / f
279
+ });
280
+ return out;
281
+ }
282
+ };
283
+ function programmaticSignal(source, options) {
284
+ return new ProgrammaticSignal(source, options);
285
+ }
286
+ let activeComputed = null;
287
+ function registerSignalDependency(subscribers) {
288
+ if (activeComputed !== null) {
289
+ subscribers.add(activeComputed);
290
+ activeComputed.deps.add(subscribers);
291
+ }
292
+ }
293
+ function isSignal(val) {
294
+ return val !== null && typeof val === "object" && "get" in val && typeof val.get === "function" && "value" in val;
295
+ }
296
+ var StateSignalImpl = class {
297
+ _value;
298
+ _subscribers = /* @__PURE__ */ new Set();
299
+ constructor(initialValue) {
300
+ this._value = initialValue;
301
+ }
302
+ get value() {
303
+ return this.get();
304
+ }
305
+ set value(nextValue) {
306
+ this.set(nextValue);
307
+ }
308
+ get() {
309
+ if (activeComputed !== null) {
310
+ this._subscribers.add(activeComputed);
311
+ activeComputed.deps.add(this._subscribers);
312
+ }
313
+ return this._value;
314
+ }
315
+ peek() {
316
+ return this._value;
317
+ }
318
+ set(nextValue) {
319
+ if (Object.is(this._value, nextValue)) return;
320
+ this._value = nextValue;
321
+ this.notifySubscribers();
322
+ }
323
+ update(fn) {
324
+ this.set(fn(this._value));
325
+ }
326
+ notifySubscribers() {
327
+ if (this._subscribers.size === 0) return;
328
+ const toNotify = Array.from(this._subscribers);
329
+ for (const sub of toNotify) sub.markDirty();
330
+ }
331
+ valueOf() {
332
+ return this.get();
333
+ }
334
+ [Symbol.toPrimitive](hint) {
335
+ if (hint === "number") return Number(this.get());
336
+ if (hint === "string") return String(this.get());
337
+ return this.get();
338
+ }
339
+ };
340
+ var ComputedImpl = class {
341
+ _fn;
342
+ _value;
343
+ _dirty = true;
344
+ _evaluating = false;
345
+ deps = /* @__PURE__ */ new Set();
346
+ _subscribers = /* @__PURE__ */ new Set();
347
+ constructor(fn) {
348
+ this._fn = fn;
349
+ }
350
+ get value() {
351
+ return this.get();
352
+ }
353
+ get() {
354
+ if (activeComputed !== null && activeComputed !== this) {
355
+ this._subscribers.add(activeComputed);
356
+ activeComputed.deps.add(this._subscribers);
357
+ }
358
+ if (this._dirty) this.compute();
359
+ return this._value;
360
+ }
361
+ peek() {
362
+ if (this._dirty) this.compute();
363
+ return this._value;
364
+ }
365
+ markDirty() {
366
+ if (this._dirty) return;
367
+ this._dirty = true;
368
+ const toNotify = Array.from(this._subscribers);
369
+ for (const sub of toNotify) sub.markDirty();
370
+ }
371
+ compute() {
372
+ if (this._evaluating) throw new Error("Cyclic dependency detected in computed signal");
373
+ for (const depSubSet of this.deps) depSubSet.delete(this);
374
+ this.deps.clear();
375
+ const prevActive = activeComputed;
376
+ activeComputed = this;
377
+ this._evaluating = true;
378
+ try {
379
+ this._value = this._fn();
380
+ this._dirty = false;
381
+ } finally {
382
+ this._evaluating = false;
383
+ activeComputed = prevActive;
384
+ }
385
+ }
386
+ valueOf() {
387
+ return this.get();
388
+ }
389
+ [Symbol.toPrimitive](hint) {
390
+ if (hint === "number") return Number(this.get());
391
+ if (hint === "string") return String(this.get());
392
+ return this.get();
393
+ }
394
+ };
395
+ var FrameArraySignalImpl = class {
396
+ gpuBinding;
397
+ fps;
398
+ _samples;
399
+ _cachedFrame = -1;
400
+ _cachedValue = 0;
401
+ _seekedFrame = -1;
402
+ _subscribers = /* @__PURE__ */ new Set();
403
+ constructor(samples, fps = 24, gpuBinding) {
404
+ this._samples = samples;
405
+ this.fps = fps;
406
+ this.gpuBinding = gpuBinding;
407
+ if (samples.length > 0) this._cachedValue = samples[0];
408
+ }
409
+ get value() {
410
+ return this.get();
411
+ }
412
+ get(ctx) {
413
+ if (activeComputed !== null) {
414
+ this._subscribers.add(activeComputed);
415
+ activeComputed.deps.add(this._subscribers);
416
+ }
417
+ frameSignal.get();
418
+ const currentFrame = ctx?.frame ?? (this._seekedFrame >= 0 ? this._seekedFrame : frameSignal.value);
419
+ if (this._cachedFrame === currentFrame) return this._cachedValue;
420
+ this._cachedFrame = currentFrame;
421
+ if (!this._samples || this._samples.length === 0) {
422
+ this._cachedValue = 0;
423
+ return 0;
424
+ }
425
+ const compFps = ctx?.fps ?? this.fps;
426
+ const targetFrame = compFps !== this.fps && compFps > 0 ? currentFrame / compFps * this.fps : currentFrame;
427
+ const idx = Math.max(0, Math.min(this._samples.length - 1, Math.round(targetFrame)));
428
+ this._cachedValue = Number(this._samples[idx]) || 0;
429
+ return this._cachedValue;
430
+ }
431
+ peek() {
432
+ return this._cachedValue;
433
+ }
434
+ seekFrame(frame) {
435
+ this._seekedFrame = frame;
436
+ this._cachedFrame = frame;
437
+ if (!this._samples || this._samples.length === 0) this._cachedValue = 0;
438
+ else {
439
+ const idx = Math.max(0, Math.min(this._samples.length - 1, Math.round(frame)));
440
+ this._cachedValue = Number(this._samples[idx]) || 0;
441
+ }
442
+ for (const sub of this._subscribers) sub.markDirty();
443
+ }
444
+ valueOf() {
445
+ return this.get();
446
+ }
447
+ [Symbol.toPrimitive](hint) {
448
+ if (hint === "number") return Number(this.get());
449
+ if (hint === "string") return String(this.get());
450
+ return this.get();
451
+ }
452
+ };
453
+ function signal(initialValue) {
454
+ return new StateSignalImpl(initialValue);
455
+ }
456
+ function computed(fn) {
457
+ return new ComputedImpl(fn);
458
+ }
459
+ function frameArraySignal(samples, fps = 24, gpuBinding) {
460
+ return new FrameArraySignalImpl(samples, fps, gpuBinding);
461
+ }
462
+ const frameSignal = signal(0);
463
+ const timeSignal = signal(0);
464
+ const progressSignal = signal(0);
465
+ function updateClockSignals(frame, fps = 24, durationMs) {
466
+ frameSignal.set(frame);
467
+ const timeSec = fps > 0 ? frame / fps : 0;
468
+ timeSignal.set(timeSec);
469
+ if (durationMs && durationMs > 0) {
470
+ const durSec = durationMs / 1e3;
471
+ progressSignal.set(Math.max(0, Math.min(1, timeSec / durSec)));
472
+ } else progressSignal.set(0);
473
+ }
474
+ var ProceduralSignalImpl = class {
475
+ gpuBinding;
476
+ _config;
477
+ constructor(config) {
478
+ this._config = {
479
+ type: "sine",
480
+ frequency: 1,
481
+ amplitude: 1,
482
+ offset: 0,
483
+ phase: 0,
484
+ ...config
485
+ };
486
+ this.gpuBinding = config.gpuBinding;
487
+ }
488
+ get value() {
489
+ return this.get();
490
+ }
491
+ get(ctx) {
492
+ frameSignal.get();
493
+ const frame = ctx?.frame ?? frameSignal.value;
494
+ const fps = ctx?.fps ?? 24;
495
+ const time = ctx?.time ?? (fps > 0 ? frame / fps : 0);
496
+ if (this._config.fn) return this._config.fn(ctx ?? {
497
+ frame,
498
+ fps,
499
+ time,
500
+ duration: 0,
501
+ durationMs: 0,
502
+ progress: 0,
503
+ deltaTime: fps > 0 ? 1 / fps : 0
504
+ });
505
+ const freq = this._config.frequency ?? 1;
506
+ const amp = this._config.amplitude ?? 1;
507
+ const off = this._config.offset ?? 0;
508
+ const phase = this._config.phase ?? 0;
509
+ switch (this._config.type ?? "sine") {
510
+ case "sine": return off + amp * Math.sin(2 * Math.PI * freq * time + phase);
511
+ case "triangle": return off + amp * (2 * Math.abs(2 * (time * freq + phase / (2 * Math.PI) - Math.floor(time * freq + phase / (2 * Math.PI) + .5))) - 1);
512
+ case "sawtooth": return off + amp * (2 * (time * freq + phase / (2 * Math.PI) - Math.floor(time * freq + phase / (2 * Math.PI) + .5)));
513
+ case "square": return off + (Math.sin(2 * Math.PI * freq * time + phase) >= 0 ? amp : -amp);
514
+ case "bounce": {
515
+ const cycle = time * freq % 1;
516
+ return off + amp * Math.abs(Math.sin(Math.PI * cycle));
517
+ }
518
+ default: return off + amp * Math.sin(2 * Math.PI * freq * time + phase);
519
+ }
520
+ }
521
+ peek() {
522
+ return this.get();
523
+ }
524
+ valueOf() {
525
+ return this.get();
526
+ }
527
+ [Symbol.toPrimitive](hint) {
528
+ if (hint === "number") return Number(this.get());
529
+ if (hint === "string") return String(this.get());
530
+ return this.get();
531
+ }
532
+ };
533
+ function signalBuilder(config) {
534
+ return new ProceduralSignalImpl(config);
535
+ }
536
+ function sineSignal(options = {}) {
537
+ const min = options.min;
538
+ const max = options.max;
539
+ let amplitude = options.amplitude ?? 1;
540
+ let offset = options.offset ?? 0;
541
+ if (min !== void 0 && max !== void 0) {
542
+ offset = (min + max) / 2;
543
+ amplitude = (max - min) / 2;
544
+ } else if (min !== void 0) offset = min + amplitude;
545
+ else if (max !== void 0) offset = max - amplitude;
546
+ const period = options.period;
547
+ const frequency = options.frequency;
548
+ const phase = options.phase ?? 0;
549
+ return signalBuilder({
550
+ type: "custom",
551
+ fn: (ctx) => {
552
+ let angle;
553
+ if (period !== void 0 && period > 0) angle = 2 * Math.PI * ctx.frame / period + phase;
554
+ else {
555
+ const freq = frequency ?? 1;
556
+ angle = 2 * Math.PI * freq * ctx.time + phase;
557
+ }
558
+ return offset + amplitude * Math.sin(angle);
559
+ }
560
+ });
561
+ }
562
+ function cosineSignal(options = {}) {
563
+ const min = options.min;
564
+ const max = options.max;
565
+ let amplitude = options.amplitude ?? 1;
566
+ let offset = options.offset ?? 0;
567
+ if (min !== void 0 && max !== void 0) {
568
+ offset = (min + max) / 2;
569
+ amplitude = (max - min) / 2;
570
+ } else if (min !== void 0) offset = min + amplitude;
571
+ else if (max !== void 0) offset = max - amplitude;
572
+ const period = options.period;
573
+ const frequency = options.frequency;
574
+ const phase = options.phase ?? 0;
575
+ return signalBuilder({
576
+ type: "custom",
577
+ fn: (ctx) => {
578
+ let angle;
579
+ if (period !== void 0 && period > 0) angle = 2 * Math.PI * ctx.frame / period + phase;
580
+ else {
581
+ const freq = frequency ?? 1;
582
+ angle = 2 * Math.PI * freq * ctx.time + phase;
583
+ }
584
+ return offset + amplitude * Math.cos(angle);
585
+ }
586
+ });
587
+ }
588
+ const Signal = {
589
+ state: signal,
590
+ computed,
591
+ builder: signalBuilder,
592
+ fromArray: frameArraySignal,
593
+ programmatic: programmaticSignal,
594
+ frame: frameSignal,
595
+ time: timeSignal,
596
+ progress: progressSignal,
597
+ sine: sineSignal,
598
+ cosine: cosineSignal
599
+ };
600
+ var Effect = class {
601
+ signals;
602
+ config;
603
+ _frameHooks = [];
604
+ constructor(initialConfig) {
605
+ this.config = { ...initialConfig };
606
+ this.signals = {};
607
+ for (const [key, val] of Object.entries(initialConfig)) if (isSignal(val)) this.signals[key] = val;
608
+ else this.signals[key] = signal(val);
609
+ return new Proxy(this, {
610
+ get(target, prop, receiver) {
611
+ if (typeof prop === "string" && prop in target.signals) {
612
+ const sig = target.signals[prop];
613
+ return sig ? sig.value : void 0;
614
+ }
615
+ return Reflect.get(target, prop, receiver);
616
+ },
617
+ set(target, prop, val, receiver) {
618
+ if (typeof prop === "string" && prop in target.signals) {
619
+ const currentSig = target.signals[prop];
620
+ if (isSignal(val)) {
621
+ target.signals[prop] = val;
622
+ target.config[prop] = val;
623
+ } else if (currentSig && typeof currentSig.set === "function") {
624
+ currentSig.set(val);
625
+ target.config[prop] = val;
626
+ } else {
627
+ target.signals[prop] = signal(val);
628
+ target.config[prop] = val;
629
+ }
630
+ return true;
631
+ }
632
+ return Reflect.set(target, prop, val, receiver);
633
+ }
634
+ });
635
+ }
636
+ /**
637
+ * Game-engine per-frame lifecycle hook.
638
+ * Invoked before rendering each frame so scripts or agents can mutate properties.
639
+ */
640
+ onRequestFrame(callback) {
641
+ this._frameHooks.push(callback);
642
+ return this;
643
+ }
644
+ notifyFrame(ctx) {
645
+ for (const hook of this._frameHooks) hook(ctx);
646
+ }
647
+ /**
648
+ * Samples concrete scalar / config values for the current frame.
649
+ */
650
+ resolveUniforms(ctx) {
651
+ const out = {};
652
+ for (const [k, sig] of Object.entries(this.signals)) out[k] = sig.get(ctx);
653
+ return out;
654
+ }
655
+ /**
656
+ * Returns WebGPU texture bindings attached to any property signal.
657
+ */
658
+ resolveTextures() {
659
+ const out = {};
660
+ for (const [k, sig] of Object.entries(this.signals)) {
661
+ const gpu = sig.gpuBinding;
662
+ if (gpu?.textureView) out[`${k}TextureView`] = gpu.textureView;
663
+ if (gpu?.texture) out[`${k}Texture`] = gpu.texture;
664
+ }
665
+ return out;
666
+ }
667
+ /**
668
+ * Converts the effect into an operation node for the VirtualMediaData AST.
669
+ */
670
+ toOperation() {
671
+ const opPayload = {
672
+ op: this.op,
673
+ effect: this,
674
+ onRequestFrame: (ctx) => this.notifyFrame(ctx),
675
+ signals: this.signals,
676
+ ...this.config
677
+ };
678
+ for (const [k, sig] of Object.entries(this.signals)) {
679
+ const orig = this.config[k];
680
+ if (!isSignal(orig) && (typeof orig === "string" || typeof orig === "boolean")) opPayload[k] = sig.value;
681
+ else opPayload[k] = sig;
682
+ }
683
+ return opPayload;
684
+ }
685
+ };
686
+ var ModerationError = class extends Error {
687
+ constructor(message) {
688
+ super(message);
689
+ this.name = "ModerationError";
690
+ }
691
+ };
692
+ var ModerationUnrefundableError = class extends Error {
693
+ constructor(message) {
694
+ super(message);
695
+ this.name = "ModerationUnrefundableError";
696
+ }
697
+ };
698
+ /**
699
+ * Generates a unique ID using nanoid.
700
+ * This function is cross-platform compatible (Node.js and Browser).
701
+ *
702
+ * @param size - Optional size of the ID (default is 21)
703
+ * @returns A unique string ID
704
+ */
705
+ const generateId = (size) => {
706
+ return nanoid(size);
707
+ };
708
+ const HandleDefinitionSchema = z$1.object({
709
+ dataTypes: z$1.custom(),
710
+ label: z$1.string(),
711
+ required: z$1.boolean().optional(),
712
+ order: z$1.number(),
713
+ description: z$1.string().optional()
714
+ });
715
+ const ConfigHandleSchema = z$1.object({
716
+ configKey: z$1.string(),
717
+ dataTypes: z$1.custom(),
718
+ label: z$1.string(),
719
+ description: z$1.string().optional()
720
+ });
721
+ /**
722
+ * Metadata defining the interface and identity of a node.
723
+ * This is safe to import in any environment.
724
+ */
725
+ const NodeMetadataSchema = z$1.object({
726
+ type: z$1.string().min(1),
727
+ version: z$1.number().int().positive().optional(),
728
+ baseType: z$1.string().optional(),
729
+ displayName: z$1.string().min(1),
730
+ description: z$1.string().optional(),
731
+ showInQuickAccess: z$1.boolean().optional(),
732
+ showInSidebar: z$1.boolean().optional(),
733
+ advanced: z$1.boolean().optional(),
734
+ category: z$1.string().min(1),
735
+ subcategory: z$1.string().optional(),
736
+ defaultDimensions: z$1.object({
737
+ width: z$1.number().positive().optional(),
738
+ height: z$1.number().positive().optional()
739
+ }).optional(),
740
+ handles: z$1.object({
741
+ inputs: z$1.array(HandleDefinitionSchema),
742
+ outputs: z$1.array(HandleDefinitionSchema)
743
+ }),
744
+ variableInputs: z$1.discriminatedUnion("enabled", [z$1.object({
745
+ enabled: z$1.literal(true),
746
+ dataTypes: z$1.custom()
747
+ }), z$1.object({
748
+ enabled: z$1.literal(false),
749
+ dataTypes: z$1.custom().optional()
750
+ })]).optional(),
751
+ variableOutputs: z$1.discriminatedUnion("enabled", [z$1.object({
752
+ enabled: z$1.literal(true),
753
+ dataTypes: z$1.custom()
754
+ }), z$1.object({
755
+ enabled: z$1.literal(false),
756
+ dataTypes: z$1.custom().optional()
757
+ })]).optional(),
758
+ isTerminal: z$1.boolean().optional(),
759
+ isTransient: z$1.boolean().optional(),
760
+ configSchema: z$1.custom().optional(),
761
+ defaultConfig: z$1.record(z$1.string(), z$1.unknown()).optional(),
762
+ validation: z$1.any().optional(),
763
+ resultSchema: z$1.custom().optional(),
764
+ configHandles: z$1.array(ConfigHandleSchema).optional()
765
+ });
766
+ const DataTypeVal = {
767
+ Text: "Text",
768
+ Number: "Number",
769
+ Boolean: "Boolean",
770
+ Image: "Image",
771
+ Video: "Video",
772
+ Audio: "Audio",
773
+ SVG: "SVG",
774
+ Caption: "Caption",
775
+ Lottie: "Lottie",
776
+ ThreeD: "ThreeD",
777
+ GIF: "GIF",
778
+ Signal: "Signal",
779
+ LUT: "LUT"
780
+ };
781
+ const DataType = DataTypeVal;
782
+ const DataTypes = Object.values(DataTypeVal);
783
+ const HandleTypeVal = {
784
+ Input: "Input",
785
+ Output: "Output"
786
+ };
787
+ const JsonValueSchema = z$1.lazy(() => z$1.union([
788
+ z$1.string(),
789
+ z$1.number(),
790
+ z$1.boolean(),
791
+ z$1.null(),
792
+ z$1.array(JsonValueSchema),
793
+ z$1.record(z$1.string(), JsonValueSchema.optional())
794
+ ]));
795
+ /**
796
+ * Zod schema for the FileAsset model
797
+ * Matches the Prisma schema definitions for types and nullability.
798
+ */
799
+ const FileAssetSchema = z$1.object({
800
+ id: z$1.cuid2(),
801
+ name: z$1.string(),
802
+ createdAt: z$1.coerce.date(),
803
+ updatedAt: z$1.coerce.date(),
804
+ userId: z$1.cuid2().nullable(),
805
+ width: z$1.number().int().nullable(),
806
+ height: z$1.number().int().nullable(),
807
+ bucket: z$1.string(),
808
+ size: z$1.number().int(),
809
+ mimeType: z$1.string(),
810
+ key: z$1.string(),
811
+ isUploaded: z$1.boolean().default(true),
812
+ duration: z$1.number().int().nullable(),
813
+ sampleRate: z$1.number().int().nullable().optional(),
814
+ channels: z$1.number().int().nullable().optional(),
815
+ bitDepth: z$1.number().int().nullable().optional(),
816
+ audioCodec: z$1.string().nullable().optional(),
817
+ audioBitrate: z$1.number().int().nullable().optional(),
818
+ fps: z$1.number().int().nullable(),
819
+ fingerprint: z$1.string().nullable()
820
+ });
821
+ const NodeVisualConfigSchema = z$1.object({
822
+ position: z$1.object({
823
+ x: z$1.number(),
824
+ y: z$1.number()
825
+ }).optional(),
826
+ width: z$1.number().nullish(),
827
+ height: z$1.number().nullish(),
828
+ locked: z$1.boolean().optional().default(false),
829
+ collapsed: z$1.boolean().optional().default(false)
830
+ }).passthrough();
831
+ const dataTypeColors = {
832
+ Text: {
833
+ bg: "bg-blue-500",
834
+ stroke: "stroke-blue-500",
835
+ hex: "#3b82f6",
836
+ text: "text-blue-500",
837
+ border: "border-blue-500"
838
+ },
839
+ Image: {
840
+ bg: "bg-purple-500",
841
+ stroke: "stroke-purple-500",
842
+ hex: "#a855f7",
843
+ text: "text-purple-500",
844
+ border: "border-purple-500"
845
+ },
846
+ Video: {
847
+ bg: "bg-teal-500",
848
+ stroke: "stroke-teal-500",
849
+ hex: "oklch(60% 0.118 184.704)",
850
+ text: "text-teal-500",
851
+ border: "border-teal-500"
852
+ },
853
+ Audio: {
854
+ bg: "bg-orange-500",
855
+ stroke: "stroke-orange-500",
856
+ hex: "#f97316",
857
+ text: "text-orange-500",
858
+ border: "border-orange-500"
859
+ },
860
+ Number: {
861
+ bg: "bg-green-600",
862
+ stroke: "stroke-green-600",
863
+ hex: "#16a34a",
864
+ text: "text-green-600",
865
+ border: "border-green-600"
866
+ },
867
+ Boolean: {
868
+ bg: "bg-amber-600",
869
+ stroke: "stroke-amber-600",
870
+ hex: "#d97706",
871
+ text: "text-amber-600",
872
+ border: "border-amber-600"
873
+ },
874
+ SVG: {
875
+ bg: "bg-lime-600",
876
+ stroke: "stroke-lime-600",
877
+ hex: "#65a30d",
878
+ text: "text-lime-600",
879
+ border: "border-lime-600"
880
+ },
881
+ Caption: {
882
+ bg: "bg-rose-500",
883
+ stroke: "stroke-rose-500",
884
+ hex: "#f43f5e",
885
+ text: "text-rose-500",
886
+ border: "border-rose-500"
887
+ },
888
+ GIF: {
889
+ bg: "bg-pink-500",
890
+ stroke: "stroke-pink-500",
891
+ hex: "#ec4899",
892
+ text: "text-pink-500",
893
+ border: "border-pink-500"
894
+ },
895
+ Lottie: {
896
+ bg: "bg-indigo-500",
897
+ stroke: "stroke-indigo-500",
898
+ hex: "#6366f1",
899
+ text: "text-indigo-500",
900
+ border: "border-indigo-500"
901
+ },
902
+ Signal: {
903
+ bg: "bg-fuchsia-500",
904
+ stroke: "stroke-fuchsia-500",
905
+ hex: "#d946ef",
906
+ text: "text-fuchsia-500",
907
+ border: "border-fuchsia-500"
908
+ },
909
+ LUT: {
910
+ bg: "bg-cyan-600",
911
+ stroke: "stroke-cyan-600",
912
+ hex: "#0891b2",
913
+ text: "text-cyan-600",
914
+ border: "border-cyan-600"
915
+ }
916
+ };
917
+ const DATA_TYPE_EXTENSIONS = {
918
+ Image: "png",
919
+ Video: "mp4",
920
+ Audio: "mp3",
921
+ Text: "txt",
922
+ Number: "txt",
923
+ Boolean: "txt",
924
+ SVG: "svg",
925
+ Caption: "srt",
926
+ Lottie: "json",
927
+ ThreeD: "glb",
928
+ GIF: "gif",
929
+ Signal: "txt",
930
+ LUT: "cube"
931
+ };
932
+ const MIME_TYPES = {
933
+ png: "image/png",
934
+ jpg: "image/jpeg",
935
+ jpeg: "image/jpeg",
936
+ gif: "image/gif",
937
+ webp: "image/webp",
938
+ svg: "image/svg+xml",
939
+ mp4: "video/mp4",
940
+ webm: "video/webm",
941
+ mov: "video/quicktime",
942
+ mp3: "audio/mpeg",
943
+ wav: "audio/wav",
944
+ ogg: "audio/ogg",
945
+ pdf: "application/pdf",
946
+ json: "application/json",
947
+ txt: "text/plain",
948
+ srt: "text/srt",
949
+ aac: "audio/aac",
950
+ flac: "audio/flac"
951
+ };
952
+ const COMPOSITE_OPERATIONS$1 = [
953
+ "source-over",
954
+ "source-in",
955
+ "source-out",
956
+ "source-atop",
957
+ "destination-over",
958
+ "destination-in",
959
+ "destination-out",
960
+ "destination-atop",
961
+ "lighter",
962
+ "copy",
963
+ "xor",
964
+ "multiply",
965
+ "screen",
966
+ "overlay",
967
+ "darken",
968
+ "lighten",
969
+ "color-dodge",
970
+ "color-burn",
971
+ "hard-light",
972
+ "soft-light",
973
+ "difference",
974
+ "exclusion",
975
+ "hue",
976
+ "saturation",
977
+ "color",
978
+ "luminosity"
979
+ ];
980
+ const GlobalCompositeOperation$1 = z.enum(COMPOSITE_OPERATIONS$1);
981
+ var ConfigSchemaRegistry = class ConfigSchemaRegistry$1 {
982
+ static schemas = /* @__PURE__ */ new Map();
983
+ static register(type, schema) {
984
+ ConfigSchemaRegistry$1.schemas.set(type, schema);
985
+ }
986
+ static get(type) {
987
+ return ConfigSchemaRegistry$1.schemas.get(type);
988
+ }
989
+ static clear() {
990
+ ConfigSchemaRegistry$1.schemas.clear();
991
+ }
992
+ };
993
+ const COMPOSITOR_DEFAULTS = {
994
+ FONT_FAMILY: "Inter",
995
+ FONT_SIZE: 64,
996
+ FILL: "#ffffff",
997
+ LINE_HEIGHT: 1.1,
998
+ ALIGN: "left",
999
+ VERTICAL_ALIGN: "top",
1000
+ LETTER_SPACING: 0,
1001
+ STROKE_WIDTH: 0,
1002
+ STROKE: "#000000",
1003
+ CORNER_RADIUS: 0,
1004
+ PADDING: 0
1005
+ };
1006
+ const FileDataSchema = z$1.object({ entity: z$1.custom().optional() });
1007
+ const SignalStatsSchema = z$1.object({
1008
+ min: z$1.number().optional(),
1009
+ max: z$1.number().optional(),
1010
+ mean: z$1.number().optional(),
1011
+ rms: z$1.number().optional(),
1012
+ peak: z$1.number().optional(),
1013
+ beats: z$1.array(z$1.number()).optional(),
1014
+ tempo: z$1.number().optional()
1015
+ });
1016
+ const SignalChannelMetaSchema = z$1.object({
1017
+ id: z$1.string(),
1018
+ name: z$1.string(),
1019
+ index: z$1.number(),
1020
+ stats: SignalStatsSchema.optional()
1021
+ });
1022
+ const GeneratorSignalDataSchema = z$1.object({
1023
+ v: z$1.literal(2).optional(),
1024
+ type: z$1.literal("generator"),
1025
+ nodeId: z$1.string().optional(),
1026
+ baseType: z$1.string().optional(),
1027
+ func: z$1.enum([
1028
+ "sine",
1029
+ "triangle",
1030
+ "sawtooth",
1031
+ "square",
1032
+ "custom"
1033
+ ]).or(z$1.string()).default("sine"),
1034
+ frequency: z$1.number().default(1),
1035
+ amplitude: z$1.number().default(1),
1036
+ phase: z$1.number().default(0),
1037
+ offset: z$1.number().default(0),
1038
+ waveform: z$1.string().optional(),
1039
+ bpm: z$1.number().optional(),
1040
+ syncToBpm: z$1.boolean().optional(),
1041
+ fmFrequency: z$1.number().optional(),
1042
+ fmDepth: z$1.number().optional(),
1043
+ envelope: z$1.string().optional(),
1044
+ fnBody: z$1.string().optional(),
1045
+ fnParams: z$1.array(z$1.record(z$1.string(), z$1.unknown())).optional(),
1046
+ customWGSL: z$1.string().optional(),
1047
+ signalFnName: z$1.string().optional(),
1048
+ outputType: z$1.string().optional(),
1049
+ fps: z$1.number().optional(),
1050
+ durationMs: z$1.number().optional(),
1051
+ stats: SignalStatsSchema.optional()
1052
+ }).passthrough();
1053
+ const AudioExtractorSignalDataSchema = z$1.object({
1054
+ v: z$1.literal(2).optional(),
1055
+ type: z$1.literal("audio_extractor"),
1056
+ nodeId: z$1.string().optional(),
1057
+ sourceUrl: z$1.string().optional(),
1058
+ channel: z$1.enum([
1059
+ "primary",
1060
+ "beat",
1061
+ "bass",
1062
+ "energy",
1063
+ "spectral_flux"
1064
+ ]).or(z$1.string()).optional(),
1065
+ channelIndex: z$1.number().optional(),
1066
+ channels: z$1.array(SignalChannelMetaSchema).optional(),
1067
+ extractionMode: z$1.string().optional(),
1068
+ attackMs: z$1.number().optional(),
1069
+ releaseMs: z$1.number().optional(),
1070
+ sensitivity: z$1.number().optional(),
1071
+ noiseFloorDb: z$1.number().optional(),
1072
+ dynamicRangeDb: z$1.number().optional(),
1073
+ autoRange: z$1.boolean().optional(),
1074
+ smoothing: z$1.number().optional(),
1075
+ curve: z$1.string().optional(),
1076
+ beatThreshold: z$1.number().optional(),
1077
+ beatDecayMs: z$1.number().optional(),
1078
+ previewMode: z$1.string().optional(),
1079
+ samples: z$1.union([z$1.array(z$1.number()), z$1.instanceof(Float32Array)]).optional(),
1080
+ fps: z$1.number().optional(),
1081
+ durationMs: z$1.number().optional(),
1082
+ stats: SignalStatsSchema.optional(),
1083
+ state: z$1.enum([
1084
+ "pending",
1085
+ "ready",
1086
+ "failed"
1087
+ ]).optional(),
1088
+ error: z$1.string().optional(),
1089
+ virtualMedia: z$1.record(z$1.string(), z$1.unknown()).optional(),
1090
+ signalFnName: z$1.string().optional(),
1091
+ customWGSL: z$1.string().optional()
1092
+ }).passthrough();
1093
+ const SignalMathDataSchema = z$1.object({
1094
+ v: z$1.literal(2).optional(),
1095
+ type: z$1.literal("signal_math"),
1096
+ nodeId: z$1.string().optional(),
1097
+ op: z$1.string().optional(),
1098
+ operation: z$1.string().optional(),
1099
+ signalA: z$1.unknown().optional(),
1100
+ signalB: z$1.unknown().optional(),
1101
+ bValue: z$1.number().optional(),
1102
+ inMin: z$1.number().optional(),
1103
+ inMax: z$1.number().optional(),
1104
+ outMin: z$1.number().optional(),
1105
+ outMax: z$1.number().optional(),
1106
+ clampMin: z$1.number().optional(),
1107
+ clampMax: z$1.number().optional(),
1108
+ exponent: z$1.number().optional(),
1109
+ edge0: z$1.number().optional(),
1110
+ edge1: z$1.number().optional(),
1111
+ customWGSL: z$1.string().optional(),
1112
+ stats: SignalStatsSchema.optional()
1113
+ }).passthrough();
1114
+ const SignalGateDataSchema = z$1.object({
1115
+ v: z$1.literal(2).optional(),
1116
+ type: z$1.literal("gate"),
1117
+ nodeId: z$1.string().optional(),
1118
+ sourceSignal: z$1.unknown().optional(),
1119
+ threshold: z$1.number().optional(),
1120
+ debounceMs: z$1.number().optional(),
1121
+ mode: z$1.enum([
1122
+ "gate",
1123
+ "trigger",
1124
+ "toggle"
1125
+ ]).optional(),
1126
+ holdFrames: z$1.number().optional(),
1127
+ invert: z$1.boolean().optional(),
1128
+ stats: SignalStatsSchema.optional()
1129
+ }).passthrough();
1130
+ const SignalDataSchema = z$1.union([
1131
+ GeneratorSignalDataSchema,
1132
+ AudioExtractorSignalDataSchema,
1133
+ SignalMathDataSchema,
1134
+ SignalGateDataSchema,
1135
+ z$1.object({ type: z$1.string() }).passthrough()
1136
+ ]);
1137
+ function serializeSignalData(data) {
1138
+ return JSON.stringify(data, (_key, value) => {
1139
+ if (value instanceof Float32Array) return Array.from(value);
1140
+ return value;
1141
+ });
1142
+ }
1143
+ function deserializeSignalData(raw) {
1144
+ if (typeof raw === "string") try {
1145
+ return SignalDataSchema.parse(JSON.parse(raw));
1146
+ } catch {
1147
+ return { type: "unknown" };
1148
+ }
1149
+ return SignalDataSchema.parse(raw);
1150
+ }
1151
+ const OutputItemSchema = z$1.object({
1152
+ type: z$1.custom(),
1153
+ data: z$1.any(),
1154
+ outputHandleId: z$1.string().optional()
1155
+ });
1156
+ const createOutputItemSchema = (type, dataSchema) => {
1157
+ return z$1.object({
1158
+ type,
1159
+ data: dataSchema,
1160
+ outputHandleId: z$1.string().optional()
1161
+ });
1162
+ };
1163
+ const SingleOutputGenericSchema = (outputItemSchema) => z$1.object({
1164
+ selectedOutputIndex: z$1.literal(0),
1165
+ outputs: z$1.tuple([z$1.object({ items: z$1.tuple([outputItemSchema]) })])
1166
+ });
1167
+ const MultiOutputGenericSchema = (outputItemSchema) => z$1.object({
1168
+ selectedOutputIndex: z$1.number(),
1169
+ outputs: z$1.array(z$1.object({ items: z$1.array(outputItemSchema) })),
1170
+ sourceFingerprint: z$1.string().optional()
1171
+ });
1172
+ const AnyOutputUnionSchema = z$1.object({
1173
+ type: z$1.custom(),
1174
+ data: z$1.any(),
1175
+ outputHandleId: z$1.string().optional()
1176
+ });
1177
+ const NodeResultSchema = z$1.object({
1178
+ selectedOutputIndex: z$1.number(),
1179
+ outputs: z$1.array(z$1.object({ items: z$1.array(AnyOutputUnionSchema) }))
1180
+ });
1181
+ const ExportResultSchema = MultiOutputGenericSchema(AnyOutputUnionSchema);
1182
+ const AnimationTypeSchema = z.enum([
1183
+ "fade-in",
1184
+ "fade-out",
1185
+ "slide-in-left",
1186
+ "slide-in-right",
1187
+ "slide-in-top",
1188
+ "slide-in-bottom",
1189
+ "zoom-in",
1190
+ "zoom-out",
1191
+ "rotate-cw",
1192
+ "rotate-ccw",
1193
+ "bounce",
1194
+ "shake"
1195
+ ]);
1196
+ const VideoAnimationSchema = z.object({
1197
+ id: z.string(),
1198
+ type: AnimationTypeSchema,
1199
+ value: z.number()
1200
+ });
1201
+ const VideoSpatialPropsSchema = z.object({
1202
+ x: z.number().default(0),
1203
+ y: z.number().default(0),
1204
+ width: z.number().optional(),
1205
+ height: z.number().optional(),
1206
+ rotation: z.number().default(0),
1207
+ scale: z.number().min(0).default(1),
1208
+ opacity: z.number().min(0).max(1).default(1),
1209
+ zIndex: z.number().int().optional(),
1210
+ blendMode: GlobalCompositeOperation$1.optional()
1211
+ });
1212
+ const VideoTimingPropsSchema = z.object({
1213
+ startFrame: z.number().int().min(0).default(0),
1214
+ durationInMS: z.number().int().min(1).optional(),
1215
+ trimStart: z.number().int().min(0).optional(),
1216
+ trimEnd: z.number().int().min(0).optional(),
1217
+ speed: z.number().min(.25).max(4).optional()
1218
+ });
1219
+ const VideoTextPropsSchema = z.object({
1220
+ text: z.string().optional(),
1221
+ fontSize: z.number().optional(),
1222
+ fontFamily: z.string().optional(),
1223
+ fontStyle: z.string().optional(),
1224
+ fontWeight: z.union([z.number(), z.string()]).optional(),
1225
+ fill: z.string().optional(),
1226
+ align: z.string().optional(),
1227
+ verticalAlign: z.string().optional(),
1228
+ letterSpacing: z.number().optional(),
1229
+ lineHeight: z.number().optional(),
1230
+ padding: z.number().optional(),
1231
+ stroke: z.string().optional(),
1232
+ strokeWidth: z.number().optional(),
1233
+ strokeAlign: z.enum([
1234
+ "inside",
1235
+ "center",
1236
+ "outside"
1237
+ ]).optional(),
1238
+ textShadow: z.string().optional(),
1239
+ textBackgroundColor: z.string().optional(),
1240
+ shadows: z.array(z.object({
1241
+ color: z.string(),
1242
+ offset: z.object({
1243
+ x: z.number(),
1244
+ y: z.number()
1245
+ }).optional(),
1246
+ blurRadius: z.number().optional()
1247
+ })).optional(),
1248
+ textAnimation: z.any().optional(),
1249
+ bottomPadding: z.number().optional(),
1250
+ maxWidth: z.number().optional()
1251
+ });
1252
+ const VideoStylePropsSchema = z.object({
1253
+ backgroundColor: z.string().optional(),
1254
+ borderColor: z.string().optional(),
1255
+ borderWidth: z.number().optional(),
1256
+ strokeRadius: z.number().optional(),
1257
+ autoDimensions: z.boolean().optional()
1258
+ });
1259
+ const VideoLayerTypeSchema = z.enum([
1260
+ "Video",
1261
+ "Image",
1262
+ "Audio",
1263
+ "Text",
1264
+ "Caption",
1265
+ "SVG",
1266
+ "GIF",
1267
+ "Lottie"
1268
+ ]);
1269
+ const BaseVideoLayerPropsSchema = VideoSpatialPropsSchema.merge(VideoTimingPropsSchema).merge(VideoTextPropsSchema).merge(VideoStylePropsSchema).extend({
1270
+ type: VideoLayerTypeSchema.optional(),
1271
+ animations: z.array(VideoAnimationSchema).optional()
1272
+ });
1273
+ const TransitionTypeSchema = z.enum([
1274
+ "none",
1275
+ "crossfade",
1276
+ "wipe-left",
1277
+ "wipe-right",
1278
+ "slide-up",
1279
+ "slide-down"
1280
+ ]);
1281
+ const TransitionSchema = z.object({
1282
+ type: TransitionTypeSchema,
1283
+ durationFrames: z.number().min(1)
1284
+ });
1285
+ const MediaSourceSchema = z.object({ entity: FileAssetSchema.optional() });
1286
+ const MediaMetadataSchema = z.object({
1287
+ width: z.number().optional().nullable(),
1288
+ height: z.number().optional().nullable(),
1289
+ fps: z.number().optional().nullable(),
1290
+ durationMs: z.number().optional().nullable(),
1291
+ sampleRate: z.number().optional().nullable(),
1292
+ channels: z.number().optional().nullable(),
1293
+ bitDepth: z.number().optional().nullable(),
1294
+ audioCodec: z.string().optional().nullable(),
1295
+ audioBitrate: z.number().optional().nullable()
1296
+ });
1297
+ const TimelineSegmentSchema = z.object({
1298
+ startSec: z.number(),
1299
+ endSec: z.number().optional()
1300
+ });
1301
+ const TimelineTransformSchema = z.object({
1302
+ startFrame: z.number().optional(),
1303
+ segments: z.array(TimelineSegmentSchema).optional()
1304
+ });
1305
+ const BaseMediaOperationPropsSchema = z.object({
1306
+ volume: z.number().optional(),
1307
+ opacity: z.number().optional(),
1308
+ startFrame: z.number().optional(),
1309
+ timeline: TimelineTransformSchema.optional()
1310
+ });
1311
+ /** Original source file (leaf node) */
1312
+ const SourceOperationSchema = BaseMediaOperationPropsSchema.extend({
1313
+ op: z.literal("source"),
1314
+ source: MediaSourceSchema,
1315
+ sourceMeta: MediaMetadataSchema,
1316
+ dataType: z.enum(Object.values(DataTypeVal))
1317
+ });
1318
+ /** Text content (leaf node) */
1319
+ const TextOperationSchema = BaseMediaOperationPropsSchema.extend({
1320
+ op: z.literal("text"),
1321
+ text: z.string(),
1322
+ metadata: MediaMetadataSchema.optional(),
1323
+ dataType: z.enum(Object.values(DataTypeVal))
1324
+ }).passthrough();
1325
+ const MediaOperationSchema = z.union([
1326
+ SourceOperationSchema,
1327
+ TextOperationSchema,
1328
+ BaseMediaOperationPropsSchema.extend({
1329
+ op: z.string(),
1330
+ metadata: MediaMetadataSchema.optional(),
1331
+ dataType: z.enum(Object.values(DataTypeVal))
1332
+ }).passthrough()
1333
+ ]);
1334
+ const VirtualMediaDataSchema = z.lazy(() => z.object({
1335
+ metadata: MediaMetadataSchema,
1336
+ operation: MediaOperationSchema,
1337
+ children: z.array(VirtualMediaDataSchema).default([])
1338
+ }));
1339
+ const ExtendedLayerSchema = BaseVideoLayerPropsSchema.extend({
1340
+ id: z.string(),
1341
+ name: z.string().optional(),
1342
+ type: VideoLayerTypeSchema,
1343
+ virtualMedia: VirtualMediaDataSchema.optional(),
1344
+ transitionIn: TransitionSchema.optional(),
1345
+ transitionOut: TransitionSchema.optional(),
1346
+ durationInMS: z.number(),
1347
+ maxDurationInMS: z.number().optional(),
1348
+ src: z.string().optional(),
1349
+ volume: z.number().default(1),
1350
+ inputHandleId: z.string().optional(),
1351
+ isPlaceholder: z.boolean().optional(),
1352
+ lockAspect: z.boolean().optional(),
1353
+ lockRatio: z.boolean().optional(),
1354
+ zIndex: z.number().optional()
1355
+ }).strict();
1356
+ const DEFAULT_FPS = 60;
1357
+ const DEFAULT_DURATION_MS = 3e3;
1358
+ const DEFAULT_MEDIA_DIMENSION = 1080;
1359
+ /** Default styling applied to new Text layers. */
1360
+ const TEXT_LAYER_DEFAULTS = {
1361
+ fontSize: 60,
1362
+ fontFamily: "Inter",
1363
+ fill: "#ffffff",
1364
+ fontStyle: "normal",
1365
+ align: "start",
1366
+ padding: 0
1367
+ };
1368
+ /** Default styling applied to new Caption layers. */
1369
+ const CAPTION_LAYER_DEFAULTS = {
1370
+ fontSize: 48,
1371
+ fontFamily: "Inter",
1372
+ fill: "#ffffff",
1373
+ align: "center",
1374
+ verticalAlign: "bottom",
1375
+ padding: 0,
1376
+ lineHeight: 1.1,
1377
+ textAnimation: {
1378
+ in: "none",
1379
+ out: "none",
1380
+ entranceMs: 0,
1381
+ exitMs: 0,
1382
+ kinetic: "none",
1383
+ applyBy: "word",
1384
+ smoothing: true
1385
+ },
1386
+ bottomPadding: 60
1387
+ };
1388
+ /**
1389
+ * Identify the actual leaf media type (Video, Image, etc) by walking down the tree.
1390
+ * This can differ from getMediaType() if an operation (like Ken Burns) upgrades
1391
+ * an Image to a Video.
1392
+ */
1393
+ function getLeafMediaType(vv) {
1394
+ if (!vv) return void 0;
1395
+ if (vv.operation?.op === "source") return vv.operation.dataType;
1396
+ if (vv.children && vv.children.length > 0) return getLeafMediaType(vv.children[0]);
1397
+ return vv.operation.dataType;
1398
+ }
1399
+ /**
1400
+ * Resolve the MIME type of the leaf source media.
1401
+ */
1402
+ function resolveMediaMimeType(vv) {
1403
+ if (!vv) return void 0;
1404
+ if (vv.operation?.op === "source") {
1405
+ const op = vv.operation;
1406
+ if (op.mimeType) return op.mimeType;
1407
+ if (op.sourceMeta?.mimeType) return op.sourceMeta.mimeType;
1408
+ if (op.source?.entity?.mimeType) return op.source.entity.mimeType;
1409
+ const url = typeof op.source === "string" ? op.source : op.url ?? op.source?.url;
1410
+ if (typeof url === "string") {
1411
+ const ext = url.split(".").pop()?.toLowerCase();
1412
+ if (ext === "mp4") return "video/mp4";
1413
+ if (ext === "webm") return "video/webm";
1414
+ if (ext === "mov") return "video/quicktime";
1415
+ if (ext === "png") return "image/png";
1416
+ if (ext === "jpg" || ext === "jpeg") return "image/jpeg";
1417
+ if (ext === "webp") return "image/webp";
1418
+ if (ext === "svg") return "image/svg+xml";
1419
+ if (ext === "mp3") return "audio/mpeg";
1420
+ if (ext === "wav") return "audio/wav";
1421
+ if (ext === "aac") return "audio/aac";
1422
+ }
1423
+ }
1424
+ if (vv.children && vv.children.length > 0) return resolveMediaMimeType(vv.children[0]);
1425
+ }
1426
+ /**
1427
+ * Create a VirtualMediaData from a FileData source or Text.
1428
+ * Used by Import, VideoGen, and Text nodes to wrap concrete content.
1429
+ */
1430
+ function createVirtualMedia(source, type = "Video") {
1431
+ if (source && typeof source === "object" && "operation" in source && "metadata" in source) return source;
1432
+ if (typeof source === "string") {
1433
+ const url = source;
1434
+ const sourceMeta$1 = {
1435
+ width: void 0,
1436
+ height: void 0,
1437
+ durationMs: type === "Image" || /\.(png|jpg|jpeg|webp|gif|svg)$/i.test(url) ? DEFAULT_DURATION_MS : void 0,
1438
+ fps: void 0
1439
+ };
1440
+ return {
1441
+ metadata: sourceMeta$1,
1442
+ operation: {
1443
+ op: "source",
1444
+ url,
1445
+ source: url,
1446
+ sourceMeta: sourceMeta$1,
1447
+ dataType: type
1448
+ },
1449
+ children: []
1450
+ };
1451
+ }
1452
+ if (typeof source === "object" && source !== null && ("url" in source || "path" in source)) {
1453
+ const desc = source;
1454
+ const url = desc.url ?? desc.path;
1455
+ const isImage = type === "Image" || /\.(png|jpg|jpeg|webp|gif|svg)$/i.test(url);
1456
+ const durationMs = desc.durationMs ?? (isImage ? DEFAULT_DURATION_MS : void 0);
1457
+ const sourceMeta$1 = {
1458
+ width: desc.width,
1459
+ height: desc.height,
1460
+ durationMs,
1461
+ fps: desc.fps,
1462
+ sampleRate: desc.sampleRate,
1463
+ channels: desc.channels,
1464
+ bitDepth: desc.bitDepth,
1465
+ audioCodec: desc.audioCodec,
1466
+ audioBitrate: desc.audioBitrate
1467
+ };
1468
+ return {
1469
+ metadata: sourceMeta$1,
1470
+ operation: {
1471
+ op: "source",
1472
+ url,
1473
+ source: url,
1474
+ sourceMeta: sourceMeta$1,
1475
+ dataType: type
1476
+ },
1477
+ children: []
1478
+ };
1479
+ }
1480
+ const sourceMeta = getFileDataMetadata(source) || {
1481
+ width: void 0,
1482
+ height: void 0,
1483
+ durationMs: void 0,
1484
+ fps: void 0
1485
+ };
1486
+ return {
1487
+ metadata: sourceMeta,
1488
+ operation: {
1489
+ op: "source",
1490
+ source: { entity: source?.entity },
1491
+ sourceMeta,
1492
+ dataType: type
1493
+ },
1494
+ children: []
1495
+ };
1496
+ }
1497
+ /**
1498
+ * Identify if a VirtualMediaData node is intended to be Video, Audio, Image, or Text.
1499
+ */
1500
+ function getMediaType(vv) {
1501
+ if (!vv) throw new Error("No media data provided");
1502
+ return vv.operation.dataType;
1503
+ }
1504
+ /**
1505
+ * Append an operation to an existing VirtualMediaData (recursive).
1506
+ * This creates a new parent node wrapping the current one as a child.
1507
+ */
1508
+ function appendOperation(vv, operation) {
1509
+ return {
1510
+ metadata: computeNextMetadata(getActiveMediaMetadata(vv) ?? {}, operation),
1511
+ operation,
1512
+ children: [vv]
1513
+ };
1514
+ }
1515
+ /**
1516
+ * Helper to compute the metadata of the NEXT node in the operator tree.
1517
+ */
1518
+ function computeNextMetadata(baseMeta, op) {
1519
+ let { width, height, durationMs, fps, sampleRate, channels, bitDepth, audioCodec, audioBitrate } = baseMeta;
1520
+ if ("metadata" in op && op.metadata) {
1521
+ width = op.metadata.width ?? width;
1522
+ height = op.metadata.height ?? height;
1523
+ durationMs = op.metadata.durationMs ?? durationMs;
1524
+ fps = op.metadata.fps ?? fps;
1525
+ sampleRate = op.metadata.sampleRate ?? sampleRate;
1526
+ channels = op.metadata.channels ?? channels;
1527
+ bitDepth = op.metadata.bitDepth ?? bitDepth;
1528
+ audioCodec = op.metadata.audioCodec ?? audioCodec;
1529
+ audioBitrate = op.metadata.audioBitrate ?? audioBitrate;
1530
+ }
1531
+ if (op.dataType === "Audio" || op.dataType === "Text" || op.dataType === "Caption") {
1532
+ width = void 0;
1533
+ height = void 0;
1534
+ fps = void 0;
1535
+ }
1536
+ return {
1537
+ width,
1538
+ height,
1539
+ durationMs,
1540
+ fps,
1541
+ sampleRate,
1542
+ channels,
1543
+ bitDepth,
1544
+ audioCodec,
1545
+ audioBitrate
1546
+ };
1547
+ }
1548
+ function getFileDataMetadata(filedata) {
1549
+ if (!filedata) return null;
1550
+ const { entity } = filedata;
1551
+ const width = entity?.width;
1552
+ const height = entity?.height;
1553
+ let durationMs = entity?.duration;
1554
+ const fps = entity?.fps;
1555
+ const sampleRate = entity?.sampleRate;
1556
+ const channels = entity?.channels;
1557
+ const bitDepth = entity?.bitDepth;
1558
+ const audioCodec = entity?.audioCodec;
1559
+ const audioBitrate = entity?.audioBitrate;
1560
+ if (durationMs === void 0 || durationMs === null) {
1561
+ const mimeType = entity?.mimeType ?? "";
1562
+ if (mimeType.startsWith("image/") || mimeType === "image/svg+xml") durationMs = DEFAULT_DURATION_MS;
1563
+ }
1564
+ if (width === void 0 && height === void 0 && durationMs === void 0 && sampleRate === void 0 && channels === void 0) return null;
1565
+ return {
1566
+ width,
1567
+ height,
1568
+ durationMs: durationMs === 0 ? void 0 : durationMs,
1569
+ fps,
1570
+ sampleRate,
1571
+ channels,
1572
+ bitDepth,
1573
+ audioCodec,
1574
+ audioBitrate
1575
+ };
1576
+ }
1577
+ /**
1578
+ * Get the active metadata from the VirtualMediaData node.
1579
+ * Simply returns the metadata property of the node.
1580
+ * Supports legacy formats (sourceMeta) and extracts from source if needed.
1581
+ */
1582
+ function getActiveMediaMetadata(vv) {
1583
+ if (!vv) return null;
1584
+ let width = vv.metadata?.width;
1585
+ let height = vv.metadata?.height;
1586
+ let durationMs = vv.metadata?.durationMs;
1587
+ let fps = vv.metadata?.fps;
1588
+ let sampleRate = vv.metadata?.sampleRate;
1589
+ let channels = vv.metadata?.channels;
1590
+ let bitDepth = vv.metadata?.bitDepth;
1591
+ let audioCodec = vv.metadata?.audioCodec;
1592
+ let audioBitrate = vv.metadata?.audioBitrate;
1593
+ const op = vv.operation;
1594
+ if (op?.op === "source") {
1595
+ const sm = op.sourceMeta || {};
1596
+ const fdm = op.source ? getFileDataMetadata(op.source) : null;
1597
+ width = width ?? sm.width ?? fdm?.width;
1598
+ height = height ?? sm.height ?? fdm?.height;
1599
+ durationMs = durationMs ?? sm.durationMs ?? fdm?.durationMs;
1600
+ fps = fps ?? sm.fps ?? fdm?.fps;
1601
+ sampleRate = sampleRate ?? sm.sampleRate ?? fdm?.sampleRate;
1602
+ channels = channels ?? sm.channels ?? fdm?.channels;
1603
+ bitDepth = bitDepth ?? sm.bitDepth ?? fdm?.bitDepth;
1604
+ audioCodec = audioCodec ?? sm.audioCodec ?? fdm?.audioCodec;
1605
+ audioBitrate = audioBitrate ?? sm.audioBitrate ?? fdm?.audioBitrate;
1606
+ }
1607
+ if (width === void 0 && height === void 0 && durationMs === void 0 && sampleRate === void 0 && channels === void 0) return null;
1608
+ return {
1609
+ width,
1610
+ height,
1611
+ durationMs: durationMs === 0 ? void 0 : durationMs,
1612
+ fps,
1613
+ sampleRate,
1614
+ channels,
1615
+ bitDepth,
1616
+ audioCodec,
1617
+ audioBitrate
1618
+ };
1619
+ }
1620
+ /**
1621
+ * Compute the dimensions and offsets for rendering a cropped video.
1622
+ * Traverses the VirtualMediaData tree to find nested crops and source dimensions.
1623
+ */
1624
+ function computeVideoCropRenderProps(virtualMedia) {
1625
+ let totalOffsetX = 0;
1626
+ let totalOffsetY = 0;
1627
+ let hasCrop = false;
1628
+ let sourceMetaFound = null;
1629
+ let currentNode = virtualMedia;
1630
+ while (currentNode) {
1631
+ const op = currentNode.operation;
1632
+ if (op.op === "Crop") {
1633
+ hasCrop = true;
1634
+ const anyOp = op;
1635
+ const inputMeta = currentNode.children[0]?.metadata;
1636
+ const inputW = inputMeta?.width ?? 0;
1637
+ const inputH = inputMeta?.height ?? 0;
1638
+ if (inputW > 0 && inputH > 0) {
1639
+ const cropLeftPx = anyOp.leftPercentage / 100 * inputW;
1640
+ const cropTopPx = anyOp.topPercentage / 100 * inputH;
1641
+ totalOffsetX += cropLeftPx;
1642
+ totalOffsetY += cropTopPx;
1643
+ }
1644
+ }
1645
+ if (op.op === "source") sourceMetaFound = op.sourceMeta ?? currentNode.metadata;
1646
+ currentNode = currentNode.children[0];
1647
+ }
1648
+ if (!hasCrop || !sourceMetaFound) return null;
1649
+ const sourceW = sourceMetaFound.width ?? 1;
1650
+ const sourceH = sourceMetaFound.height ?? 1;
1651
+ return {
1652
+ videoNaturalWidth: sourceW,
1653
+ videoNaturalHeight: sourceH,
1654
+ cropTranslatePercentageX: -(totalOffsetX / sourceW) * 100,
1655
+ cropTranslatePercentageY: -(totalOffsetY / sourceH) * 100
1656
+ };
1657
+ }
1658
+ /**
1659
+ * Performs a deep comparison between two values to determine if they are equivalent.
1660
+ * Handles primitives, objects, arrays, Dates, and NaNs.
1661
+ */
1662
+ function isEqual(a, b) {
1663
+ if (a === b) return true;
1664
+ if (typeof a === "number" && typeof b === "number" && Number.isNaN(a) && Number.isNaN(b)) return true;
1665
+ if (a && b && typeof a === "object" && typeof b === "object") {
1666
+ if (a.constructor !== b.constructor) return false;
1667
+ if (Array.isArray(a)) {
1668
+ if (a.length !== b.length) return false;
1669
+ for (let i = 0; i < a.length; i++) if (!isEqual(a[i], b[i])) return false;
1670
+ return true;
1671
+ }
1672
+ if (a instanceof Date) return a.getTime() === b.getTime();
1673
+ if (a instanceof RegExp) return a.toString() === b.toString();
1674
+ const keys = Object.keys(a);
1675
+ if (keys.length !== Object.keys(b).length) return false;
1676
+ for (const key of keys) {
1677
+ if (!Object.hasOwn(b, key)) return false;
1678
+ if (!isEqual(a[key], b[key])) return false;
1679
+ }
1680
+ return true;
1681
+ }
1682
+ return false;
1683
+ }
1684
+ /**
1685
+ * Gets a stable object for hashing/fingerprinting by sorting keys and skipping volatile fields.
1686
+ */
1687
+ function getStableData(data) {
1688
+ if (typeof data !== "object" || data === null) return data;
1689
+ if (Array.isArray(data)) return data.map(getStableData);
1690
+ const result = {};
1691
+ const keys = Object.keys(data).sort();
1692
+ for (const key of keys) {
1693
+ const val = data[key];
1694
+ if (key === "createdAt" || key === "updatedAt" || key === "dataUrl" || key === "filePath") continue;
1695
+ if (typeof val === "string" && val.startsWith("blob:")) continue;
1696
+ if (key === "entity" && val && typeof val === "object" && val.id) {
1697
+ result.entity = { id: val.id };
1698
+ continue;
1699
+ }
1700
+ result[key] = getStableData(val);
1701
+ }
1702
+ return result;
1703
+ }
1704
+ /**
1705
+ * Generates a fingerprint for the given data.
1706
+ */
1707
+ function getFingerprint(data) {
1708
+ const stableData = getStableData(data);
1709
+ const str = JSON.stringify(stableData);
1710
+ if (str.length === 0) return "0";
1711
+ const getHash = (seed) => {
1712
+ let h1 = 3735928559 ^ seed;
1713
+ let h2 = 1103547991 ^ seed;
1714
+ for (let i = 0; i < str.length; i++) {
1715
+ const ch = str.charCodeAt(i);
1716
+ h1 = Math.imul(h1 ^ ch, 2654435761);
1717
+ h2 = Math.imul(h2 ^ ch, 1597334677);
1718
+ }
1719
+ h1 = Math.imul(h1 ^ h1 >>> 16, 2246822519);
1720
+ h1 = Math.imul(h1 ^ h1 >>> 13, 3266489917);
1721
+ h1 = h1 ^ h1 >>> 16;
1722
+ h2 = Math.imul(h2 ^ h2 >>> 16, 2246822519);
1723
+ h2 = Math.imul(h2 ^ h2 >>> 13, 3266489917);
1724
+ h2 = h2 ^ h2 >>> 16;
1725
+ return (h1 >>> 0).toString(36).padStart(7, "0") + (h2 >>> 0).toString(36).padStart(7, "0");
1726
+ };
1727
+ return getHash(0) + getHash(1);
1728
+ }
1729
+ /**
1730
+ * Generates a version-aware fingerprint for render caching.
1731
+ * Includes app version so cached renders are invalidated on version bumps.
1732
+ */
1733
+ function getRenderFingerprint(data, appVersion, extra) {
1734
+ return getFingerprint({
1735
+ __v: appVersion,
1736
+ data,
1737
+ ...extra
1738
+ });
1739
+ }
1740
+ /**
1741
+ * Checks if data is VirtualMediaData.
1742
+ */
1743
+ function isVirtualMediaData(data) {
1744
+ return typeof data === "object" && data !== null && "metadata" in data && "operation" in data && "children" in data;
1745
+ }
1746
+ /**
1747
+ * Checks if the virtual media is just a raw source without any significant operations
1748
+ * that would require a rendering step (like transformations, trimming, or effects).
1749
+ */
1750
+ function hasOnlySingleSource(data) {
1751
+ if (!isVirtualMediaData(data)) return false;
1752
+ if (data.operation.op === "source") {
1753
+ if (data.children && data.children.length > 0) return false;
1754
+ const op = data.operation;
1755
+ return !(op.volume !== void 0 && op.volume !== 1 || op.opacity !== void 0 && op.opacity !== 1 || op.startFrame !== void 0 && op.startFrame !== 0 || op.timeline && (op.timeline.startFrame !== void 0 && op.timeline.startFrame !== 0 || op.timeline.segments && op.timeline.segments.length > 0));
1756
+ }
1757
+ if (data.operation.op === "compose") return false;
1758
+ if (data.operation.op === "layer" && data.children.length === 1) {
1759
+ const op = data.operation;
1760
+ if (!(op.x !== void 0 && op.x !== 0 || op.y !== void 0 && op.y !== 0 || op.rotation !== void 0 && op.rotation !== 0 || op.scale !== void 0 && op.scale !== 1 || op.opacity !== void 0 && op.opacity !== 1 || op.volume !== void 0 && op.volume !== 1 || op.zIndex !== void 0 || op.blendMode !== void 0 || op.filters !== void 0 || op.transitionIn !== void 0 || op.transitionOut !== void 0 || op.animations && op.animations.length > 0 || op.trimStart !== void 0 || op.trimEnd !== void 0 || op.speed !== void 0 || op.backgroundColor !== void 0 || op.borderColor !== void 0 || op.borderWidth !== void 0 || op.strokeRadius !== void 0)) return hasOnlySingleSource(data.children[0]);
1761
+ }
1762
+ return false;
1763
+ }
1764
+ /**
1765
+ * Finds the source asset in a virtual media tree.
1766
+ */
1767
+ function findSourceAsset(vm) {
1768
+ if (vm.operation.op === "source") return vm.operation.source;
1769
+ if (vm.children && vm.children.length > 0) return findSourceAsset(vm.children[0]);
1770
+ return null;
1771
+ }
1772
+ /**
1773
+ * Checks if a cached node result can be reused based on source fingerprint.
1774
+ * Returns the valid cached result, or null if the cache is stale.
1775
+ */
1776
+ function checkNodeResultCache(currentResult, input) {
1777
+ if (!currentResult?.sourceFingerprint) return null;
1778
+ const inputFingerprint = getFingerprint(input);
1779
+ if (currentResult.sourceFingerprint !== inputFingerprint) return null;
1780
+ return currentResult;
1781
+ }
1782
+ /**
1783
+ * Creates a fresh result with a source fingerprint stamp.
1784
+ */
1785
+ function stampResult(result, input) {
1786
+ return {
1787
+ ...result,
1788
+ sourceFingerprint: getFingerprint(input)
1789
+ };
1790
+ }
1791
+ /**
1792
+ * Attempts to parse the width and height of an SVG from its buffer.
1793
+ * Falls back to viewBox ratios if explicitly defined sizes are missing.
1794
+ */
1795
+ function extractSvgDimensions(buffer) {
1796
+ try {
1797
+ const match = buffer.toString("utf-8").match(/<svg[^>]*>/i);
1798
+ if (!match) return null;
1799
+ const svgTag = match[0];
1800
+ const getAttr = (name) => {
1801
+ const attrMatch = svgTag.match(new RegExp(`${name}=["']([^"']+)["']`, "i"));
1802
+ if (!attrMatch) return null;
1803
+ const val = parseFloat(attrMatch[1].replace(/[a-z%]/gi, ""));
1804
+ return isNaN(val) ? null : val;
1805
+ };
1806
+ let w = getAttr("width");
1807
+ let h = getAttr("height");
1808
+ const viewBoxMatch = svgTag.match(/viewBox=["']([^"']+)["']/i);
1809
+ if (viewBoxMatch) {
1810
+ const parts = viewBoxMatch[1].split(/[\s,]+/).map(parseFloat);
1811
+ if (parts.length >= 4 && !isNaN(parts[2]) && !isNaN(parts[3])) {
1812
+ const vbW = parts[2];
1813
+ const vbH = parts[3];
1814
+ if (w == null && h == null) {
1815
+ w = vbW;
1816
+ h = vbH;
1817
+ } else if (w != null && h == null) h = w * (vbH / vbW);
1818
+ else if (h != null && w == null) w = h * (vbW / vbH);
1819
+ }
1820
+ }
1821
+ if (w != null && h != null && w > 0 && h > 0) return {
1822
+ w,
1823
+ h
1824
+ };
1825
+ return null;
1826
+ } catch (error) {
1827
+ console.error("Error parsing SVG dimensions", error);
1828
+ return null;
1829
+ }
1830
+ }
1831
+
1832
+ //#endregion
1833
+ //#region ../node-sdk/dist/renderer.mjs
1834
+ function defineRenderer(plugin) {
1835
+ return Object.freeze(plugin);
1836
+ }
1837
+ const GLOBAL_WEBGPU_REGISTRY_KEY = Symbol.for("gitframes.webgpuRegistry");
1838
+ var WebGPURegistry = class {
1839
+ renderers = /* @__PURE__ */ new Map();
1840
+ register(op, renderer) {
1841
+ this.renderers.set(op, renderer);
1842
+ }
1843
+ get(op) {
1844
+ return this.renderers.get(op);
1845
+ }
1846
+ has(op) {
1847
+ return this.renderers.has(op);
1848
+ }
1849
+ };
1850
+ const webgpuRegistry = (() => {
1851
+ const g = globalThis;
1852
+ if (!g[GLOBAL_WEBGPU_REGISTRY_KEY]) g[GLOBAL_WEBGPU_REGISTRY_KEY] = new WebGPURegistry();
1853
+ return g[GLOBAL_WEBGPU_REGISTRY_KEY];
1854
+ })();
1855
+ function registerWebGPURenderer(op, renderer) {
1856
+ webgpuRegistry.register(op, renderer);
1857
+ }
1858
+ var AudioProcessorRegistry = class {
1859
+ processors = /* @__PURE__ */ new Map();
1860
+ register(opType, processor) {
1861
+ this.processors.set(opType, processor);
1862
+ }
1863
+ get(opType) {
1864
+ return this.processors.get(opType) || null;
1865
+ }
1866
+ };
1867
+ const GLOBAL_AUDIO_KEY = Symbol.for("gitframes.audioRegistry");
1868
+ const audioRegistry = (() => {
1869
+ const g = globalThis;
1870
+ if (!g[GLOBAL_AUDIO_KEY]) g[GLOBAL_AUDIO_KEY] = new AudioProcessorRegistry();
1871
+ return g[GLOBAL_AUDIO_KEY];
1872
+ })();
1873
+
1874
+ //#endregion
1875
+ //#region ../node-sdk/dist/index.mjs
1876
+ var ConfigBuilder = class ConfigBuilder$1 {
1877
+ constructor(shape = {}, options = { strict: true }) {
1878
+ this.shape = shape;
1879
+ this.options = options;
1880
+ }
1881
+ field(key, schema, options) {
1882
+ const withMeta = options ? schema.meta({ ...options }) : schema;
1883
+ return new ConfigBuilder$1({
1884
+ ...this.shape,
1885
+ [key]: withMeta
1886
+ }, this.options);
1887
+ }
1888
+ build() {
1889
+ const base = z.object(this.shape);
1890
+ return { schema: this.options.strict !== false ? base.strict() : base };
1891
+ }
1892
+ };
1893
+ function configBuilder(options = { strict: true }) {
1894
+ return new ConfigBuilder({}, options);
1895
+ }
1896
+ const COMPOSITE_OPERATIONS = [
1897
+ "source-over",
1898
+ "source-in",
1899
+ "source-out",
1900
+ "source-atop",
1901
+ "destination-over",
1902
+ "destination-in",
1903
+ "destination-out",
1904
+ "destination-atop",
1905
+ "lighter",
1906
+ "copy",
1907
+ "xor",
1908
+ "multiply",
1909
+ "screen",
1910
+ "overlay",
1911
+ "darken",
1912
+ "lighten",
1913
+ "color-dodge",
1914
+ "color-burn",
1915
+ "hard-light",
1916
+ "soft-light",
1917
+ "difference",
1918
+ "exclusion",
1919
+ "hue",
1920
+ "saturation",
1921
+ "color",
1922
+ "luminosity"
1923
+ ];
1924
+ const GlobalCompositeOperation = z.enum(COMPOSITE_OPERATIONS);
1925
+ const ColorSchema = z.string().optional();
1926
+ const PercentageSchema = z.number().min(0).max(100);
1927
+ const DimensionSchema = z.number().min(0).optional();
1928
+ const AnimationSchema = z.object({ animations: z.array(z.object({
1929
+ id: z.string(),
1930
+ type: z.enum([
1931
+ "fade-in",
1932
+ "fade-out",
1933
+ "slide-in-left",
1934
+ "slide-in-right",
1935
+ "slide-in-top",
1936
+ "slide-in-bottom",
1937
+ "zoom-in",
1938
+ "zoom-out",
1939
+ "rotate-cw",
1940
+ "rotate-ccw",
1941
+ "bounce",
1942
+ "shake"
1943
+ ]),
1944
+ value: z.number()
1945
+ })).optional() });
1946
+ const StrokeSchema = z.object({
1947
+ stroke: ColorSchema,
1948
+ strokeWidth: z.number().min(0).optional(),
1949
+ strokeAlign: z.enum([
1950
+ "inside",
1951
+ "center",
1952
+ "outside"
1953
+ ]).optional().default("inside"),
1954
+ strokeRadius: z.number().min(0).optional()
1955
+ });
1956
+ const ImageResultSchema = SingleOutputGenericSchema(createOutputItemSchema(z.literal("Image"), FileDataSchema));
1957
+ const VideoResultSchema = SingleOutputGenericSchema(createOutputItemSchema(z.literal("Video"), VirtualMediaDataSchema));
1958
+ const AudioResultSchema = SingleOutputGenericSchema(createOutputItemSchema(z.literal("Audio"), VirtualMediaDataSchema));
1959
+ const TextResultSchema = SingleOutputGenericSchema(createOutputItemSchema(z.literal("Text"), z.string()));
1960
+ const NumberResultSchema = SingleOutputGenericSchema(createOutputItemSchema(z.literal("Number"), z.number()));
1961
+ const SignalResultSchema = SingleOutputGenericSchema(createOutputItemSchema(z.literal("Signal"), z.unknown()));
1962
+
1963
+ //#endregion
1964
+ //#region ../renderers/dist/index.mjs
1965
+ const mapBlendModeToSkia = (mode) => {
1966
+ if (!mode) return "srcOver";
1967
+ switch (mode) {
1968
+ case "source-over": return "srcOver";
1969
+ case "source-in": return "srcIn";
1970
+ case "source-out": return "srcOut";
1971
+ case "source-atop": return "srcATop";
1972
+ case "destination-over": return "dstOver";
1973
+ case "destination-in": return "dstIn";
1974
+ case "destination-out": return "dstOut";
1975
+ case "destination-atop": return "dstATop";
1976
+ case "copy": return "src";
1977
+ case "xor": return "xor";
1978
+ case "multiply": return "multiply";
1979
+ case "screen": return "screen";
1980
+ case "overlay": return "overlay";
1981
+ case "darken": return "darken";
1982
+ case "lighten":
1983
+ case "lighter": return "lighten";
1984
+ case "color-dodge": return "colorDodge";
1985
+ case "color-burn": return "colorBurn";
1986
+ case "hard-light": return "hardLight";
1987
+ case "soft-light": return "softLight";
1988
+ case "difference": return "difference";
1989
+ case "exclusion": return "exclusion";
1990
+ case "hue": return "hue";
1991
+ case "saturation": return "saturation";
1992
+ case "color": return "color";
1993
+ case "luminosity": return "luminosity";
1994
+ default: return "srcOver";
1995
+ }
1996
+ };
1997
+ const requiresIsolation = (mode) => {
1998
+ return mode === "destination-in" || mode === "source-in";
1999
+ };
2000
+ function computeStrokeRect(w, h, cornerRadius, strokeWidth, align = "outside") {
2001
+ if (align === "inside") {
2002
+ const half = strokeWidth / 2;
2003
+ return {
2004
+ x: half,
2005
+ y: half,
2006
+ width: Math.max(0, w - strokeWidth),
2007
+ height: Math.max(0, h - strokeWidth),
2008
+ radius: cornerRadius === 0 ? 0 : Math.max(0, cornerRadius - half)
2009
+ };
2010
+ }
2011
+ if (align === "outside") {
2012
+ const half = strokeWidth / 2;
2013
+ return {
2014
+ x: -half,
2015
+ y: -half,
2016
+ width: w + strokeWidth,
2017
+ height: h + strokeWidth,
2018
+ radius: cornerRadius === 0 ? 0 : cornerRadius + half
2019
+ };
2020
+ }
2021
+ return {
2022
+ x: 0,
2023
+ y: 0,
2024
+ width: w,
2025
+ height: h,
2026
+ radius: cornerRadius
2027
+ };
2028
+ }
2029
+ const { cbrt, sqrt, PI: π } = Math;
2030
+ const x2t = (x, a, b, c, d) => {
2031
+ const q = a + b * x;
2032
+ const s = q ** 2 + c;
2033
+ if (s > 0) {
2034
+ const root = sqrt(s);
2035
+ return cbrt(q + root) + cbrt(q - root) - d;
2036
+ }
2037
+ const l = cbrt(sqrt(q * q - s));
2038
+ const angle = q ? Math.atan(sqrt(-s) / q) : -π / 2;
2039
+ let φ;
2040
+ if (b < 0) φ = (q > 0 ? 2 * π : π) - angle;
2041
+ else if (d < 0) φ = (q > 0 ? 2 * π : -3 * π) + angle;
2042
+ else φ = (q > 0 ? 0 : π) + angle;
2043
+ return 2 * l * Math.cos(φ / 3) - d;
2044
+ };
2045
+ const Y = (t, ay, by, cy) => ((ay * t + 3 * by) * t + cy) * t;
2046
+ const linearEasing = (x) => x;
2047
+ function bezier(mX1, mY1, mX2, mY2) {
2048
+ if (!(0 <= mX1 && mX1 <= 1 && 0 <= mX2 && mX2 <= 1)) throw new Error("bezier x values must be in [0, 1] range");
2049
+ if (mX1 === mY1 && mX2 === mY2) return linearEasing;
2050
+ const a = 6 * (3 * mX1 - 3 * mX2 + 1);
2051
+ const b = 6 * (mX2 - 2 * mX1);
2052
+ const c = 3 * mX1;
2053
+ const a2 = a * a;
2054
+ const b2 = b * b;
2055
+ const d = b / a;
2056
+ const e = 3 * b * c / a2 - b2 * b / (a2 * a);
2057
+ const w1 = 2 * c / a - b2 / a2;
2058
+ const w = w1 * w1 * w1;
2059
+ const o = 3 / a;
2060
+ const ay = 3 * mY1 - 3 * mY2 + 1;
2061
+ const by = mY2 - 2 * mY1;
2062
+ const cy = 3 * mY1;
2063
+ const X2T = a ? x2t : linearEasing;
2064
+ return function BezierEasing(x) {
2065
+ if (x === 0 || x === 1) return x;
2066
+ return Y(X2T(x, e, o, w, d), ay, by, cy);
2067
+ };
2068
+ }
2069
+ const Easing = {
2070
+ linear: linearEasing,
2071
+ ease: bezier(.25, .1, .25, 1),
2072
+ quad: (t) => t * t,
2073
+ cubic: (t) => t * t * t,
2074
+ poly: (n) => (t) => t ** n,
2075
+ sin: (t) => 1 - Math.cos(t * Math.PI / 2),
2076
+ circle: (t) => 1 - Math.sqrt(1 - t * t),
2077
+ exp: (t) => 2 ** (10 * (t - 1)),
2078
+ elastic: (bounciness = 1) => {
2079
+ const p = bounciness * Math.PI;
2080
+ return (t) => 1 - Math.cos(t * Math.PI / 2) ** 3 * Math.cos(t * p);
2081
+ },
2082
+ back: (s = 1.70158) => (t) => t * t * ((s + 1) * t - s),
2083
+ bounce: (t) => {
2084
+ if (t < 1 / 2.75) return 7.5625 * t * t;
2085
+ if (t < 2 / 2.75) {
2086
+ const t2$1 = t - 1.5 / 2.75;
2087
+ return 7.5625 * t2$1 * t2$1 + .75;
2088
+ }
2089
+ if (t < 2.5 / 2.75) {
2090
+ const t2$1 = t - 2.25 / 2.75;
2091
+ return 7.5625 * t2$1 * t2$1 + .9375;
2092
+ }
2093
+ const t2 = t - 2.625 / 2.75;
2094
+ return 7.5625 * t2 * t2 + .984375;
2095
+ },
2096
+ bezier,
2097
+ in: (fn) => fn,
2098
+ out: (fn) => (t) => 1 - fn(1 - t),
2099
+ inOut: (fn) => (t) => t < .5 ? fn(t * 2) / 2 : 1 - fn((1 - t) * 2) / 2
2100
+ };
2101
+ function interpolate(value, inputRange, outputRange, options) {
2102
+ const extrapolateLeft = options?.extrapolateLeft ?? "extend";
2103
+ const extrapolateRight = options?.extrapolateRight ?? "extend";
2104
+ if (inputRange.length !== outputRange.length) throw new Error("inputRange and outputRange must have the same length");
2105
+ if (inputRange.length < 2) throw new Error("inputRange must have at least 2 elements");
2106
+ const firstInput = inputRange[0];
2107
+ const lastInput = inputRange[inputRange.length - 1];
2108
+ if (value < firstInput) {
2109
+ if (extrapolateLeft === "clamp") return outputRange[0];
2110
+ if (extrapolateLeft === "identity") return value;
2111
+ }
2112
+ if (value > lastInput) {
2113
+ if (extrapolateRight === "clamp") return outputRange[outputRange.length - 1];
2114
+ if (extrapolateRight === "identity") return value;
2115
+ }
2116
+ let i = 1;
2117
+ for (; i < inputRange.length - 1; i++) if (value < inputRange[i]) break;
2118
+ const x0 = inputRange[i - 1];
2119
+ const x1 = inputRange[i];
2120
+ const y0 = outputRange[i - 1];
2121
+ const y1 = outputRange[i];
2122
+ if (x0 === x1) return y0;
2123
+ let progress = (value - x0) / (x1 - x0);
2124
+ if (options?.easing) progress = options.easing(progress);
2125
+ return y0 + progress * (y1 - y0);
2126
+ }
2127
+ const Vector3Math = {
2128
+ create(x = 0, y = 0, z$2 = 0) {
2129
+ return [
2130
+ x,
2131
+ y,
2132
+ z$2
2133
+ ];
2134
+ },
2135
+ copy(v) {
2136
+ return [
2137
+ v[0],
2138
+ v[1],
2139
+ v[2]
2140
+ ];
2141
+ },
2142
+ set(out, x, y, z$2) {
2143
+ out[0] = x;
2144
+ out[1] = y;
2145
+ out[2] = z$2;
2146
+ return out;
2147
+ },
2148
+ add(a, b) {
2149
+ return [
2150
+ a[0] + b[0],
2151
+ a[1] + b[1],
2152
+ a[2] + b[2]
2153
+ ];
2154
+ },
2155
+ subtract(a, b) {
2156
+ return [
2157
+ a[0] - b[0],
2158
+ a[1] - b[1],
2159
+ a[2] - b[2]
2160
+ ];
2161
+ },
2162
+ multiplyScalar(v, s) {
2163
+ return [
2164
+ v[0] * s,
2165
+ v[1] * s,
2166
+ v[2] * s
2167
+ ];
2168
+ },
2169
+ dot(a, b) {
2170
+ return a[0] * b[0] + a[1] * b[1] + a[2] * b[2];
2171
+ },
2172
+ cross(a, b) {
2173
+ return [
2174
+ a[1] * b[2] - a[2] * b[1],
2175
+ a[2] * b[0] - a[0] * b[2],
2176
+ a[0] * b[1] - a[1] * b[0]
2177
+ ];
2178
+ },
2179
+ lengthSq(v) {
2180
+ return v[0] * v[0] + v[1] * v[1] + v[2] * v[2];
2181
+ },
2182
+ length(v) {
2183
+ return Math.hypot(v[0], v[1], v[2]);
2184
+ },
2185
+ distance(a, b) {
2186
+ return Math.hypot(a[0] - b[0], a[1] - b[1], a[2] - b[2]);
2187
+ },
2188
+ normalize(v) {
2189
+ const len = Math.hypot(v[0], v[1], v[2]);
2190
+ if (len === 0) return [
2191
+ 0,
2192
+ 0,
2193
+ 0
2194
+ ];
2195
+ const inv = 1 / len;
2196
+ return [
2197
+ v[0] * inv,
2198
+ v[1] * inv,
2199
+ v[2] * inv
2200
+ ];
2201
+ },
2202
+ lerp(a, b, t) {
2203
+ return [
2204
+ a[0] + (b[0] - a[0]) * t,
2205
+ a[1] + (b[1] - a[1]) * t,
2206
+ a[2] + (b[2] - a[2]) * t
2207
+ ];
2208
+ },
2209
+ equals(a, b, epsilon = 1e-6) {
2210
+ return Math.abs(a[0] - b[0]) <= epsilon && Math.abs(a[1] - b[1]) <= epsilon && Math.abs(a[2] - b[2]) <= epsilon;
2211
+ }
2212
+ };
2213
+ /**
2214
+ * Column-major 4x4 matrix mathematics for WebGPU 3D rendering and camera projections.
2215
+ * Memory layout matches WGSL `mat4x4<f32>`:
2216
+ * [ 0, 4, 8, 12 ]
2217
+ * [ 1, 5, 9, 13 ]
2218
+ * [ 2, 6, 10, 14 ]
2219
+ * [ 3, 7, 11, 15 ]
2220
+ */
2221
+ const Matrix4Math = {
2222
+ identity() {
2223
+ return [
2224
+ 1,
2225
+ 0,
2226
+ 0,
2227
+ 0,
2228
+ 0,
2229
+ 1,
2230
+ 0,
2231
+ 0,
2232
+ 0,
2233
+ 0,
2234
+ 1,
2235
+ 0,
2236
+ 0,
2237
+ 0,
2238
+ 0,
2239
+ 1
2240
+ ];
2241
+ },
2242
+ copy(m) {
2243
+ return [...m];
2244
+ },
2245
+ multiply(a, b) {
2246
+ const out = new Array(16);
2247
+ for (let row = 0; row < 4; row++) for (let col = 0; col < 4; col++) out[col * 4 + row] = a[0 + row] * b[col * 4 + 0] + a[4 + row] * b[col * 4 + 1] + a[8 + row] * b[col * 4 + 2] + a[12 + row] * b[col * 4 + 3];
2248
+ return out;
2249
+ },
2250
+ translate(tx, ty, tz = 0) {
2251
+ return [
2252
+ 1,
2253
+ 0,
2254
+ 0,
2255
+ 0,
2256
+ 0,
2257
+ 1,
2258
+ 0,
2259
+ 0,
2260
+ 0,
2261
+ 0,
2262
+ 1,
2263
+ 0,
2264
+ tx,
2265
+ ty,
2266
+ tz,
2267
+ 1
2268
+ ];
2269
+ },
2270
+ scale(sx, sy, sz = 1) {
2271
+ return [
2272
+ sx,
2273
+ 0,
2274
+ 0,
2275
+ 0,
2276
+ 0,
2277
+ sy,
2278
+ 0,
2279
+ 0,
2280
+ 0,
2281
+ 0,
2282
+ sz,
2283
+ 0,
2284
+ 0,
2285
+ 0,
2286
+ 0,
2287
+ 1
2288
+ ];
2289
+ },
2290
+ rotateX(rad) {
2291
+ const c = Math.cos(rad);
2292
+ const s = Math.sin(rad);
2293
+ return [
2294
+ 1,
2295
+ 0,
2296
+ 0,
2297
+ 0,
2298
+ 0,
2299
+ c,
2300
+ s,
2301
+ 0,
2302
+ 0,
2303
+ -s,
2304
+ c,
2305
+ 0,
2306
+ 0,
2307
+ 0,
2308
+ 0,
2309
+ 1
2310
+ ];
2311
+ },
2312
+ rotateY(rad) {
2313
+ const c = Math.cos(rad);
2314
+ const s = Math.sin(rad);
2315
+ return [
2316
+ c,
2317
+ 0,
2318
+ -s,
2319
+ 0,
2320
+ 0,
2321
+ 1,
2322
+ 0,
2323
+ 0,
2324
+ s,
2325
+ 0,
2326
+ c,
2327
+ 0,
2328
+ 0,
2329
+ 0,
2330
+ 0,
2331
+ 1
2332
+ ];
2333
+ },
2334
+ rotateZ(rad) {
2335
+ const c = Math.cos(rad);
2336
+ const s = Math.sin(rad);
2337
+ return [
2338
+ c,
2339
+ s,
2340
+ 0,
2341
+ 0,
2342
+ -s,
2343
+ c,
2344
+ 0,
2345
+ 0,
2346
+ 0,
2347
+ 0,
2348
+ 1,
2349
+ 0,
2350
+ 0,
2351
+ 0,
2352
+ 0,
2353
+ 1
2354
+ ];
2355
+ },
2356
+ rotateAxis(axis, rad) {
2357
+ const norm = Vector3Math.normalize(axis);
2358
+ const x = norm[0];
2359
+ const y = norm[1];
2360
+ const z$2 = norm[2];
2361
+ const c = Math.cos(rad);
2362
+ const s = Math.sin(rad);
2363
+ const t = 1 - c;
2364
+ return [
2365
+ t * x * x + c,
2366
+ t * x * y + s * z$2,
2367
+ t * x * z$2 - s * y,
2368
+ 0,
2369
+ t * x * y - s * z$2,
2370
+ t * y * y + c,
2371
+ t * y * z$2 + s * x,
2372
+ 0,
2373
+ t * x * z$2 + s * y,
2374
+ t * y * z$2 - s * x,
2375
+ t * z$2 * z$2 + c,
2376
+ 0,
2377
+ 0,
2378
+ 0,
2379
+ 0,
2380
+ 1
2381
+ ];
2382
+ },
2383
+ lookAt(eye, target, up = [
2384
+ 0,
2385
+ -1,
2386
+ 0
2387
+ ]) {
2388
+ let f = Vector3Math.subtract(target, eye);
2389
+ if (Vector3Math.lengthSq(f) === 0) f = [
2390
+ 0,
2391
+ 0,
2392
+ 1
2393
+ ];
2394
+ else f = Vector3Math.normalize(f);
2395
+ let r = Vector3Math.cross(f, up);
2396
+ if (Vector3Math.lengthSq(r) === 0) r = [
2397
+ 1,
2398
+ 0,
2399
+ 0
2400
+ ];
2401
+ else r = Vector3Math.normalize(r);
2402
+ const u = Vector3Math.cross(r, f);
2403
+ return [
2404
+ r[0],
2405
+ u[0],
2406
+ f[0],
2407
+ 0,
2408
+ r[1],
2409
+ u[1],
2410
+ f[1],
2411
+ 0,
2412
+ r[2],
2413
+ u[2],
2414
+ f[2],
2415
+ 0,
2416
+ -Vector3Math.dot(r, eye),
2417
+ -Vector3Math.dot(u, eye),
2418
+ -Vector3Math.dot(f, eye),
2419
+ 1
2420
+ ];
2421
+ },
2422
+ perspective(fovYRad, aspect, near, far) {
2423
+ const f = 1 / Math.tan(fovYRad / 2);
2424
+ const rangeInv = 1 / (far - near);
2425
+ return [
2426
+ f / aspect,
2427
+ 0,
2428
+ 0,
2429
+ 0,
2430
+ 0,
2431
+ f,
2432
+ 0,
2433
+ 0,
2434
+ 0,
2435
+ 0,
2436
+ far * rangeInv,
2437
+ 1,
2438
+ 0,
2439
+ 0,
2440
+ -near * far * rangeInv,
2441
+ 0
2442
+ ];
2443
+ },
2444
+ orthographic(left, right, bottom, top, near, far) {
2445
+ const lr = 1 / (right - left);
2446
+ const bt = 1 / (top - bottom);
2447
+ const nf = 1 / (far - near);
2448
+ return [
2449
+ 2 * lr,
2450
+ 0,
2451
+ 0,
2452
+ 0,
2453
+ 0,
2454
+ 2 * bt,
2455
+ 0,
2456
+ 0,
2457
+ 0,
2458
+ 0,
2459
+ nf,
2460
+ 0,
2461
+ -(right + left) * lr,
2462
+ -(top + bottom) * bt,
2463
+ -near * nf,
2464
+ 1
2465
+ ];
2466
+ },
2467
+ invert(m) {
2468
+ const inv = new Array(16);
2469
+ inv[0] = m[5] * m[10] * m[15] - m[5] * m[11] * m[14] - m[9] * m[6] * m[15] + m[9] * m[7] * m[14] + m[13] * m[6] * m[11] - m[13] * m[7] * m[10];
2470
+ inv[4] = -m[4] * m[10] * m[15] + m[4] * m[11] * m[14] + m[8] * m[6] * m[15] - m[8] * m[7] * m[14] - m[12] * m[6] * m[11] + m[12] * m[7] * m[10];
2471
+ inv[8] = m[4] * m[9] * m[15] - m[4] * m[11] * m[13] - m[8] * m[5] * m[15] + m[8] * m[7] * m[13] + m[12] * m[5] * m[11] - m[12] * m[7] * m[9];
2472
+ inv[12] = -m[4] * m[9] * m[14] + m[4] * m[10] * m[13] + m[8] * m[5] * m[14] - m[8] * m[6] * m[13] - m[12] * m[5] * m[10] + m[12] * m[6] * m[9];
2473
+ inv[1] = -m[1] * m[10] * m[15] + m[1] * m[11] * m[14] + m[9] * m[2] * m[15] - m[9] * m[3] * m[14] - m[13] * m[2] * m[11] + m[13] * m[3] * m[10];
2474
+ inv[5] = m[0] * m[10] * m[15] - m[0] * m[11] * m[14] - m[8] * m[2] * m[15] + m[8] * m[3] * m[14] + m[12] * m[2] * m[11] - m[12] * m[3] * m[10];
2475
+ inv[9] = -m[0] * m[9] * m[15] + m[0] * m[11] * m[13] + m[8] * m[1] * m[15] - m[8] * m[3] * m[13] - m[12] * m[1] * m[11] + m[12] * m[3] * m[9];
2476
+ inv[13] = m[0] * m[9] * m[14] - m[0] * m[10] * m[13] - m[8] * m[1] * m[14] + m[8] * m[2] * m[13] + m[12] * m[1] * m[10] - m[12] * m[2] * m[9];
2477
+ inv[2] = m[1] * m[6] * m[15] - m[1] * m[7] * m[14] - m[5] * m[2] * m[15] + m[5] * m[3] * m[14] + m[13] * m[2] * m[7] - m[13] * m[3] * m[6];
2478
+ inv[6] = -m[0] * m[6] * m[15] + m[0] * m[7] * m[14] + m[4] * m[2] * m[15] - m[4] * m[3] * m[14] - m[12] * m[2] * m[7] + m[12] * m[3] * m[6];
2479
+ inv[10] = m[0] * m[5] * m[15] - m[0] * m[7] * m[13] - m[4] * m[1] * m[15] + m[4] * m[3] * m[13] + m[12] * m[1] * m[7] - m[12] * m[3] * m[5];
2480
+ inv[14] = -m[0] * m[5] * m[14] + m[0] * m[6] * m[13] + m[4] * m[1] * m[14] - m[4] * m[2] * m[13] - m[12] * m[1] * m[6] + m[12] * m[2] * m[5];
2481
+ inv[3] = -m[1] * m[6] * m[11] + m[1] * m[7] * m[10] + m[5] * m[2] * m[11] - m[5] * m[3] * m[10] - m[9] * m[2] * m[7] + m[9] * m[3] * m[6];
2482
+ inv[7] = m[0] * m[6] * m[11] - m[0] * m[7] * m[10] - m[4] * m[2] * m[11] + m[4] * m[3] * m[10] + m[8] * m[2] * m[7] - m[8] * m[3] * m[6];
2483
+ inv[11] = -m[0] * m[5] * m[11] + m[0] * m[7] * m[9] + m[4] * m[1] * m[11] - m[4] * m[3] * m[9] - m[8] * m[1] * m[7] + m[8] * m[3] * m[5];
2484
+ inv[15] = m[0] * m[5] * m[10] - m[0] * m[6] * m[9] - m[4] * m[1] * m[10] + m[4] * m[2] * m[9] + m[8] * m[1] * m[6] - m[8] * m[2] * m[5];
2485
+ let det = m[0] * inv[0] + m[1] * inv[4] + m[2] * inv[8] + m[3] * inv[12];
2486
+ if (Math.abs(det) < 1e-12) return null;
2487
+ det = 1 / det;
2488
+ for (let i = 0; i < 16; i++) inv[i] *= det;
2489
+ return inv;
2490
+ },
2491
+ transpose(m) {
2492
+ return [
2493
+ m[0],
2494
+ m[4],
2495
+ m[8],
2496
+ m[12],
2497
+ m[1],
2498
+ m[5],
2499
+ m[9],
2500
+ m[13],
2501
+ m[2],
2502
+ m[6],
2503
+ m[10],
2504
+ m[14],
2505
+ m[3],
2506
+ m[7],
2507
+ m[11],
2508
+ m[15]
2509
+ ];
2510
+ },
2511
+ projectPoint(m, p) {
2512
+ const x = p[0];
2513
+ const y = p[1];
2514
+ const z$2 = p[2];
2515
+ const X = m[0] * x + m[4] * y + m[8] * z$2 + m[12];
2516
+ const Y$1 = m[1] * x + m[5] * y + m[9] * z$2 + m[13];
2517
+ const Z = m[2] * x + m[6] * y + m[10] * z$2 + m[14];
2518
+ const W = m[3] * x + m[7] * y + m[11] * z$2 + m[15];
2519
+ const wInv = Math.abs(W) > 1e-6 ? 1 / W : 1;
2520
+ return [
2521
+ X * wInv,
2522
+ Y$1 * wInv,
2523
+ Z * wInv
2524
+ ];
2525
+ },
2526
+ projectRectangle(m, width, height) {
2527
+ return [
2528
+ Matrix4Math.projectPoint(m, [
2529
+ 0,
2530
+ 0,
2531
+ 0
2532
+ ]),
2533
+ Matrix4Math.projectPoint(m, [
2534
+ width,
2535
+ 0,
2536
+ 0
2537
+ ]),
2538
+ Matrix4Math.projectPoint(m, [
2539
+ width,
2540
+ height,
2541
+ 0
2542
+ ]),
2543
+ Matrix4Math.projectPoint(m, [
2544
+ 0,
2545
+ height,
2546
+ 0
2547
+ ])
2548
+ ];
2549
+ },
2550
+ toFloat32Array(m) {
2551
+ return new Float32Array(m);
2552
+ }
2553
+ };
2554
+ /**
2555
+ * WebGPU 3D Lighting & Material Shading engine.
2556
+ * Supports Ambient, Directional, Point, and Spot light sources,
2557
+ * normal matrix computation for non-uniform surface scaling,
2558
+ * and binary uniform buffer packing for WGSL shaders.
2559
+ */
2560
+ const MAX_LIGHTS = 8;
2561
+ const LIGHT_UNIFORM_SIZE_BYTES = 544;
2562
+ /**
2563
+ * Parses CSS / hex / rgb color strings, arrays, or color objects into normalized linear [r, g, b] floats in [0, 1].
2564
+ */
2565
+ function parseColorToRgb(color) {
2566
+ if (!color) return [
2567
+ 1,
2568
+ 1,
2569
+ 1
2570
+ ];
2571
+ if (Array.isArray(color)) {
2572
+ const r = color[0] ?? 1;
2573
+ const g = color[1] ?? 1;
2574
+ const b = color[2] ?? 1;
2575
+ return [
2576
+ r > 1 ? r / 255 : r,
2577
+ g > 1 ? g / 255 : g,
2578
+ b > 1 ? b / 255 : b
2579
+ ];
2580
+ }
2581
+ if (typeof color === "object" && color !== null) {
2582
+ const r = color.r ?? 1;
2583
+ const g = color.g ?? 1;
2584
+ const b = color.b ?? 1;
2585
+ return [
2586
+ r > 1 ? r / 255 : r,
2587
+ g > 1 ? g / 255 : g,
2588
+ b > 1 ? b / 255 : b
2589
+ ];
2590
+ }
2591
+ if (typeof color !== "string") return [
2592
+ 1,
2593
+ 1,
2594
+ 1
2595
+ ];
2596
+ const str = color.trim().toLowerCase();
2597
+ if (str.startsWith("#")) {
2598
+ const hex = str.slice(1);
2599
+ if (hex.length === 3) return [
2600
+ Number.parseInt(hex[0] + hex[0], 16) / 255,
2601
+ Number.parseInt(hex[1] + hex[1], 16) / 255,
2602
+ Number.parseInt(hex[2] + hex[2], 16) / 255
2603
+ ];
2604
+ if (hex.length >= 6) return [
2605
+ Number.parseInt(hex.slice(0, 2), 16) / 255,
2606
+ Number.parseInt(hex.slice(2, 4), 16) / 255,
2607
+ Number.parseInt(hex.slice(4, 6), 16) / 255
2608
+ ];
2609
+ }
2610
+ const rgbMatch = str.match(/rgba?\s*\(\s*([\d.]+)\s*,\s*([\d.]+)\s*,\s*([\d.]+)/);
2611
+ if (rgbMatch) {
2612
+ const r = Number.parseFloat(rgbMatch[1]) / 255;
2613
+ const g = Number.parseFloat(rgbMatch[2]) / 255;
2614
+ const b = Number.parseFloat(rgbMatch[3]) / 255;
2615
+ return [
2616
+ Math.max(0, Math.min(1, r)),
2617
+ Math.max(0, Math.min(1, g)),
2618
+ Math.max(0, Math.min(1, b))
2619
+ ];
2620
+ }
2621
+ const hslMatch = str.match(/hsla?\s*\(\s*([\d.]+)\s*,\s*([\d.]+)%?\s*,\s*([\d.]+)%?/);
2622
+ if (hslMatch) return hslToRgb(Number.parseFloat(hslMatch[1]) / 360, Number.parseFloat(hslMatch[2]) / 100, Number.parseFloat(hslMatch[3]) / 100);
2623
+ return [
2624
+ 1,
2625
+ 1,
2626
+ 1
2627
+ ];
2628
+ }
2629
+ function hslToRgb(h, s, l) {
2630
+ if (s === 0) return [
2631
+ l,
2632
+ l,
2633
+ l
2634
+ ];
2635
+ const hue2rgb = (p$1, q$1, t) => {
2636
+ let tt = t;
2637
+ if (tt < 0) tt += 1;
2638
+ if (tt > 1) tt -= 1;
2639
+ if (tt < 1 / 6) return p$1 + (q$1 - p$1) * 6 * tt;
2640
+ if (tt < 1 / 2) return q$1;
2641
+ if (tt < 2 / 3) return p$1 + (q$1 - p$1) * (2 / 3 - tt) * 6;
2642
+ return p$1;
2643
+ };
2644
+ const q = l < .5 ? l * (1 + s) : l + s - l * s;
2645
+ const p = 2 * l - q;
2646
+ return [
2647
+ hue2rgb(p, q, h + 1 / 3),
2648
+ hue2rgb(p, q, h),
2649
+ hue2rgb(p, q, h - 1 / 3)
2650
+ ];
2651
+ }
2652
+ const PlaybackContext = createContext({
2653
+ frame: 0,
2654
+ fps: 24,
2655
+ isPlaying: false
2656
+ });
2657
+ const PlaybackProvider = PlaybackContext.Provider;
2658
+ const useCurrentFrame = () => {
2659
+ return useContext(PlaybackContext).frame;
2660
+ };
2661
+ const useVideoConfig = () => {
2662
+ return {
2663
+ fps: useContext(PlaybackContext).fps,
2664
+ width: 1920,
2665
+ height: 1080
2666
+ };
2667
+ };
2668
+ const defaultSpringConfig = {
2669
+ damping: 10,
2670
+ mass: 1,
2671
+ stiffness: 100,
2672
+ overshootClamping: false
2673
+ };
2674
+ const advanceCache = {};
2675
+ function advance({ animation, now, config }) {
2676
+ const { toValue, lastTimestamp, current, velocity } = animation;
2677
+ const deltaTime = Math.min(now - lastTimestamp, 64);
2678
+ if (config.damping <= 0) throw new Error("Spring damping must be greater than 0, otherwise the spring() animation will never end, causing an infinite loop.");
2679
+ if (config.stiffness <= 0) throw new Error("Spring stiffness must be greater than 0.");
2680
+ if (config.mass <= 0) throw new Error("Spring mass must be greater than 0.");
2681
+ const c = config.damping;
2682
+ const m = config.mass;
2683
+ const k = config.stiffness;
2684
+ const cacheKey = [
2685
+ toValue,
2686
+ lastTimestamp,
2687
+ current,
2688
+ velocity,
2689
+ c,
2690
+ m,
2691
+ k,
2692
+ now
2693
+ ].join("-");
2694
+ if (advanceCache[cacheKey]) return advanceCache[cacheKey];
2695
+ const v0 = -velocity;
2696
+ const x0 = toValue - current;
2697
+ const zeta = c / (2 * Math.sqrt(k * m));
2698
+ const omega0 = Math.sqrt(k / m);
2699
+ const omega1 = omega0 * Math.sqrt(1 - zeta ** 2);
2700
+ const t = deltaTime / 1e3;
2701
+ const sin1 = Math.sin(omega1 * t);
2702
+ const cos1 = Math.cos(omega1 * t);
2703
+ const underDampedEnvelope = Math.exp(-zeta * omega0 * t);
2704
+ const underDampedFrag1 = underDampedEnvelope * (sin1 * ((v0 + zeta * omega0 * x0) / omega1) + x0 * cos1);
2705
+ const underDampedPosition = toValue - underDampedFrag1;
2706
+ const underDampedVelocity = zeta * omega0 * underDampedFrag1 - underDampedEnvelope * (cos1 * (v0 + zeta * omega0 * x0) - omega1 * x0 * sin1);
2707
+ const criticallyDampedEnvelope = Math.exp(-omega0 * t);
2708
+ const criticallyDampedPosition = toValue - criticallyDampedEnvelope * (x0 + (v0 + omega0 * x0) * t);
2709
+ const criticallyDampedVelocity = criticallyDampedEnvelope * (v0 * (t * omega0 - 1) + t * x0 * omega0 * omega0);
2710
+ const animationNode = {
2711
+ toValue,
2712
+ prevPosition: current,
2713
+ lastTimestamp: now,
2714
+ current: zeta < 1 ? underDampedPosition : criticallyDampedPosition,
2715
+ velocity: zeta < 1 ? underDampedVelocity : criticallyDampedVelocity
2716
+ };
2717
+ advanceCache[cacheKey] = animationNode;
2718
+ return animationNode;
2719
+ }
2720
+ const calculationCache = {};
2721
+ function springCalculation({ frame, fps, config = {} }) {
2722
+ const from = 0;
2723
+ const to = 1;
2724
+ const cacheKey = [
2725
+ frame,
2726
+ fps,
2727
+ config.damping,
2728
+ config.mass,
2729
+ config.overshootClamping,
2730
+ config.stiffness
2731
+ ].join("-");
2732
+ if (calculationCache[cacheKey]) return calculationCache[cacheKey];
2733
+ let animation = {
2734
+ lastTimestamp: 0,
2735
+ current: from,
2736
+ toValue: to,
2737
+ velocity: 0,
2738
+ prevPosition: 0
2739
+ };
2740
+ const frameClamped = Math.max(0, frame);
2741
+ const unevenRest = frameClamped % 1;
2742
+ for (let f = 0; f <= Math.floor(frameClamped); f++) {
2743
+ if (f === Math.floor(frameClamped)) f += unevenRest;
2744
+ const time = f / fps * 1e3;
2745
+ animation = advance({
2746
+ animation,
2747
+ now: time,
2748
+ config: {
2749
+ ...defaultSpringConfig,
2750
+ ...config
2751
+ }
2752
+ });
2753
+ }
2754
+ calculationCache[cacheKey] = animation;
2755
+ return animation;
2756
+ }
2757
+ const measureCache = /* @__PURE__ */ new Map();
2758
+ function measureSpring({ fps, config = {}, threshold = .005 }) {
2759
+ if (typeof threshold !== "number") throw new TypeError(`threshold must be a number, got ${threshold}`);
2760
+ if (threshold === 0) return Infinity;
2761
+ if (threshold === 1) return 0;
2762
+ if (Number.isNaN(threshold)) throw new TypeError("Threshold is NaN");
2763
+ if (!Number.isFinite(threshold)) throw new TypeError("Threshold is not finite");
2764
+ if (threshold < 0) throw new TypeError("Threshold is below 0");
2765
+ const cacheKey = [
2766
+ fps,
2767
+ config.damping,
2768
+ config.mass,
2769
+ config.overshootClamping,
2770
+ config.stiffness,
2771
+ threshold
2772
+ ].join("-");
2773
+ if (measureCache.has(cacheKey)) return measureCache.get(cacheKey);
2774
+ let frame = 0;
2775
+ let finishedFrame = 0;
2776
+ const calc = () => {
2777
+ return springCalculation({
2778
+ fps,
2779
+ frame,
2780
+ config
2781
+ });
2782
+ };
2783
+ let animation = calc();
2784
+ const calcDifference = () => {
2785
+ return Math.abs(animation.current - animation.toValue);
2786
+ };
2787
+ let difference = calcDifference();
2788
+ while (difference >= threshold) {
2789
+ frame++;
2790
+ animation = calc();
2791
+ difference = calcDifference();
2792
+ }
2793
+ finishedFrame = frame;
2794
+ for (let i = 0; i < 20; i++) {
2795
+ frame++;
2796
+ animation = calc();
2797
+ difference = calcDifference();
2798
+ if (difference >= threshold) {
2799
+ i = 0;
2800
+ finishedFrame = frame + 1;
2801
+ }
2802
+ }
2803
+ measureCache.set(cacheKey, finishedFrame);
2804
+ return finishedFrame;
2805
+ }
2806
+ function spring({ frame: passedFrame, fps, config = {}, from = 0, to = 1, durationInFrames: passedDurationInFrames, durationRestThreshold, delay = 0, reverse = false }) {
2807
+ const needsToCalculateNaturalDuration = reverse || typeof passedDurationInFrames !== "undefined";
2808
+ const naturalDuration = needsToCalculateNaturalDuration ? measureSpring({
2809
+ fps,
2810
+ config,
2811
+ threshold: durationRestThreshold
2812
+ }) : void 0;
2813
+ const naturalDurationGetter = needsToCalculateNaturalDuration ? { get: () => naturalDuration } : { get: () => {
2814
+ throw new Error("did not calculate natural duration");
2815
+ } };
2816
+ const delayProcessed = (reverse ? (passedDurationInFrames ?? naturalDurationGetter.get()) - passedFrame : passedFrame) + (reverse ? delay : -delay);
2817
+ const durationProcessed = passedDurationInFrames === void 0 ? delayProcessed : delayProcessed / (passedDurationInFrames / naturalDurationGetter.get());
2818
+ if (passedDurationInFrames && delayProcessed > passedDurationInFrames) return to;
2819
+ const spr = springCalculation({
2820
+ fps,
2821
+ frame: durationProcessed,
2822
+ config
2823
+ });
2824
+ const inner = config.overshootClamping ? to >= from ? Math.min(spr.current, to) : Math.max(spr.current, to) : spr.current;
2825
+ return from === 0 && to === 1 ? inner : interpolate(inner, [0, 1], [from, to]);
2826
+ }
2827
+ const cache = /* @__PURE__ */ new Map();
2828
+ function getCacheKey(text, style) {
2829
+ return JSON.stringify({
2830
+ text,
2831
+ ...style
2832
+ });
2833
+ }
2834
+ /**
2835
+ * Shared text measurer that uses SlugGeometry.measure for all environments.
2836
+ */
2837
+ function measureText(text, style) {
2838
+ const safeText = text ? String(text) : " ";
2839
+ const cacheKey = getCacheKey(safeText, style);
2840
+ const cached = cache.get(cacheKey);
2841
+ if (cached) return cached;
2842
+ const fontSize = style.fontSize ?? 48;
2843
+ let fontFamily = style.fontFamily ?? "Inter";
2844
+ if (SlugFontCache.isFailed(fontFamily)) fontFamily = "Inter";
2845
+ const letterSpacing = style.letterSpacing ?? 0;
2846
+ const calculatedLineHeight = style.lineHeight !== void 0 && style.lineHeight < 10 ? style.lineHeight * fontSize : style.lineHeight ?? fontSize * 1.2;
2847
+ const slugFont = SlugFontCache.getFont(fontFamily, style.fontWeight, null);
2848
+ const padding = style.padding ?? 0;
2849
+ if (!slugFont) {
2850
+ if (!SlugFontCache.isFailed(fontFamily)) {
2851
+ const fontUrl = GetFontAssetUrl(fontFamily);
2852
+ SlugFontCache.preloadSlugFont(null, fontFamily, fontUrl).catch(() => {});
2853
+ }
2854
+ const lines = safeText.split("\n");
2855
+ let totalWrappedLines = 0;
2856
+ const maxW = style.width !== void 0 ? Math.max(10, style.width - padding * 2) : 0;
2857
+ for (const line of lines) if (maxW > 0) {
2858
+ const estLineWidth = line.length * fontSize * .55 + letterSpacing * line.length;
2859
+ const wrapCount = Math.max(1, Math.ceil(estLineWidth / maxW));
2860
+ totalWrappedLines += wrapCount;
2861
+ } else totalWrappedLines += 1;
2862
+ const result$1 = {
2863
+ width: style.width !== void 0 ? style.width : Math.ceil(safeText.length * fontSize * .6 + letterSpacing * safeText.length) + 2 + padding * 2,
2864
+ height: style.height !== void 0 ? style.height : Math.ceil(totalWrappedLines * calculatedLineHeight) + 2 + padding * 2
2865
+ };
2866
+ cache.set(cacheKey, result$1);
2867
+ return result$1;
2868
+ }
2869
+ const measured = SlugGeometry.measure(safeText, slugFont, fontSize, letterSpacing, calculatedLineHeight, style.width !== void 0 ? Math.max(0, style.width - padding * 2) : void 0, style.spans);
2870
+ const result = {
2871
+ width: style.width !== void 0 && !style.keepNaturalWidth ? style.width : Math.ceil(measured.width) + padding * 2 + 8,
2872
+ height: style.height !== void 0 ? style.height : Math.ceil(measured.height) + padding * 2
2873
+ };
2874
+ cache.set(cacheKey, result);
2875
+ return result;
2876
+ }
2877
+ /**
2878
+ * Async variant of measureText. Preloads the Slug font and yields to the browser.
2879
+ */
2880
+ function measureTextAsync(text, style) {
2881
+ let fontFamily = style.fontFamily ?? "Inter";
2882
+ if (SlugFontCache.isFailed(fontFamily)) fontFamily = "Inter";
2883
+ if (SlugFontCache.isFailed(fontFamily)) return Promise.resolve(measureText(text, style));
2884
+ const fontUrl = GetFontAssetUrl(fontFamily);
2885
+ return SlugFontCache.preloadSlugFont(null, fontFamily, fontUrl).then(() => measureText(text, style)).catch(() => measureText(text, style));
2886
+ }
2887
+ if (typeof globalThis !== "undefined") globalThis.__GATEWAI_DELAYS__ = globalThis.__GATEWAI_DELAYS__ || /* @__PURE__ */ new Set();
2888
+ const hasDelayRender = () => {
2889
+ return useMemo(() => {
2890
+ let nextHandle = 0;
2891
+ return {
2892
+ delayRender: (_msg) => {
2893
+ const h = nextHandle++;
2894
+ if (typeof globalThis !== "undefined") globalThis.__GATEWAI_DELAYS__.add(h);
2895
+ return h;
2896
+ },
2897
+ continueRender: (h) => {
2898
+ if (typeof globalThis !== "undefined") globalThis.__GATEWAI_DELAYS__.delete(h);
2899
+ }
2900
+ };
2901
+ }, []);
2902
+ };
2903
+ /**
2904
+ * Preloads a font into the browser or headless environment.
2905
+ */
2906
+ async function preloadFont(family, url) {
2907
+ if (typeof window !== "undefined" && typeof FontFace !== "undefined") try {
2908
+ const loadedFace = await new FontFace(family, `url(${url})`).load();
2909
+ document.fonts.add(loadedFace);
2910
+ await document.fonts.ready;
2911
+ } catch (e) {
2912
+ console.warn(`[preloadFont] Failed to load font "${family}" from ${url}:`, e);
2913
+ }
2914
+ else await registerHeadlessFont(family, url);
2915
+ }
2916
+
2917
+ //#endregion
2918
+ //#region ../compositions/dist/layer-logic-B4xSPDvq.mjs
2919
+ const BASE_URL = getEnv("BASE_URL");
2920
+ const CDN_DOMAIN = getEnv("R2_CUSTOM_DOMAIN");
2921
+ /**
2922
+ * Compute render parameters for the CURRENT node only — does NOT recurse into
2923
+ * children. Each node in the VirtualMedia tree is responsible for exactly its
2924
+ * own operation; `SingleClipComposition` drives traversal via React recursion.
2925
+ *
2926
+ * This is the correct function to use inside the renderer.
2927
+ */
2928
+ function computeRenderParams(vv) {
2929
+ const op = vv.operation;
2930
+ const baseMeta = getActiveMediaMetadata(vv);
2931
+ const params = {
2932
+ sourceUrl: void 0,
2933
+ trimStartSec: 0,
2934
+ trimEndSec: null,
2935
+ speed: 1,
2936
+ cropRegion: null,
2937
+ flipH: false,
2938
+ flipV: false,
2939
+ rotation: 0,
2940
+ effectiveDurationSec: 0,
2941
+ mimeType: resolveMediaMimeType(vv) || null
2942
+ };
2943
+ switch (op.op) {
2944
+ case "text":
2945
+ params.sourceUrl = void 0;
2946
+ break;
2947
+ default:
2948
+ params.sourceUrl = resolveMediaSourceUrl(vv);
2949
+ break;
2950
+ }
2951
+ const sourceDurationSec = (baseMeta?.durationMs ?? 0) / 1e3;
2952
+ params.effectiveDurationSec = ((params.trimEndSec ?? sourceDurationSec) - params.trimStartSec) / params.speed;
2953
+ return params;
2954
+ }
2955
+ function secondsToFrames(seconds, fps) {
2956
+ return Math.round(seconds * fps);
2957
+ }
2958
+
2959
+ //#endregion
2960
+ //#region ../compositions/dist/index.mjs
2961
+ /**
2962
+ * normalizeTimeline recurses through the virtual media tree and "absorbs"
2963
+ * generic timeline-shifting operations into standard properties
2964
+ * of their children.
2965
+ */
2966
+ function normalizeTimeline(vv, fps, context = { timeOffsetSec: 0 }) {
2967
+ if (!vv) return vv;
2968
+ const { operation, metadata = {}, children = [] } = vv;
2969
+ const { timeOffsetSec, durationLimitMs } = context;
2970
+ if (!operation) return {
2971
+ ...vv,
2972
+ children: (children || []).map((child) => normalizeTimeline(child, fps, context))
2973
+ };
2974
+ const timeline = operation.timeline;
2975
+ const segments = timeline?.segments || [];
2976
+ let totalDurationMs = getActiveMediaMetadata(vv)?.durationMs ?? 0;
2977
+ if (segments.length > 0) {
2978
+ const totalSegmentsFrames = segments.reduce((acc, seg) => {
2979
+ const durSec = seg.endSec != null ? seg.endSec - seg.startSec : 0;
2980
+ return acc + Math.max(1, Math.round(durSec * fps));
2981
+ }, 0);
2982
+ if (totalSegmentsFrames > 0) totalDurationMs = totalSegmentsFrames / fps * 1e3;
2983
+ }
2984
+ const newOperation = { ...operation };
2985
+ const rawStartFrame = timeline?.startFrame ?? newOperation.startFrame;
2986
+ if (typeof rawStartFrame === "number") newOperation.startFrame = rawStartFrame - secondsToFrames(timeOffsetSec, fps);
2987
+ const newMetadata = {
2988
+ ...metadata,
2989
+ durationMs: totalDurationMs
2990
+ };
2991
+ const childContext = typeof rawStartFrame === "number" || newOperation.op === "compose" ? {
2992
+ timeOffsetSec: 0,
2993
+ durationLimitMs: void 0
2994
+ } : {
2995
+ timeOffsetSec,
2996
+ durationLimitMs
2997
+ };
2998
+ return {
2999
+ metadata: newMetadata,
3000
+ operation: newOperation,
3001
+ children: (children || []).map((child) => normalizeTimeline(child, fps, childContext))
3002
+ };
3003
+ }
3004
+ let mediaLogger = {
3005
+ info: (msg, ...args) => {
3006
+ if (typeof msg === "object" && msg !== null) console.info("[AudioExtractor]", msg, ...args);
3007
+ else console.info(`[AudioExtractor] ${msg}`, ...args);
3008
+ },
3009
+ warn: (msg, ...args) => {
3010
+ if (typeof msg === "object" && msg !== null) console.warn("[AudioExtractor]", msg, ...args);
3011
+ else console.warn(`[AudioExtractor] ${msg}`, ...args);
3012
+ },
3013
+ error: (msg, ...args) => {
3014
+ if (typeof msg === "object" && msg !== null) console.error("[AudioExtractor]", msg, ...args);
3015
+ else console.error(`[AudioExtractor] ${msg}`, ...args);
3016
+ }
3017
+ };
3018
+ if (typeof window === "undefined" || typeof process !== "undefined" && process.versions?.node) import(
3019
+ /* webpackIgnore: true */
3020
+ /* @vite-ignore */
3021
+ "@gitframes/server-utils"
3022
+ ).then((m) => {
3023
+ if (m.mediaLogger) mediaLogger = m.mediaLogger;
3024
+ }).catch(() => {});
3025
+ const yieldToMain$1 = () => {
3026
+ if (typeof window === "undefined") return Promise.resolve();
3027
+ const sched = globalThis.scheduler;
3028
+ return typeof sched?.yield === "function" ? sched.yield() : new Promise((resolve) => setTimeout(resolve, 0));
3029
+ };
3030
+ const waitForDelayRender$1 = async (timeoutMs = 3e4) => {
3031
+ await new Promise((resolve) => setTimeout(resolve, 100));
3032
+ const start = Date.now();
3033
+ return new Promise((resolve) => {
3034
+ const check = () => {
3035
+ const browserHandles = typeof window !== "undefined" && window.renderer_delayRenderHandles ? window.renderer_delayRenderHandles : [];
3036
+ const globalDelays = typeof globalThis !== "undefined" ? globalThis.__GATEWAI_DELAYS__ : null;
3037
+ const hasBrowserDelays = browserHandles.length > 0;
3038
+ const hasGlobalDelays = globalDelays instanceof Set && globalDelays.size > 0;
3039
+ if (!hasBrowserDelays && !hasGlobalDelays) {
3040
+ resolve();
3041
+ return;
3042
+ }
3043
+ if (Date.now() - start > timeoutMs) {
3044
+ mediaLogger.warn(`[AudioExtractor] waitForDelayRender timed out waiting for handles: ${JSON.stringify({
3045
+ browserHandles,
3046
+ globalDelays: globalDelays ? Array.from(globalDelays) : []
3047
+ })}`);
3048
+ resolve();
3049
+ return;
3050
+ }
3051
+ setTimeout(check, 50);
3052
+ };
3053
+ check();
3054
+ });
3055
+ };
3056
+ /**
3057
+ * Decodes all audio samples from a given source URL into raw Float32 mono/stereo channel buffers.
3058
+ */
3059
+ async function decodeAudioSource(url) {
3060
+ mediaLogger.info(`[AudioExtractor] decodeAudioSource started for url: ${url}`);
3061
+ try {
3062
+ if (typeof window !== "undefined" && typeof window.document !== "undefined" && !(typeof process !== "undefined" && process.versions && process.versions.node)) {
3063
+ mediaLogger.info(`[AudioExtractor] Browser detected. Fetching arrayBuffer for ${url}...`);
3064
+ const AudioContextClass = window.AudioContext || window.webkitAudioContext;
3065
+ if (!AudioContextClass) throw new Error("AudioContext is not supported in this browser");
3066
+ const audioCtx = new AudioContextClass();
3067
+ try {
3068
+ const response = await fetch(url);
3069
+ mediaLogger.info(`[AudioExtractor] Fetch response status for ${url}: ${response.status} (${response.statusText})`);
3070
+ if (!response.ok) throw new Error(`Failed to fetch audio source: ${response.statusText}`);
3071
+ const arrayBuffer = await response.arrayBuffer();
3072
+ mediaLogger.info(`[AudioExtractor] Fetched ${arrayBuffer.byteLength} bytes. Decoding audio data...`);
3073
+ const audioBuffer = await audioCtx.decodeAudioData(arrayBuffer);
3074
+ mediaLogger.info(`[AudioExtractor] Decoded successfully: ${audioBuffer.numberOfChannels} channels, sampleRate: ${audioBuffer.sampleRate}, duration: ${audioBuffer.duration}s`);
3075
+ const channels = [];
3076
+ for (let c = 0; c < audioBuffer.numberOfChannels; c++) channels.push(new Float32Array(audioBuffer.getChannelData(c)));
3077
+ return {
3078
+ channels,
3079
+ sampleRate: audioBuffer.sampleRate
3080
+ };
3081
+ } finally {
3082
+ audioCtx.close();
3083
+ mediaLogger.info(`[AudioExtractor] AudioContext closed for ${url}`);
3084
+ }
3085
+ }
3086
+ let inputAcquired = false;
3087
+ try {
3088
+ mediaLogger.info(`[AudioExtractor] Server / Node.js detected. Acquiring from inputStore for ${url}...`);
3089
+ const input = await inputStore.acquire(url);
3090
+ inputAcquired = true;
3091
+ mediaLogger.info(`[AudioExtractor] inputStore acquired. Retrieving primary audio track...`);
3092
+ const track = await input.getPrimaryAudioTrack();
3093
+ if (!track) {
3094
+ mediaLogger.warn(`[AudioExtractor] No primary audio track found in input for ${url}`);
3095
+ return null;
3096
+ }
3097
+ const generator = new AudioSampleSink(track).samples(0);
3098
+ const decodedPlanes = [];
3099
+ let totalFrames = 0;
3100
+ let sampleRate = 48e3;
3101
+ let numChannels = 1;
3102
+ mediaLogger.info(`[AudioExtractor] Starting mediabunny generator loop for ${url}...`);
3103
+ for await (const sample of generator) {
3104
+ sampleRate = sample.sampleRate;
3105
+ numChannels = sample.numberOfChannels;
3106
+ const sampleFrames = sample.numberOfFrames;
3107
+ if (decodedPlanes.length === 0) for (let c = 0; c < numChannels; c++) decodedPlanes.push([]);
3108
+ for (let c = 0; c < numChannels; c++) {
3109
+ let planeBuffer;
3110
+ try {
3111
+ const allocSize = sample.allocationSize({
3112
+ planeIndex: c,
3113
+ format: "f32-planar"
3114
+ });
3115
+ planeBuffer = new Float32Array(allocSize / 4);
3116
+ sample.copyTo(planeBuffer, {
3117
+ planeIndex: c,
3118
+ format: "f32-planar"
3119
+ });
3120
+ } catch {
3121
+ const allocSize = sample.allocationSize({ planeIndex: c });
3122
+ if (sample.format === "s16" || sample.format === "s16-planar") {
3123
+ const int16Buffer = new Int16Array(allocSize / 2);
3124
+ sample.copyTo(int16Buffer, { planeIndex: c });
3125
+ planeBuffer = new Float32Array(int16Buffer.length);
3126
+ for (let i = 0; i < int16Buffer.length; i++) planeBuffer[i] = (int16Buffer[i] ?? 0) / 32768;
3127
+ } else if (sample.format === "s32" || sample.format === "s32-planar") {
3128
+ const int32Buffer = new Int32Array(allocSize / 4);
3129
+ sample.copyTo(int32Buffer, { planeIndex: c });
3130
+ planeBuffer = new Float32Array(int32Buffer.length);
3131
+ for (let i = 0; i < int32Buffer.length; i++) planeBuffer[i] = (int32Buffer[i] ?? 0) / 2147483648;
3132
+ } else if (sample.format === "u8" || sample.format === "u8-planar") {
3133
+ const u8Buffer = new Uint8Array(allocSize);
3134
+ sample.copyTo(u8Buffer, { planeIndex: c });
3135
+ planeBuffer = new Float32Array(u8Buffer.length);
3136
+ for (let i = 0; i < u8Buffer.length; i++) planeBuffer[i] = ((u8Buffer[i] ?? 128) - 128) / 128;
3137
+ } else {
3138
+ planeBuffer = new Float32Array(allocSize / 4);
3139
+ sample.copyTo(planeBuffer, { planeIndex: c });
3140
+ }
3141
+ }
3142
+ decodedPlanes[c].push(planeBuffer);
3143
+ }
3144
+ totalFrames += sampleFrames;
3145
+ sample.close();
3146
+ }
3147
+ mediaLogger.info(`[AudioExtractor] Generator loop finished. Decoded total frames: ${totalFrames}`);
3148
+ if (totalFrames === 0 || decodedPlanes.length === 0) return null;
3149
+ return {
3150
+ channels: decodedPlanes.map((planes) => {
3151
+ const merged = new Float32Array(totalFrames);
3152
+ let offset = 0;
3153
+ for (const plane of planes) {
3154
+ merged.set(plane, offset);
3155
+ offset += plane.length;
3156
+ }
3157
+ return merged;
3158
+ }),
3159
+ sampleRate
3160
+ };
3161
+ } catch (err) {
3162
+ if (typeof process !== "undefined" && process.versions?.node) try {
3163
+ const { execSync } = await import("node:child_process");
3164
+ const stdout = execSync(`ffmpeg -v error -i "${url}" -f f32le -ar 48000 -ac 2 pipe:1`, { maxBuffer: 100 * 1024 * 1024 });
3165
+ const totalSamples = stdout.byteLength / 4;
3166
+ const numFrames = totalSamples / 2;
3167
+ const interleaved = new Float32Array(stdout.buffer, stdout.byteOffset, totalSamples);
3168
+ const left = new Float32Array(numFrames);
3169
+ const right = new Float32Array(numFrames);
3170
+ for (let i = 0; i < numFrames; i++) {
3171
+ left[i] = interleaved[i * 2];
3172
+ right[i] = interleaved[i * 2 + 1];
3173
+ }
3174
+ return {
3175
+ channels: [left, right],
3176
+ sampleRate: 48e3
3177
+ };
3178
+ } catch (ffmpegErr) {
3179
+ mediaLogger.warn({ ffmpegErr }, `[AudioExtractor] ffmpeg fallback decode failed for ${url}`);
3180
+ }
3181
+ mediaLogger.error({ err }, `[AudioExtractor] Failed to decode source ${url}`);
3182
+ return null;
3183
+ } finally {
3184
+ if (inputAcquired) inputStore.release(url);
3185
+ }
3186
+ } catch (err) {
3187
+ mediaLogger.error({ err }, `[AudioExtractor] Failed to decode source ${url}`);
3188
+ return null;
3189
+ }
3190
+ }
3191
+ /**
3192
+ * Resamples a PCM buffer using linear interpolation to target sample rate & speed.
3193
+ */
3194
+ async function resampleAndStretchPCM(inputChannel, sourceSampleRate, targetSampleRate, speed, outputLength, trimOffsetSamples) {
3195
+ const output = new Float32Array(outputLength);
3196
+ const factor = sourceSampleRate / targetSampleRate * speed;
3197
+ const YIELD_INTERVAL = 1e5;
3198
+ for (let i = 0; i < outputLength; i++) {
3199
+ if (i > 0 && i % YIELD_INTERVAL === 0) await yieldToMain$1();
3200
+ const srcIndex = trimOffsetSamples + i * factor;
3201
+ const indexFloor = Math.floor(srcIndex);
3202
+ const indexCeil = Math.ceil(srcIndex);
3203
+ if (indexFloor < 0) {
3204
+ output[i] = 0;
3205
+ continue;
3206
+ }
3207
+ if (indexFloor >= inputChannel.length) break;
3208
+ const weight = srcIndex - indexFloor;
3209
+ const sampleFloor = inputChannel[indexFloor] ?? 0;
3210
+ const val = sampleFloor + weight * ((indexCeil >= 0 && indexCeil < inputChannel.length ? inputChannel[indexCeil] ?? sampleFloor : sampleFloor) - sampleFloor);
3211
+ output[i] = Number.isFinite(val) ? val : 0;
3212
+ }
3213
+ return output;
3214
+ }
3215
+ /**
3216
+ * Recursively parses the VirtualMediaData tree and mixes all active audio tracks
3217
+ * into a single unified stereo PCM stream (Float32Array channels).
3218
+ */
3219
+ async function mixAudioTracks(virtualMedia, fps, targetSampleRate = 48e3, device, renderId) {
3220
+ mediaLogger.info(`[AudioExtractor] mixAudioTracks started. FPS: ${fps}, targetSampleRate: ${targetSampleRate}`);
3221
+ await waitForDelayRender$1();
3222
+ shaderStore.registerSignalsFromComposition(renderId, virtualMedia);
3223
+ const durationMs = getActiveMediaMetadata(virtualMedia)?.durationMs || 1e3;
3224
+ const totalDurationSec = durationMs / 1e3;
3225
+ const totalTargetSamples = Math.ceil(totalDurationSec * targetSampleRate);
3226
+ mediaLogger.info(`[AudioExtractor] Composition total duration: ${durationMs}ms (${totalDurationSec}s). Total mixed samples to allocate: ${totalTargetSamples}`);
3227
+ const outputLeft = new Float32Array(totalTargetSamples);
3228
+ const outputRight = new Float32Array(totalTargetSamples);
3229
+ const mixedChannels = [outputLeft, outputRight];
3230
+ const decodedCache = /* @__PURE__ */ new Map();
3231
+ async function traverse(node, inheritedSeekSec, inheritedClockFrames, inheritedVolume, inheritedSpeed, inheritedDurationFrames) {
3232
+ if (!node) return null;
3233
+ const op = node.operation;
3234
+ if (!op) {
3235
+ const durationMs$1 = getActiveMediaMetadata(node)?.durationMs ?? 0;
3236
+ const durationFrames = Math.round(durationMs$1 / 1e3 * fps);
3237
+ const actualDurationFrames = inheritedDurationFrames !== null ? durationFrames > 0 ? Math.min(inheritedDurationFrames, durationFrames) : inheritedDurationFrames : durationFrames;
3238
+ const localSamplesCount$1 = Math.ceil(actualDurationFrames / fps * targetSampleRate);
3239
+ if (localSamplesCount$1 <= 0) return null;
3240
+ const localChannels$1 = [new Float32Array(localSamplesCount$1), new Float32Array(localSamplesCount$1)];
3241
+ for (const child of node.children) {
3242
+ const childChannels = await traverse(child, inheritedSeekSec, inheritedClockFrames, inheritedVolume, inheritedSpeed, actualDurationFrames);
3243
+ if (childChannels) {
3244
+ const childOp = child.operation;
3245
+ const childStartFrame = childOp?.startFrame ?? childOp?.timeline?.startFrame ?? 0;
3246
+ const childStartSample = Math.round(childStartFrame / fps * targetSampleRate);
3247
+ for (let c = 0; c < 2; c++) {
3248
+ const dest = localChannels$1[c];
3249
+ const src = childChannels[c];
3250
+ const len = Math.min(src.length, dest.length - childStartSample);
3251
+ for (let i = 0; i < len; i++) if (childStartSample + i >= 0) dest[childStartSample + i] += src[i];
3252
+ }
3253
+ }
3254
+ }
3255
+ return localChannels$1;
3256
+ }
3257
+ const volume = inheritedVolume * (op.volume ?? 1) * (op.opacity === 0 ? 0 : 1) * (op.muted ? 0 : 1);
3258
+ const speedMultiplier = inheritedSpeed;
3259
+ const localClockFrames = inheritedClockFrames + (op.startFrame ?? op.timeline?.startFrame ?? 0);
3260
+ const segments = op.timeline?.segments || [];
3261
+ const nodeDurationMs = getActiveMediaMetadata(node)?.durationMs ?? 0;
3262
+ const nodeDurationFrames = Math.round(nodeDurationMs / 1e3 * fps);
3263
+ const nodeActualDurationFrames = inheritedDurationFrames !== null ? nodeDurationFrames > 0 ? Math.min(inheritedDurationFrames, nodeDurationFrames) : inheritedDurationFrames : nodeDurationFrames;
3264
+ const localSamplesCount = Math.ceil(nodeActualDurationFrames / fps * targetSampleRate);
3265
+ if (localSamplesCount <= 0) return null;
3266
+ const localLeft = new Float32Array(localSamplesCount);
3267
+ const localRight = new Float32Array(localSamplesCount);
3268
+ const localChannels = [localLeft, localRight];
3269
+ const processSegment = async (seg, windowStartFrameOffset) => {
3270
+ const totalSeekSec = inheritedSeekSec + (seg ? seg.startSec : 0);
3271
+ const totalClockFrames = localClockFrames + windowStartFrameOffset;
3272
+ let segDurationFrames = 0;
3273
+ if (seg) segDurationFrames = Math.max(1, Math.round(((seg.endSec ?? 0) - seg.startSec) * fps));
3274
+ else segDurationFrames = nodeDurationFrames;
3275
+ const actualDurationFrames = inheritedDurationFrames !== null ? Math.min(inheritedDurationFrames, segDurationFrames) : segDurationFrames;
3276
+ const segmentStartSample = Math.round(windowStartFrameOffset / fps * targetSampleRate);
3277
+ if (op.op === "source") {
3278
+ const mediaType = getMediaType(node);
3279
+ const params = computeRenderParams(node);
3280
+ if (params.sourceUrl && (mediaType === "Audio" || mediaType === "Video") && volume > 0) {
3281
+ const finalSpeed = Math.max(.5, speedMultiplier * (Number(params.speed) || 1));
3282
+ const effectiveTrimSec = Math.max(0, (Number(params.trimStartSec) || 0) + totalSeekSec);
3283
+ let decoded = decodedCache.get(params.sourceUrl);
3284
+ if (decoded === void 0) {
3285
+ decoded = await decodeAudioSource(params.sourceUrl);
3286
+ decodedCache.set(params.sourceUrl, decoded);
3287
+ }
3288
+ if (decoded) {
3289
+ const clipDurationSec = actualDurationFrames / fps;
3290
+ const clipSamplesCount = Math.min(localSamplesCount - segmentStartSample, Math.ceil(clipDurationSec * targetSampleRate));
3291
+ if (clipSamplesCount > 0 && segmentStartSample < localSamplesCount) {
3292
+ const trimOffsetSamples = Math.round(effectiveTrimSec * decoded.sampleRate);
3293
+ const srcLeft = decoded.channels[0];
3294
+ const srcRight = decoded.channels.length > 1 ? decoded.channels[1] : decoded.channels[0];
3295
+ const stretchedLeft = await resampleAndStretchPCM(srcLeft, decoded.sampleRate, targetSampleRate, finalSpeed, clipSamplesCount, trimOffsetSamples);
3296
+ const stretchedRight = await resampleAndStretchPCM(srcRight, decoded.sampleRate, targetSampleRate, finalSpeed, clipSamplesCount, trimOffsetSamples);
3297
+ for (let i = 0; i < clipSamplesCount; i++) {
3298
+ const outIndex = segmentStartSample + i;
3299
+ localLeft[outIndex] += stretchedLeft[i] * volume;
3300
+ localRight[outIndex] += stretchedRight[i] * volume;
3301
+ }
3302
+ }
3303
+ }
3304
+ await yieldToMain$1();
3305
+ }
3306
+ }
3307
+ if (node.children) for (const child of node.children) {
3308
+ const childChannels = await traverse(child, totalSeekSec, totalClockFrames, volume, speedMultiplier, actualDurationFrames);
3309
+ if (childChannels) {
3310
+ const childOp = child.operation;
3311
+ const childStartFrame = childOp?.startFrame ?? childOp?.timeline?.startFrame ?? 0;
3312
+ const destStartSample = segmentStartSample + Math.round(childStartFrame / fps * targetSampleRate);
3313
+ for (let c = 0; c < 2; c++) {
3314
+ const dest = localChannels[c];
3315
+ const src = childChannels[c];
3316
+ const len = Math.min(src.length, dest.length - destStartSample);
3317
+ for (let i = 0; i < len; i++) if (destStartSample + i >= 0) dest[destStartSample + i] += src[i];
3318
+ }
3319
+ }
3320
+ }
3321
+ const pluginProcessor = audioRegistry.get(op.op);
3322
+ if (pluginProcessor) try {
3323
+ const clipDurationSec = actualDurationFrames / fps;
3324
+ const clipSamplesCount = Math.min(localSamplesCount - segmentStartSample, Math.ceil(clipDurationSec * targetSampleRate));
3325
+ if (clipSamplesCount > 0 && segmentStartSample < localSamplesCount && segmentStartSample >= 0) await pluginProcessor(localChannels.map((ch) => ch.subarray(segmentStartSample, segmentStartSample + clipSamplesCount)), targetSampleRate, node, {
3326
+ device,
3327
+ frame: totalClockFrames,
3328
+ fps,
3329
+ renderId,
3330
+ elapsedMs: (totalClockFrames / fps + totalSeekSec) * 1e3,
3331
+ durationMs: getActiveMediaMetadata(node)?.durationMs ?? void 0
3332
+ });
3333
+ } catch (pluginErr) {
3334
+ mediaLogger.error({ err: pluginErr }, `[AudioExtractor] Custom plugin for operation '${op.op}' failed`);
3335
+ }
3336
+ };
3337
+ if (segments.length > 0) {
3338
+ let currentStart = 0;
3339
+ for (const seg of segments) {
3340
+ const durSec = (seg.endSec ?? 0) - seg.startSec;
3341
+ const durFrames = Math.max(1, Math.round(durSec * fps));
3342
+ await processSegment(seg, currentStart);
3343
+ currentStart += durFrames;
3344
+ }
3345
+ } else await processSegment(null, 0);
3346
+ return localChannels;
3347
+ }
3348
+ const rootChannels = await traverse(normalizeTimeline(virtualMedia, fps), 0, 0, 1, 1, null);
3349
+ if (rootChannels) for (let c = 0; c < 2; c++) {
3350
+ const dest = mixedChannels[c];
3351
+ const src = rootChannels[c];
3352
+ const len = Math.min(src.length, dest.length);
3353
+ for (let i = 0; i < len; i++) dest[i] = src[i];
3354
+ }
3355
+ decodedCache.clear();
3356
+ let mixPeak = 0;
3357
+ for (let i = 0; i < totalTargetSamples; i++) {
3358
+ const l = Math.abs(outputLeft[i] ?? 0);
3359
+ const r = Math.abs(outputRight[i] ?? 0);
3360
+ if (l > mixPeak) mixPeak = l;
3361
+ if (r > mixPeak) mixPeak = r;
3362
+ }
3363
+ const autoGain = mixPeak > .95 ? .92 / mixPeak : 1;
3364
+ for (let i = 0; i < totalTargetSamples; i++) {
3365
+ const l = (outputLeft[i] ?? 0) * autoGain;
3366
+ const r = (outputRight[i] ?? 0) * autoGain;
3367
+ outputLeft[i] = Number.isFinite(l) ? Math.tanh(l) : 0;
3368
+ outputRight[i] = Number.isFinite(r) ? Math.tanh(r) : 0;
3369
+ }
3370
+ return {
3371
+ channels: mixedChannels,
3372
+ sampleRate: targetSampleRate
3373
+ };
3374
+ }
3375
+ const RenderTimelineContext = createContext({
3376
+ accumulatedSeekOffsetSec: 0,
3377
+ accumulatedClockOffsetSec: 0
3378
+ });
3379
+ const RenderTimelineProvider = RenderTimelineContext.Provider;
3380
+ const AudioProcessorContext = React.createContext({ processors: [] });
3381
+ const DEFAULT_STATE = {
3382
+ frame: 0,
3383
+ fps: 30,
3384
+ isPlaying: false
3385
+ };
3386
+ var CompositionStateStore = class {
3387
+ states = /* @__PURE__ */ new Map();
3388
+ listeners = /* @__PURE__ */ new Map();
3389
+ setState(key, frame, fps, isPlaying) {
3390
+ const current = this.states.get(key);
3391
+ const resolvedPlaying = isPlaying !== void 0 ? isPlaying : current?.isPlaying ?? false;
3392
+ if (current?.frame === frame && current?.fps === fps && current?.isPlaying === resolvedPlaying) return;
3393
+ this.states.set(key, {
3394
+ frame,
3395
+ fps,
3396
+ isPlaying: resolvedPlaying
3397
+ });
3398
+ this.notify(key);
3399
+ }
3400
+ getState(key) {
3401
+ if (this.states.has(key)) return this.states.get(key);
3402
+ for (const [storeKey, value] of this.states.entries()) if (key.startsWith(storeKey)) return value;
3403
+ return DEFAULT_STATE;
3404
+ }
3405
+ subscribe(key, listener) {
3406
+ if (!this.listeners.has(key)) this.listeners.set(key, /* @__PURE__ */ new Set());
3407
+ this.listeners.get(key).add(listener);
3408
+ return () => {
3409
+ const set = this.listeners.get(key);
3410
+ set?.delete(listener);
3411
+ if (set?.size === 0) {
3412
+ this.listeners.delete(key);
3413
+ this.states.delete(key);
3414
+ }
3415
+ };
3416
+ }
3417
+ clearKey(key) {
3418
+ this.states.delete(key);
3419
+ }
3420
+ notify(key) {
3421
+ for (const [listenerKey, set] of this.listeners.entries()) if (listenerKey === key || listenerKey.startsWith(key)) set.forEach((l) => l());
3422
+ }
3423
+ };
3424
+ const compositionStateStore = new CompositionStateStore();
3425
+ const useCompositionState = (renderId) => {
3426
+ if (typeof globalThis !== "undefined" && globalThis.__IS_HEADLESS_RENDERER__ === true) return compositionStateStore.getState(renderId);
3427
+ return useSyncExternalStore(useCallback((onStoreChange) => compositionStateStore.subscribe(renderId, onStoreChange), [renderId]), () => compositionStateStore.getState(renderId), () => DEFAULT_STATE);
3428
+ };
3429
+ const segmentRangeCache = /* @__PURE__ */ new WeakMap();
3430
+ function getCachedSegmentRanges(segments, fps) {
3431
+ const cached = segmentRangeCache.get(segments);
3432
+ if (cached && cached.fps === fps) return cached.ranges;
3433
+ let currentStart = 0;
3434
+ const ranges = segments.map((seg) => {
3435
+ const durSec = (seg.endSec ?? 0) - seg.startSec;
3436
+ const durFrames = Math.max(1, Math.round(durSec * fps));
3437
+ const start = currentStart;
3438
+ currentStart += durFrames;
3439
+ return {
3440
+ seg,
3441
+ start,
3442
+ duration: durFrames
3443
+ };
3444
+ });
3445
+ segmentRangeCache.set(segments, {
3446
+ fps,
3447
+ ranges
3448
+ });
3449
+ return ranges;
3450
+ }
3451
+ const frameCtxPool = [];
3452
+ function acquireFrameContext() {
3453
+ return frameCtxPool.pop() ?? {};
3454
+ }
3455
+ /**
3456
+ * Effect operations carry every config field as a signal. Hand node renderers
3457
+ * the plain value for this frame (nested objects/arrays included), keeping only
3458
+ * GPU-bound signals intact since those are consumed as textures.
3459
+ */
3460
+ function resolveSignalOperation(virtualMedia, frameCtx) {
3461
+ const op = virtualMedia.operation;
3462
+ const resolved = { ...op };
3463
+ for (const [key, sig] of Object.entries(op.signals)) if (isSignal(sig) && !sig.gpuBinding) resolved[key] = sig.get(frameCtx);
3464
+ return {
3465
+ ...virtualMedia,
3466
+ operation: resolved
3467
+ };
3468
+ }
3469
+ async function drawCompositionTree(ctx, encoder, targetView, targetTexture, targetWidth, targetHeight, virtualMedia, props, inheritedSeekOffset = 0, inheritedClockOffset = 0) {
3470
+ const fps = props.fps ?? 24;
3471
+ const op = virtualMedia.operation;
3472
+ if (!op) return;
3473
+ const isCompositor = op.op === "Compositor";
3474
+ const frame = props.frame ?? 0;
3475
+ const segments = op.timeline?.segments || [];
3476
+ const localFrame = frame - (op.startFrame ?? op.timeline?.startFrame ?? 0);
3477
+ const isTimelineProvider = op.timeline !== void 0 || op.startFrame !== void 0 || op.op === "compose";
3478
+ const resolvedDurationMs = isTimelineProvider ? getActiveMediaMetadata(virtualMedia)?.durationMs ?? props.durationMs ?? void 0 : props.durationMs ?? getActiveMediaMetadata(virtualMedia)?.durationMs ?? void 0;
3479
+ if (!(op.op === "source" || op.op === "text" || op.op === "caption")) updateClockSignals(frame, fps, resolvedDurationMs);
3480
+ const frameCtx = acquireFrameContext();
3481
+ frameCtx.frame = frame;
3482
+ frameCtx.fps = fps;
3483
+ frameCtx.time = fps > 0 ? frame / fps : 0;
3484
+ frameCtx.duration = (resolvedDurationMs ?? 0) / 1e3;
3485
+ frameCtx.durationMs = resolvedDurationMs ?? 0;
3486
+ frameCtx.progress = resolvedDurationMs && resolvedDurationMs > 0 ? Math.max(0, Math.min(1, frame / fps * 1e3 / resolvedDurationMs)) : 0;
3487
+ frameCtx.deltaTime = fps > 0 ? 1 / fps : 0;
3488
+ const resolvedElapsedMs = isTimelineProvider ? (localFrame !== void 0 && fps !== void 0 ? localFrame / fps * 1e3 : void 0) ?? props.elapsedMs : props.elapsedMs ?? (localFrame !== void 0 && fps !== void 0 ? localFrame / fps * 1e3 : void 0);
3489
+ const effectiveLocalFrame = localFrame + Math.round(inheritedSeekOffset * fps);
3490
+ let activeSeg = null;
3491
+ let windowStartFrame = 0;
3492
+ if (segments.length > 0) {
3493
+ const active = getCachedSegmentRanges(segments, fps).find(({ start, duration }) => effectiveLocalFrame >= start && effectiveLocalFrame < start + duration);
3494
+ if (active) {
3495
+ activeSeg = active.seg;
3496
+ windowStartFrame = active.start;
3497
+ } else return;
3498
+ }
3499
+ const segmentLocalFrame = localFrame - windowStartFrame;
3500
+ const accumulatedSeekOffsetSec = inheritedSeekOffset + (activeSeg ? activeSeg.startSec - windowStartFrame / fps : 0);
3501
+ const accumulatedClockOffsetSec = inheritedClockOffset + windowStartFrame / fps;
3502
+ if (typeof op.onRequestFrame === "function") op.onRequestFrame(frameCtx);
3503
+ else if (op.effect && typeof op.effect.notifyFrame === "function") op.effect.notifyFrame(frameCtx);
3504
+ const CustomRenderer = webgpuRegistry.get(op.op);
3505
+ if (CustomRenderer) {
3506
+ if (op.signals) virtualMedia = resolveSignalOperation(virtualMedia, frameCtx);
3507
+ const inputs = op?.inputs;
3508
+ if (inputs) {
3509
+ for (const entry of Object.values(inputs)) if (entry?.connectionValid && entry?.outputItem?.type === "LUT") {
3510
+ const lutVirtualMedia = entry.outputItem.data;
3511
+ if (lutVirtualMedia && typeof lutVirtualMedia === "object" && lutVirtualMedia.operation) {
3512
+ const lutW = lutVirtualMedia.metadata?.width ?? targetWidth;
3513
+ const lutH = lutVirtualMedia.metadata?.height ?? targetHeight;
3514
+ const dummyTex = ctx.renderer.getTemporaryTexture(lutW, lutH);
3515
+ await drawCompositionTree(ctx, encoder, dummyTex.createView(), dummyTex, lutW, lutH, lutVirtualMedia, {
3516
+ ...props,
3517
+ virtualMedia: lutVirtualMedia
3518
+ }, accumulatedSeekOffsetSec, accumulatedClockOffsetSec);
3519
+ }
3520
+ } else if (entry?.connectionValid && entry?.outputItem?.type === "Signal") {
3521
+ const sigData = entry.outputItem.data;
3522
+ if (sigData && typeof sigData === "object") await signalRegistry.ensureAudioSourcesExtracted(ctx.device, sigData.signalConfig ?? sigData, fps, props.renderId);
3523
+ }
3524
+ }
3525
+ const pass = ctx.renderer.beginFrame(encoder, targetView, {
3526
+ r: 0,
3527
+ g: 0,
3528
+ b: 0,
3529
+ a: 0
3530
+ }, targetWidth, targetHeight, "load");
3531
+ let passEnded = false;
3532
+ const originalEnd = pass.end.bind(pass);
3533
+ pass.end = () => {
3534
+ if (!passEnded) {
3535
+ originalEnd();
3536
+ passEnded = true;
3537
+ }
3538
+ };
3539
+ const renderArgs = {
3540
+ ctx,
3541
+ encoder,
3542
+ pass,
3543
+ targetView,
3544
+ targetTexture,
3545
+ targetWidth,
3546
+ targetHeight,
3547
+ props: {
3548
+ ...props,
3549
+ virtualMedia,
3550
+ inheritedSeekOffset: accumulatedSeekOffsetSec,
3551
+ inheritedClockOffset: accumulatedClockOffsetSec,
3552
+ durationMs: resolvedDurationMs,
3553
+ elapsedMs: resolvedElapsedMs
3554
+ },
3555
+ drawChild: async (child, overrides, targetViewOverride, targetTextureOverride, targetWidthOverride, targetHeightOverride) => {
3556
+ await drawCompositionTree(ctx, encoder, targetViewOverride ?? targetView, targetTextureOverride ?? targetTexture, targetWidthOverride ?? targetWidth, targetHeightOverride ?? targetHeight, child, {
3557
+ ...props,
3558
+ durationMs: resolvedDurationMs,
3559
+ elapsedMs: resolvedElapsedMs,
3560
+ virtualMedia: child,
3561
+ ...overrides,
3562
+ excludeTextures: [
3563
+ ...props.excludeTextures || [],
3564
+ ...overrides?.excludeTextures || [],
3565
+ targetTextureOverride ?? targetTexture
3566
+ ]
3567
+ }, isCompositor ? 0 : accumulatedSeekOffsetSec, accumulatedClockOffsetSec);
3568
+ }
3569
+ };
3570
+ await CustomRenderer(renderArgs);
3571
+ renderArgs.pass.end();
3572
+ return;
3573
+ }
3574
+ if (op.op === "LUT") return;
3575
+ if (op.op === "Signal" || op.op === "SignalMath" || op.dataType === "Signal") {
3576
+ const pass = ctx.renderer.beginFrame(encoder, targetView, {
3577
+ r: 0,
3578
+ g: 0,
3579
+ b: 0,
3580
+ a: 0
3581
+ }, targetWidth, targetHeight, "load");
3582
+ await drawSignalNode(ctx, encoder, pass, {
3583
+ nodeId: op.nodeId || op.signalConfig?.nodeId || props.renderId,
3584
+ func: op.func,
3585
+ amplitude: Number(op.amplitude ?? 1),
3586
+ frequency: Number(op.frequency ?? 1),
3587
+ phase: Number(op.phase ?? 0),
3588
+ offset: Number(op.offset ?? 0),
3589
+ signalConfig: {
3590
+ ...op.signalConfig,
3591
+ ...op
3592
+ },
3593
+ frame,
3594
+ fps,
3595
+ elapsedMs: resolvedElapsedMs,
3596
+ durationMs: resolvedDurationMs ?? 0,
3597
+ width: targetWidth,
3598
+ height: targetHeight,
3599
+ drawChild: async (child, childProps, childView, childTex, childW, childH) => {
3600
+ await drawCompositionTree(ctx, encoder, childView, childTex, childW, childH, child, {
3601
+ ...props,
3602
+ durationMs: resolvedDurationMs,
3603
+ elapsedMs: resolvedElapsedMs,
3604
+ ...childProps,
3605
+ excludeTextures: [
3606
+ ...props.excludeTextures || [],
3607
+ ...childProps?.excludeTextures || [],
3608
+ childTex
3609
+ ]
3610
+ });
3611
+ }
3612
+ });
3613
+ pass.end();
3614
+ return;
3615
+ }
3616
+ if (op.op === "source" || op.op === "text" || op.op === "caption") {
3617
+ const pass = ctx.renderer.beginFrame(encoder, targetView, {
3618
+ r: 0,
3619
+ g: 0,
3620
+ b: 0,
3621
+ a: 0
3622
+ }, targetWidth, targetHeight, "load");
3623
+ if (op.op === "source") {
3624
+ const mediaType = getMediaType(virtualMedia);
3625
+ const params = computeRenderParams(virtualMedia);
3626
+ if (params.sourceUrl) {
3627
+ const containerWidth = props.containerWidth ?? ctx.surface.width;
3628
+ const containerHeight = props.containerHeight ?? ctx.surface.height;
3629
+ if (mediaType === "Image") await drawImageNode(ctx, pass, {
3630
+ src: params.sourceUrl,
3631
+ dstRect: {
3632
+ x: 0,
3633
+ y: 0,
3634
+ width: containerWidth,
3635
+ height: containerHeight
3636
+ },
3637
+ opacity: Number(op.opacity ?? 1) * Number(props.opacity ?? 1),
3638
+ isHeadless: props.isHeadless
3639
+ });
3640
+ else if (mediaType === "SVG") await drawSvgNode(ctx, pass, {
3641
+ src: params.sourceUrl,
3642
+ dstRect: {
3643
+ x: 0,
3644
+ y: 0,
3645
+ width: containerWidth,
3646
+ height: containerHeight
3647
+ },
3648
+ opacity: Number(op.opacity ?? 1),
3649
+ isHeadless: props.isHeadless
3650
+ });
3651
+ else if (mediaType === "Lottie") await drawLottieNode(ctx, pass, {
3652
+ src: params.sourceUrl,
3653
+ dstRect: {
3654
+ x: 0,
3655
+ y: 0,
3656
+ width: containerWidth,
3657
+ height: containerHeight
3658
+ },
3659
+ frame: segmentLocalFrame + Math.round(accumulatedSeekOffsetSec * fps),
3660
+ fps,
3661
+ opacity: Number(op.opacity ?? 1) * Number(props.opacity ?? 1),
3662
+ isHeadless: props.isHeadless
3663
+ });
3664
+ else if (mediaType === "GIF") await drawGifNode(ctx, pass, {
3665
+ src: params.sourceUrl,
3666
+ dstRect: {
3667
+ x: 0,
3668
+ y: 0,
3669
+ width: containerWidth,
3670
+ height: containerHeight
3671
+ },
3672
+ frame: segmentLocalFrame + Math.round(accumulatedSeekOffsetSec * fps),
3673
+ fps,
3674
+ opacity: Number(op.opacity ?? 1) * Number(props.opacity ?? 1),
3675
+ isHeadless: props.isHeadless
3676
+ });
3677
+ else if (mediaType === "Text") {
3678
+ const opText = { ...op };
3679
+ drawParagraphNode(ctx, pass, {
3680
+ text: opText.text || "",
3681
+ dstRect: {
3682
+ x: Number(opText.x ?? 0),
3683
+ y: Number(opText.y ?? 0),
3684
+ width: Number(opText.width ?? containerWidth),
3685
+ height: Number(opText.height ?? containerHeight)
3686
+ },
3687
+ fontFamily: opText.fontFamily,
3688
+ fontSize: Number(opText.fontSize ?? 48),
3689
+ color: opText.fill ?? opText.color ?? "white",
3690
+ width: opText.width ? Number(opText.width) : void 0,
3691
+ height: opText.height ? Number(opText.height) : void 0,
3692
+ lineHeight: Number(opText.lineHeight ?? 1.2),
3693
+ align: opText.align,
3694
+ fontWeight: opText.fontWeight,
3695
+ fontStyle: opText.fontStyle,
3696
+ letterSpacing: opText.letterSpacing,
3697
+ shadows: opText.shadows,
3698
+ textBackgroundColor: opText.textBackgroundColor,
3699
+ borderRadius: opText.borderRadius != null ? Number(opText.borderRadius) : void 0,
3700
+ padding: opText.padding != null ? Number(opText.padding) : void 0,
3701
+ stroke: opText.stroke,
3702
+ strokeWidth: opText.strokeWidth ?? opText.strokeThickness ? Number(opText.strokeWidth ?? opText.strokeThickness) : void 0,
3703
+ strokeAlign: opText.strokeAlign,
3704
+ opacity: Number(opText.opacity ?? 1) * Number(props.opacity ?? 1),
3705
+ isHeadless: props.isHeadless,
3706
+ animation: opText.animation ?? opText.textAnimation,
3707
+ animators: opText.animators,
3708
+ pathOptions: opText.pathOptions,
3709
+ typewriter: opText.typewriter,
3710
+ marquee: opText.marquee,
3711
+ textProgress: opText.textProgress != null ? Number(opText.textProgress) : void 0,
3712
+ signals: props.signals ?? opText.signals,
3713
+ firstMargin: opText.firstMargin != null ? Number(opText.firstMargin) : void 0,
3714
+ lastMargin: opText.lastMargin != null ? Number(opText.lastMargin) : void 0,
3715
+ baselineShift: opText.baselineShift != null ? Number(opText.baselineShift) : void 0,
3716
+ frame: localFrame,
3717
+ fps,
3718
+ durationMs: resolvedDurationMs,
3719
+ elapsedMs: resolvedElapsedMs,
3720
+ isVideoMode: props.isVideoMode
3721
+ });
3722
+ } else if (mediaType === "Video") {
3723
+ const finalPlaybackRate = Math.max(.01, (Number(props.playbackRateOverride) || 1) * (Number(params.speed) || 1));
3724
+ const effectiveTrimSec = Math.max(0, (Number(props.trimStartOverride) || 0) + (Number(params.trimStartSec) || 0) + accumulatedSeekOffsetSec);
3725
+ const rawTimestampSec = segmentLocalFrame / fps * finalPlaybackRate + effectiveTrimSec;
3726
+ const timestampSec = Math.max(0, Number.isFinite(rawTimestampSec) ? rawTimestampSec : 0);
3727
+ await drawVideoNode(ctx, pass, {
3728
+ nodeId: props.renderId,
3729
+ frameKey: `${props.renderId ?? "video"}-${params.sourceUrl}-${Math.round(timestampSec * fps)}`,
3730
+ sourceUrl: params.sourceUrl,
3731
+ timestampSec,
3732
+ dstRect: {
3733
+ x: 0,
3734
+ y: 0,
3735
+ width: containerWidth,
3736
+ height: containerHeight
3737
+ },
3738
+ opacity: Number(op.opacity ?? 1) * Number(props.opacity ?? 1),
3739
+ isHeadless: props.isHeadless,
3740
+ isPlaying: props.isPlaying,
3741
+ forceWait: props.forceWait
3742
+ });
3743
+ } else if (mediaType === "Caption") {
3744
+ const opCap = {
3745
+ ...op,
3746
+ ...props
3747
+ };
3748
+ await drawCaptionNode(ctx, pass, {
3749
+ renderId: props.renderId,
3750
+ src: params.sourceUrl,
3751
+ dstRect: {
3752
+ x: 0,
3753
+ y: 0,
3754
+ width: containerWidth,
3755
+ height: containerHeight
3756
+ },
3757
+ frame: localFrame,
3758
+ fps,
3759
+ fontFamily: opCap.fontFamily,
3760
+ fontSize: Number(opCap.fontSize ?? 48),
3761
+ color: opCap.fill ?? opCap.color ?? "white",
3762
+ lineHeight: Number(opCap.lineHeight ?? 1.2),
3763
+ align: opCap.align === "start" ? "left" : opCap.align === "end" ? "right" : opCap.align ?? "center",
3764
+ textAlignVertical: opCap.verticalAlign ?? opCap.textAlignVertical,
3765
+ fontWeight: opCap.fontWeight,
3766
+ fontStyle: opCap.fontStyle,
3767
+ letterSpacing: opCap.letterSpacing,
3768
+ shadows: opCap.shadows,
3769
+ textBackgroundColor: opCap.background ?? opCap.textBackgroundColor,
3770
+ borderRadius: (opCap.borderRadius ?? opCap.strokeRadius) != null ? Number(opCap.borderRadius ?? opCap.strokeRadius) : void 0,
3771
+ strokeRadius: (opCap.strokeRadius ?? opCap.borderRadius) != null ? Number(opCap.strokeRadius ?? opCap.borderRadius) : void 0,
3772
+ stroke: opCap.stroke,
3773
+ strokeWidth: (opCap.strokeWidth ?? opCap.strokeThickness) != null ? Number(opCap.strokeWidth ?? opCap.strokeThickness) : void 0,
3774
+ maxWidth: opCap.maxWidth,
3775
+ padding: opCap.padding != null ? Number(opCap.padding) : void 0,
3776
+ opacity: Number(opCap.opacity ?? 1) * Number(props.opacity ?? 1),
3777
+ isHeadless: props.isHeadless,
3778
+ isVideoMode: props.isVideoMode
3779
+ });
3780
+ }
3781
+ }
3782
+ } else if (op.op === "text") {
3783
+ const opText = { ...op };
3784
+ const containerWidth = props.containerWidth ?? ctx.surface.width;
3785
+ const containerHeight = props.containerHeight ?? ctx.surface.height;
3786
+ drawParagraphNode(ctx, pass, {
3787
+ renderId: props.renderId,
3788
+ text: opText.text || "",
3789
+ dstRect: {
3790
+ x: Number(opText.x ?? 0),
3791
+ y: Number(opText.y ?? 0),
3792
+ width: Number(opText.width ?? containerWidth),
3793
+ height: Number(opText.height ?? containerHeight)
3794
+ },
3795
+ spans: opText.spans,
3796
+ fontFamily: opText.fontFamily,
3797
+ fontSize: Number(opText.fontSize ?? 48),
3798
+ color: opText.fill ?? opText.color ?? "white",
3799
+ width: opText.width ? Number(opText.width) : void 0,
3800
+ height: opText.height ? Number(opText.height) : void 0,
3801
+ lineHeight: Number(opText.lineHeight ?? 1.2),
3802
+ align: opText.align,
3803
+ fontWeight: opText.fontWeight,
3804
+ fontStyle: opText.fontStyle,
3805
+ letterSpacing: opText.letterSpacing,
3806
+ shadows: opText.shadows,
3807
+ textBackgroundColor: opText.textBackgroundColor,
3808
+ borderRadius: (opText.borderRadius ?? opText.strokeRadius) != null ? Number(opText.borderRadius ?? opText.strokeRadius) : void 0,
3809
+ strokeRadius: (opText.strokeRadius ?? opText.borderRadius) != null ? Number(opText.strokeRadius ?? opText.borderRadius) : void 0,
3810
+ padding: opText.padding != null ? Number(opText.padding) : void 0,
3811
+ stroke: opText.stroke,
3812
+ strokeWidth: opText.strokeWidth ?? opText.strokeThickness ? Number(opText.strokeWidth ?? opText.strokeThickness) : void 0,
3813
+ strokeAlign: opText.strokeAlign,
3814
+ opacity: Number(opText.opacity ?? 1) * Number(props.opacity ?? 1),
3815
+ isHeadless: props.isHeadless,
3816
+ animation: opText.animation ?? opText.textAnimation,
3817
+ animators: opText.animators,
3818
+ pathOptions: opText.pathOptions,
3819
+ typewriter: opText.typewriter,
3820
+ marquee: opText.marquee,
3821
+ signals: props.signals ?? opText.signals,
3822
+ firstMargin: opText.firstMargin != null ? Number(opText.firstMargin) : void 0,
3823
+ lastMargin: opText.lastMargin != null ? Number(opText.lastMargin) : void 0,
3824
+ baselineShift: opText.baselineShift != null ? Number(opText.baselineShift) : void 0,
3825
+ frame: localFrame,
3826
+ fps,
3827
+ durationMs: resolvedDurationMs,
3828
+ elapsedMs: resolvedElapsedMs,
3829
+ isVideoMode: props.isVideoMode
3830
+ });
3831
+ }
3832
+ pass.end();
3833
+ }
3834
+ if (virtualMedia.children && virtualMedia.children.length > 0) for (let i = 0; i < virtualMedia.children.length; i++) {
3835
+ const child = virtualMedia.children[i];
3836
+ if (child) await drawCompositionTree(ctx, encoder, targetView, targetTexture, targetWidth, targetHeight, child, {
3837
+ ...props,
3838
+ durationMs: resolvedDurationMs,
3839
+ elapsedMs: resolvedElapsedMs,
3840
+ virtualMedia: child,
3841
+ renderId: `${props.renderId}-c${i}`
3842
+ }, isCompositor ? 0 : accumulatedSeekOffsetSec, accumulatedClockOffsetSec);
3843
+ }
3844
+ }
3845
+ const CanvasComposition = memo((props) => {
3846
+ const ctx = useRenderContext();
3847
+ const { renderId, virtualMedia: rawVirtualMedia } = props;
3848
+ const state = useCompositionState(renderId);
3849
+ const { virtualMedia, fps } = React.useMemo(() => {
3850
+ const fpsValue = props.fps ?? state.fps;
3851
+ return {
3852
+ virtualMedia: normalizeTimeline(rawVirtualMedia, fpsValue),
3853
+ fps: fpsValue
3854
+ };
3855
+ }, [
3856
+ rawVirtualMedia,
3857
+ state.fps,
3858
+ props.fps
3859
+ ]);
3860
+ const frame = props.frame ?? state.frame;
3861
+ const isMountedRef = React.useRef(false);
3862
+ const latestTaskRef = React.useRef(null);
3863
+ const isRenderingRef = React.useRef(false);
3864
+ const [fontUpdateTick, forceUpdate] = React.useState(0);
3865
+ React.useEffect(() => {
3866
+ return SlugFontCache.addListener(() => {
3867
+ if (isMountedRef.current) forceUpdate((t) => t + 1);
3868
+ });
3869
+ }, []);
3870
+ React.useEffect(() => {
3871
+ const cdnDomain = getEnv("R2_CUSTOM_DOMAIN");
3872
+ if (!cdnDomain) {
3873
+ console.error("[CanvasComposition] R2_CUSTOM_DOMAIN is not defined.");
3874
+ return;
3875
+ }
3876
+ const url = `https://${cdnDomain}/static/NotoColorEmoji.ttf`;
3877
+ SlugFontCache.preloadEmojiFontFace(url).catch((err) => {
3878
+ console.error("[CanvasComposition] Failed to preload emoji font:", err);
3879
+ });
3880
+ }, []);
3881
+ const [lutUpdateTick, setLutUpdateTick] = React.useState(0);
3882
+ React.useEffect(() => {
3883
+ return lutStore.onChange(() => {
3884
+ if (isMountedRef.current) setLutUpdateTick((t) => t + 1);
3885
+ });
3886
+ }, []);
3887
+ React.useLayoutEffect(() => {
3888
+ isMountedRef.current = true;
3889
+ return () => {
3890
+ isMountedRef.current = false;
3891
+ mediaDecoderCache.clearNode(renderId);
3892
+ clearVideoNodeState(renderId, ctx?.device);
3893
+ if (typeof globalThis !== "undefined" && globalThis.__GATEWAI_DELAYS__) {
3894
+ const delays = globalThis.__GATEWAI_DELAYS__;
3895
+ if (delays instanceof Set) {
3896
+ for (const handle of delays) if (typeof handle === "string" && handle.startsWith(renderId)) delays.delete(handle);
3897
+ }
3898
+ }
3899
+ if (typeof window !== "undefined" && window.renderer_delayRenderHandles) {
3900
+ const handles = window.renderer_delayRenderHandles;
3901
+ if (Array.isArray(handles)) {
3902
+ const filtered = handles.filter((handle) => !(typeof handle === "string" && handle.startsWith(renderId)));
3903
+ window.renderer_delayRenderHandles = filtered;
3904
+ }
3905
+ }
3906
+ };
3907
+ }, [renderId, ctx]);
3908
+ React.useLayoutEffect(() => {
3909
+ if (!ctx || !virtualMedia) return;
3910
+ latestTaskRef.current = {
3911
+ ctx,
3912
+ virtualMedia,
3913
+ frame,
3914
+ fps,
3915
+ props
3916
+ };
3917
+ const triggerRender = async () => {
3918
+ if (isRenderingRef.current) return;
3919
+ isRenderingRef.current = true;
3920
+ while (latestTaskRef.current && isMountedRef.current) {
3921
+ const task = latestTaskRef.current;
3922
+ if (task.ctx !== ctx) break;
3923
+ latestTaskRef.current = null;
3924
+ try {
3925
+ const { device, surface, renderer } = task.ctx;
3926
+ const targetW = surface.width;
3927
+ const targetH = surface.height;
3928
+ if (targetW === 0 || targetH === 0) continue;
3929
+ const encoder = device.createCommandEncoder();
3930
+ const stableTex = renderer.getTemporaryTexture(targetW, targetH);
3931
+ const stableView = stableTex.createView();
3932
+ renderer.beginFrame(encoder, stableView, {
3933
+ r: 0,
3934
+ g: 0,
3935
+ b: 0,
3936
+ a: 0
3937
+ }, targetW, targetH, "clear").end();
3938
+ await drawCompositionTree(task.ctx, encoder, stableView, stableTex, targetW, targetH, task.virtualMedia, {
3939
+ ...task.props,
3940
+ frame: task.frame,
3941
+ fps: task.fps,
3942
+ isPlaying: task.props.isPlaying ?? state.isPlaying,
3943
+ isVideoMode: task.virtualMedia.operation.dataType === "Video",
3944
+ excludeTextures: [stableTex]
3945
+ });
3946
+ if (latestTaskRef.current !== null) continue;
3947
+ if (isMountedRef.current && !renderer.isDestroyed) {
3948
+ const blitW = surface.width;
3949
+ const blitH = surface.height;
3950
+ if (blitW > 0 && blitH > 0) {
3951
+ const canvasView = surface.getCurrentTextureView();
3952
+ const blitPass = renderer.beginFrame(encoder, canvasView, {
3953
+ r: 0,
3954
+ g: 0,
3955
+ b: 0,
3956
+ a: 0
3957
+ }, blitW, blitH, "clear");
3958
+ renderer.drawTexture(blitPass, stableTex, {
3959
+ x: 0,
3960
+ y: 0,
3961
+ width: blitW,
3962
+ height: blitH
3963
+ });
3964
+ blitPass.end();
3965
+ }
3966
+ device.queue.submit([encoder.finish()]);
3967
+ surface.present();
3968
+ const key = `${task.props.renderId}-${task.frame}`;
3969
+ if (!window.__frameRenderResolvers) window.__frameRenderResolvers = /* @__PURE__ */ new Map();
3970
+ const status = window.__frameRenderResolvers.get(key);
3971
+ if (status) {
3972
+ status.rendered = true;
3973
+ if (status.resolver) status.resolver();
3974
+ } else window.__frameRenderResolvers.set(key, { rendered: true });
3975
+ }
3976
+ } catch (err) {
3977
+ console.error("Error drawing WebGPU composition tree:", err);
3978
+ }
3979
+ }
3980
+ isRenderingRef.current = false;
3981
+ if (latestTaskRef.current && isMountedRef.current) triggerRender();
3982
+ };
3983
+ triggerRender();
3984
+ }, [
3985
+ ctx,
3986
+ virtualMedia,
3987
+ frame,
3988
+ fps,
3989
+ props,
3990
+ fontUpdateTick,
3991
+ lutUpdateTick
3992
+ ]);
3993
+ return null;
3994
+ });
3995
+ /**
3996
+ * browser-video-renderer.tsx
3997
+ *
3998
+ * Architecture
3999
+ * ────────────
4000
+ * ┌─────────────────────────────────────────────────────────────────┐
4001
+ * │ AudioMuxer (no React deps) │
4002
+ * │ · subscribe / getSnapshot → useSyncExternalStore contract │
4003
+ * │ · run() → mixing + encoding with cooperative yielding │
4004
+ * │ · abort() → AbortSignal cancellation │
4005
+ * └─────────────────────────────────────────────────────────────────┘
4006
+ * ▲ useSyncExternalStore
4007
+ * ┌─────────────────────────────────────────────────────────────────┐
4008
+ * │ BrowserVideoRendererInner │
4009
+ * │ Effect 1: muxer init (output, sources, OPFS target) │
4010
+ * │ Effect 2: video frame loop → setVideoFramesDone(true) │
4011
+ * │ Effect 3: audio kick-off when videoFramesDone │
4012
+ * │ Effect 4: finalization when audioState.phase === "done" │
4013
+ * │ Effect 5: progress bridge audioState → onProgress prop │
4014
+ * │ Effect 6: error bridge audioState → onError prop │
4015
+ * └─────────────────────────────────────────────────────────────────┘
4016
+ *
4017
+ * Why FPS dropped to 2-3 during audio processing (and how it's fixed)
4018
+ * ────────────────────────────────────────────────────────────────────
4019
+ *
4020
+ * ROOT CAUSE 1 — Main-thread monopoly
4021
+ * The old audio chunk loop ran entirely on the JS main thread. Each
4022
+ * iteration allocated a Float32Array, interleaved channel data, and
4023
+ * called `await audioSource.add()`. Consecutive microtask continuations
4024
+ * never returned control to the browser's rendering pipeline; the
4025
+ * compositor thread was starved waiting for a JS task to complete before
4026
+ * it could issue a new paint frame.
4027
+ *
4028
+ * ROOT CAUSE 2 — React scheduler saturation
4029
+ * `onProgress("audio", pct)` was called on every chunk (up to ~100×/sec
4030
+ * for long clips). Each call triggered a parent setState, enqueuing a
4031
+ * React reconciliation. When updates arrived faster than the reconciler
4032
+ * could drain them, React degraded to synchronous rendering to clear the
4033
+ * backlog — which blocked the main thread even longer.
4034
+ *
4035
+ * ROOT CAUSE 3 — AudioEncoder backpressure
4036
+ * mediabunny's AudioSampleSource.add() awaits the encoder's internal
4037
+ * queue. Under load it parks the caller in a microtask continuation and
4038
+ * immediately resumes on the same task. The browser task scheduler never
4039
+ * saw an idle slot to run its rendering pipeline.
4040
+ *
4041
+ * ROOT CAUSE 4 — Entangled concerns
4042
+ * Audio encoding lived inside the video frame useEffect. The render loop
4043
+ * and the encoder competed for the same task slot and the same React
4044
+ * reconciliation pass.
4045
+ *
4046
+ * FIX — yieldToMain() between every audio chunk
4047
+ * `scheduler.yield()` (Chrome 115+) is the correct primitive: it
4048
+ * explicitly yields to the browser's task queue at "user-blocking"
4049
+ * priority, guaranteeing a paint opportunity between chunks. The
4050
+ * `setTimeout(0)` fallback provides equivalent semantics on older
4051
+ * browsers (>4 ms clamped, sufficient for ~60 FPS).
4052
+ * Progress notifications are throttled to ≤12 updates/sec to avoid
4053
+ * overwhelming the React scheduler.
4054
+ */
4055
+ const isNode = typeof process !== "undefined" && Boolean(process.versions?.node);
4056
+ if (typeof window !== "undefined" && !isNode) canEncodeAudio("mp3").then((supported) => {
4057
+ if (!supported) registerMp3Encoder();
4058
+ }).catch(() => {});
4059
+
4060
+ //#endregion
4061
+ export { DataTypes as $, frameArraySignal as $t, ImageResultSchema as A, TextOperationSchema as At, BaseMediaOperationPropsSchema as B, VideoTimingPropsSchema as Bt, preloadFont as C, SignalDataSchema as Ct, useCurrentFrame as D, SingleOutputGenericSchema as Dt, springCalculation as E, SignalStatsSchema as Et, defineRenderer as F, VideoAnimationSchema as Ft, ConfigHandleSchema as G, computed as Gt, CAPTION_LAYER_DEFAULTS as H, appendOperation as Ht, registerWebGPURenderer as I, VideoLayerTypeSchema as It, DEFAULT_DURATION_MS as J, createVirtualMedia as Jt, ConfigSchemaRegistry as K, cosineSignal as Kt, AnimationTypeSchema as L, VideoSpatialPropsSchema as Lt, VideoResultSchema as M, TimelineTransformSchema as Mt, configBuilder as N, TransitionSchema as Nt, useVideoConfig as O, SourceOperationSchema as Ot, audioRegistry as P, TransitionTypeSchema as Pt, DataTypeVal as Q, findSourceAsset as Qt, AnyOutputUnionSchema as R, VideoStylePropsSchema as Rt, parseColorToRgb as S, updateClockSignals as Sn, SignalChannelMetaSchema as St, spring as T, SignalMathDataSchema as Tt, COMPOSITE_OPERATIONS$1 as U, checkNodeResultCache as Ut, BaseVideoLayerPropsSchema as V, VirtualMediaDataSchema as Vt, COMPOSITOR_DEFAULTS as W, computeVideoCropRenderProps as Wt, DEFAULT_MEDIA_DIMENSION as X, deserializeSignalData as Xt, DEFAULT_FPS as Y, dataTypeColors as Yt, DataType as Z, extractSvgDimensions as Zt, interpolate as _, signal as _n, NodeResultSchema as _t, mixAudioTracks as a, getLeafMediaType as an, GeneratorSignalDataSchema as at, measureText as b, stampResult as bn, ProgrammaticSignal as bt, LIGHT_UNIFORM_SIZE_BYTES as c, hasOnlySingleSource as cn, JsonValueSchema as ct, PlaybackContext as d, isVirtualMediaData as dn, MediaOperationSchema as dt, frameSignal as en, Effect as et, PlaybackProvider as f, programmaticSignal as fn, MediaSourceSchema as ft, hasDelayRender as g, serializeSignalData as gn, NodeMetadataSchema as gt, computeStrokeRect as h, resolveMediaMimeType as hn, MultiOutputGenericSchema as ht, drawCompositionTree as i, getFingerprint as in, FileDataSchema as it, SignalResultSchema as j, TimelineSegmentSchema as jt, ColorSchema as k, TEXT_LAYER_DEFAULTS as kt, MAX_LIGHTS as l, isEqual as ln, MIME_TYPES as lt, bezier as m, registerSignalDependency as mn, ModerationUnrefundableError as mt, compositionStateStore as n, getActiveMediaMetadata as nn, ExtendedLayerSchema as nt, useCompositionState as o, getMediaType as on, GlobalCompositeOperation$1 as ot, Vector3Math as p, progressSignal as pn, ModerationError as pt, DATA_TYPE_EXTENSIONS as q, createOutputItemSchema as qt, decodeAudioSource as r, getFileDataMetadata as rn, FileAssetSchema as rt, Easing as s, getRenderFingerprint as sn, HandleTypeVal as st, CanvasComposition as t, generateId as tn, ExportResultSchema as tt, Matrix4Math as u, isSignal as un, MediaMetadataSchema as ut, mapBlendModeToSkia as v, signalBuilder as vn, NodeVisualConfigSchema as vt, requiresIsolation as w, SignalGateDataSchema as wt, measureTextAsync as x, timeSignal as xn, Signal as xt, measureSpring as y, sineSignal as yn, OutputItemSchema as yt, AudioExtractorSignalDataSchema as z, VideoTextPropsSchema as zt };
4062
+ //# sourceMappingURL=dist-CMF9eNLB.mjs.map