pixi-effects 0.47.1 → 0.47.2

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (68) hide show
  1. package/CHANGELOG.md +13 -0
  2. package/README.md +5 -5
  3. package/ai/chat-template.html +3 -3
  4. package/dist/Composition-X6MCEFRH.cjs +16 -0
  5. package/dist/{Composition-AZEATHJB.cjs.map → Composition-X6MCEFRH.cjs.map} +1 -1
  6. package/dist/Composition-ZYMQ7ZRC.js +7 -0
  7. package/dist/{Composition-ME3L3EFT.js.map → Composition-ZYMQ7ZRC.js.map} +1 -1
  8. package/dist/{Spans-LTKEUHDN.cjs → Spans-6HSLKR5Q.cjs} +11 -11
  9. package/dist/{Spans-LTKEUHDN.cjs.map → Spans-6HSLKR5Q.cjs.map} +1 -1
  10. package/dist/{Spans-YV7K4X5I.js → Spans-LCCZ4PW7.js} +6 -6
  11. package/dist/{Spans-YV7K4X5I.js.map → Spans-LCCZ4PW7.js.map} +1 -1
  12. package/dist/{blendModes-56NHK2MP.js → blendModes-EMANEHZS.js} +4 -4
  13. package/dist/{blendModes-56NHK2MP.js.map → blendModes-EMANEHZS.js.map} +1 -1
  14. package/dist/{blendModes-ONOPMMAB.cjs → blendModes-JUISKPCA.cjs} +5 -5
  15. package/dist/{blendModes-ONOPMMAB.cjs.map → blendModes-JUISKPCA.cjs.map} +1 -1
  16. package/dist/{chunk-NEMWRJQS.cjs → chunk-2YV7ISX6.cjs} +4 -4
  17. package/dist/chunk-2YV7ISX6.cjs.map +1 -0
  18. package/dist/{chunk-BFERTUXK.cjs → chunk-4RC4GXEM.cjs} +92 -92
  19. package/dist/{chunk-BFERTUXK.cjs.map → chunk-4RC4GXEM.cjs.map} +1 -1
  20. package/dist/{chunk-QMVK55LC.js → chunk-AOQRB6AU.js} +5 -5
  21. package/dist/{chunk-QMVK55LC.js.map → chunk-AOQRB6AU.js.map} +1 -1
  22. package/dist/{chunk-53DXJEO7.cjs → chunk-DQVUKP5G.cjs} +5 -5
  23. package/dist/{chunk-53DXJEO7.cjs.map → chunk-DQVUKP5G.cjs.map} +1 -1
  24. package/dist/{chunk-7IDEWQPH.js → chunk-G4PXCV2B.js} +12 -8
  25. package/dist/chunk-G4PXCV2B.js.map +1 -0
  26. package/dist/{chunk-OVTTCX2E.js → chunk-M5P7TXZ2.js} +135 -63
  27. package/dist/chunk-M5P7TXZ2.js.map +1 -0
  28. package/dist/{chunk-NVKHUAHU.js → chunk-P2P7C2KZ.js} +4 -4
  29. package/dist/chunk-P2P7C2KZ.js.map +1 -0
  30. package/dist/{chunk-UTDELFOU.js → chunk-R43646JP.js} +4 -4
  31. package/dist/{chunk-UTDELFOU.js.map → chunk-R43646JP.js.map} +1 -1
  32. package/dist/{chunk-4NOTVQ3I.cjs → chunk-TVVC4GVK.cjs} +29 -29
  33. package/dist/{chunk-4NOTVQ3I.cjs.map → chunk-TVVC4GVK.cjs.map} +1 -1
  34. package/dist/{chunk-YXIEPTFG.cjs → chunk-UXPTDNNA.cjs} +181 -109
  35. package/dist/chunk-UXPTDNNA.cjs.map +1 -0
  36. package/dist/{chunk-6MHJKCXO.js → chunk-YF4JC2AL.js} +5 -5
  37. package/dist/{chunk-6MHJKCXO.js.map → chunk-YF4JC2AL.js.map} +1 -1
  38. package/dist/{chunk-SMHTOWXG.cjs → chunk-ZAHCGFGC.cjs} +12 -8
  39. package/dist/chunk-ZAHCGFGC.cjs.map +1 -0
  40. package/dist/index.cjs +37 -37
  41. package/dist/index.d.cts +4 -4
  42. package/dist/index.d.ts +4 -4
  43. package/dist/index.js +6 -6
  44. package/dist/loader.css +1 -1
  45. package/dist/present.cjs +6 -6
  46. package/dist/present.js +6 -6
  47. package/dist/sound.cjs +2 -2
  48. package/dist/sound.js +1 -1
  49. package/dist/three.cjs +93 -26
  50. package/dist/three.cjs.map +1 -1
  51. package/dist/three.d.cts +19 -4
  52. package/dist/three.d.ts +19 -4
  53. package/dist/three.js +89 -22
  54. package/dist/three.js.map +1 -1
  55. package/docs/dsl.md +7 -2
  56. package/llms-full.txt +9 -4
  57. package/package.json +1 -1
  58. package/skills/pixi-effects/SKILL.md +1 -1
  59. package/skills/pixi-effects/reference/recipes.md +1 -1
  60. package/skills/pixi-effects/template.html +6 -6
  61. package/dist/Composition-AZEATHJB.cjs +0 -16
  62. package/dist/Composition-ME3L3EFT.js +0 -7
  63. package/dist/chunk-7IDEWQPH.js.map +0 -1
  64. package/dist/chunk-NEMWRJQS.cjs.map +0 -1
  65. package/dist/chunk-NVKHUAHU.js.map +0 -1
  66. package/dist/chunk-OVTTCX2E.js.map +0 -1
  67. package/dist/chunk-SMHTOWXG.cjs.map +0 -1
  68. package/dist/chunk-YXIEPTFG.cjs.map +0 -1
@@ -1,10 +1,10 @@
1
1
  'use strict';
2
2
 
3
- var chunkBFERTUXK_cjs = require('./chunk-BFERTUXK.cjs');
4
- var chunk4NOTVQ3I_cjs = require('./chunk-4NOTVQ3I.cjs');
5
- var chunkSMHTOWXG_cjs = require('./chunk-SMHTOWXG.cjs');
6
- var chunk53DXJEO7_cjs = require('./chunk-53DXJEO7.cjs');
7
- var chunkNEMWRJQS_cjs = require('./chunk-NEMWRJQS.cjs');
3
+ var chunk4RC4GXEM_cjs = require('./chunk-4RC4GXEM.cjs');
4
+ var chunkTVVC4GVK_cjs = require('./chunk-TVVC4GVK.cjs');
5
+ var chunkZAHCGFGC_cjs = require('./chunk-ZAHCGFGC.cjs');
6
+ var chunkDQVUKP5G_cjs = require('./chunk-DQVUKP5G.cjs');
7
+ var chunk2YV7ISX6_cjs = require('./chunk-2YV7ISX6.cjs');
8
8
  var chunkTWZNOUM7_cjs = require('./chunk-TWZNOUM7.cjs');
9
9
  var pixi_js = require('pixi.js');
10
10
  var mediabunny = require('mediabunny');
@@ -109,7 +109,7 @@ async function mixdown(audios, totalDuration, sampleRate = 44100, pace) {
109
109
  if (pace) await pace(made / all);
110
110
  if (!buffer) continue;
111
111
  if (a.warp || a.sourceMap) {
112
- const r = chunkSMHTOWXG_cjs.resampleThrough(a, buffer.getChannelData(0), buffer.numberOfChannels > 1 ? buffer.getChannelData(1) : buffer.getChannelData(0), buffer.sampleRate, sampleRate, totalDuration);
112
+ const r = chunkZAHCGFGC_cjs.resampleThrough(a, buffer.getChannelData(0), buffer.numberOfChannels > 1 ? buffer.getChannelData(1) : buffer.getChannelData(0), buffer.sampleRate, sampleRate, totalDuration);
113
113
  if (!r) continue;
114
114
  const out = ctx.createBuffer(2, r.L.length, sampleRate);
115
115
  out.getChannelData(0).set(r.L);
@@ -146,81 +146,124 @@ var LOUDNESS_CEILING_DB = -1;
146
146
  var MAX_LIMIT_DB = 10;
147
147
  function limitInto(source, out, gain, ceil, sampleRate) {
148
148
  const n = source[0]?.length ?? 0;
149
- const req = new Float32Array(n);
150
- for (let i = 0; i < n; i++) {
149
+ const L = Math.max(1, Math.round(3e-3 * sampleRate));
150
+ const release = 1 - Math.exp(-1 / (0.08 * sampleRate));
151
+ const reqAt = (i) => {
151
152
  let p = 0;
152
153
  for (const c of source) {
153
154
  const a = Math.abs(c[i]) * gain;
154
155
  if (a > p) p = a;
155
156
  }
156
- req[i] = p > ceil ? ceil / p : 1;
157
- }
158
- const L = Math.max(1, Math.round(3e-3 * sampleRate));
159
- const mn = new Float32Array(n), dq = new Int32Array(n);
160
- let head = 0, tail = 0;
161
- for (let i = n - 1; i >= 0; i--) {
162
- while (tail > head && req[dq[tail - 1]] >= req[i]) tail--;
163
- dq[tail++] = i;
164
- while (dq[head] > i + L) head++;
165
- mn[i] = req[dq[head]];
166
- }
167
- const release = 1 - Math.exp(-1 / (0.08 * sampleRate));
168
- let run = 0, prev = 1, lowest = 1, lowestAt = 0;
169
- const gains = new Float32Array(n);
157
+ return p > ceil ? ceil / p : 1;
158
+ };
159
+ const size = L + 2, dqIdx = new Int32Array(size), dqVal = new Float32Array(size);
160
+ let head = 0, count = 0, added = -1;
161
+ const ring = new Float32Array(L + 1);
162
+ let run = 0;
163
+ let prev = 1, lowest = 1, lowestAt = 0;
170
164
  for (let i = 0; i < n; i++) {
171
- run += mn[i];
172
- if (i > L) run -= mn[i - L - 1];
165
+ for (; added < Math.min(n - 1, i + L); ) {
166
+ added++;
167
+ const v = reqAt(added);
168
+ while (count > 0 && dqVal[(head + count - 1) % size] >= v) count--;
169
+ const at = (head + count) % size;
170
+ dqIdx[at] = added;
171
+ dqVal[at] = v;
172
+ count++;
173
+ }
174
+ while (dqIdx[head] < i) {
175
+ head = (head + 1) % size;
176
+ count--;
177
+ }
178
+ const mn = dqVal[head];
179
+ const slot = i % (L + 1);
180
+ if (i > L) run -= ring[slot];
181
+ ring[slot] = mn;
182
+ run += mn;
173
183
  const avg = run / Math.min(i + 1, L + 1);
174
184
  const g = Math.min(avg, prev + (1 - prev) * release);
175
- gains[i] = g;
176
185
  prev = g;
177
186
  if (g < lowest) {
178
187
  lowest = g;
179
188
  lowestAt = i;
180
189
  }
181
- }
182
- for (let ch = 0; ch < source.length; ch++) {
183
- const s = source[ch], o = out[ch];
184
- for (let i = 0; i < n; i++) {
185
- const v = s[i] * gain * gains[i];
186
- o[i] = v > ceil ? ceil : v < -ceil ? -ceil : v;
190
+ for (let ch = 0; ch < source.length; ch++) {
191
+ const v = source[ch][i] * gain * g;
192
+ out[ch][i] = v > ceil ? ceil : v < -ceil ? -ceil : v;
187
193
  }
188
194
  }
189
195
  return { reductionDb: lowest < 1 ? -20 * Math.log10(lowest) : 0, at: lowestAt / sampleRate };
190
196
  }
191
- function normalizeMix(buffer, target) {
197
+ function cleanMix(buffer, audios) {
198
+ let count = 0, first = -1;
199
+ for (let ch = 0; ch < buffer.numberOfChannels; ch++) {
200
+ const d = buffer.getChannelData(ch);
201
+ for (let i = 0; i < d.length; i++) if (!Number.isFinite(d[i])) {
202
+ d[i] = 0;
203
+ count++;
204
+ if (first < 0 || i < first) first = i;
205
+ }
206
+ }
207
+ const firstAt = first < 0 ? 0 : first / buffer.sampleRate;
208
+ if (count > 0 && audios) {
209
+ const playing = audios.map((a) => ({ a, ...chunkZAHCGFGC_cjs.playSpan(a, buffer.duration) })).filter((s) => s.start <= firstAt && firstAt < s.end).map((s) => s.a.layer ?? "an audio layer");
210
+ console.warn(`pixi-effects: ${count} sample(s) of the audio mix were not numbers (NaN or Infinity), first at ${firstAt.toFixed(2)}s, and were made silent. Playing there: ${[...new Set(playing)].join(", ") || "nothing that is listed"}. A sound that produces these (a custom synth, a division by zero in a volume) is broken.`);
211
+ }
212
+ return { count, firstAt };
213
+ }
214
+ async function normalizeMix(buffer, target, between) {
192
215
  const chans = [];
193
216
  for (let ch = 0; ch < buffer.numberOfChannels; ch++) chans.push(buffer.getChannelData(ch));
194
217
  const sr = buffer.sampleRate;
195
- const m = chunkSMHTOWXG_cjs.measureLoudness(chans, sr, { truePeak: false });
218
+ const pause = async () => {
219
+ if (between) await between();
220
+ };
221
+ const m = chunkZAHCGFGC_cjs.measureLoudness(chans, sr, { truePeak: false });
196
222
  if (m.integratedLufs === null) return { target, gainDb: 0, reachedLufs: null, limited: false };
197
- const original = chans.map((c) => Float32Array.from(c));
223
+ await pause();
198
224
  let gainDb = target - m.integratedLufs;
199
225
  let ceilDb = LOUDNESS_CEILING_DB;
200
- let applied = { reductionDb: 0, at: 0 }, measured = m;
201
- for (let round2 = 0; round2 < 8; round2++) {
202
- const cap = MAX_LIMIT_DB + ceilDb - m.samplePeakDb;
226
+ let applied = { reductionDb: 0, at: 0 };
227
+ let final = m, original = null, cap = Infinity;
228
+ if (gainDb + m.samplePeakDb <= ceilDb) {
229
+ const gain = 10 ** (gainDb / 20);
230
+ for (const d of chans) for (let i = 0; i < d.length; i++) d[i] = d[i] * gain;
231
+ await pause();
232
+ final = chunkZAHCGFGC_cjs.measureLoudness(chans, sr);
233
+ } else {
234
+ original = chans.map((c) => Float32Array.from(c));
235
+ cap = MAX_LIMIT_DB + ceilDb - m.samplePeakDb;
203
236
  if (gainDb > cap) gainDb = cap;
204
- applied = limitInto(original, chans, 10 ** (gainDb / 20), 10 ** (ceilDb / 20), sr);
205
- measured = chunkSMHTOWXG_cjs.measureLoudness(chans, sr);
206
- const overTp = measured.truePeakDb - LOUDNESS_CEILING_DB;
207
- if (overTp > 0.02) {
208
- ceilDb -= overTp + 0.02;
209
- continue;
237
+ for (let round2 = 0; round2 < 3; round2++) {
238
+ cap = MAX_LIMIT_DB + ceilDb - m.samplePeakDb;
239
+ if (gainDb > cap) gainDb = cap;
240
+ applied = limitInto(original, chans, 10 ** (gainDb / 20), 10 ** (ceilDb / 20), sr);
241
+ await pause();
242
+ final = chunkZAHCGFGC_cjs.measureLoudness(chans, sr);
243
+ await pause();
244
+ const overTp = final.truePeakDb - LOUDNESS_CEILING_DB;
245
+ const deficit = target - final.integratedLufs;
246
+ if (round2 === 2 || overTp <= 0.02 && Math.abs(deficit) <= 0.15) break;
247
+ if (overTp > 0.02) ceilDb -= overTp + 0.02;
248
+ const next = Math.min(gainDb + deficit, MAX_LIMIT_DB + ceilDb - m.samplePeakDb);
249
+ if (overTp <= 0.02 && Math.abs(next - gainDb) < 0.05) break;
250
+ gainDb = next;
210
251
  }
211
- const deficit = target - measured.integratedLufs;
212
- if (Math.abs(deficit) <= 0.15) break;
213
- const next = Math.min(gainDb + deficit, cap);
214
- if (Math.abs(next - gainDb) < 0.05) break;
215
- gainDb = next;
216
252
  }
217
- const limited = measured.integratedLufs < target - 0.15;
253
+ let trimDb = 0;
254
+ if (final.truePeakDb > LOUDNESS_CEILING_DB) {
255
+ trimDb = final.truePeakDb - LOUDNESS_CEILING_DB + 0.01;
256
+ const k = 10 ** (-trimDb / 20);
257
+ for (const d of chans) for (let i = 0; i < d.length; i++) d[i] = d[i] * k;
258
+ final = { ...final, integratedLufs: final.integratedLufs === null ? null : final.integratedLufs - trimDb };
259
+ }
260
+ const reached = final.integratedLufs === null ? null : Math.round(final.integratedLufs * 10) / 10;
218
261
  const reduction = Math.round(applied.reductionDb * 10) / 10;
219
262
  return {
220
263
  target,
221
- gainDb: Math.round(gainDb * 10) / 10,
222
- reachedLufs: measured.integratedLufs,
223
- limited,
264
+ gainDb: Math.round((gainDb - trimDb) * 10) / 10,
265
+ reachedLufs: reached,
266
+ limited: reached !== null && reached < target - 0.15,
224
267
  ...reduction >= 0.1 ? { limiterDb: reduction, limiterAt: Math.round(applied.at * 100) / 100 } : {}
225
268
  };
226
269
  }
@@ -240,7 +283,7 @@ function limitMix(buffer, audios) {
240
283
  }
241
284
  const peakAt = peakIndex / buffer.sampleRate;
242
285
  if (peak > 1) {
243
- const playing = audios.map((a) => ({ a, ...chunkSMHTOWXG_cjs.playSpan(a, buffer.duration) })).filter((s) => s.start <= peakAt && peakAt < s.end).map((s) => s.a.layer ?? "an audio layer");
286
+ const playing = audios.map((a) => ({ a, ...chunkZAHCGFGC_cjs.playSpan(a, buffer.duration) })).filter((s) => s.start <= peakAt && peakAt < s.end).map((s) => s.a.layer ?? "an audio layer");
244
287
  console.warn(
245
288
  `pixi-effects: the audio mix peaks at ${peak.toFixed(2)} (${(20 * Math.log10(peak)).toFixed(1)} dBFS) at ${peakAt.toFixed(2)}s, so it was limited (it would distort). Playing there: ${[...new Set(playing)].join(", ")}. Lower their volume.`
246
289
  );
@@ -1202,8 +1245,8 @@ function expandTransitions(spec) {
1202
1245
  }
1203
1246
  const transitions = out.transitions;
1204
1247
  const sequences = out.sequences ?? [];
1205
- const remap = out.duration !== void 0 ? chunk4NOTVQ3I_cjs.timeRemapOf(out) : null;
1206
- const parentDuration = remap ? chunk4NOTVQ3I_cjs.contentLength(remap, out.duration) : out.duration ?? Infinity;
1248
+ const remap = out.duration !== void 0 ? chunkTVVC4GVK_cjs.timeRemapOf(out) : null;
1249
+ const parentDuration = remap ? chunkTVVC4GVK_cjs.contentLength(remap, out.duration) : out.duration ?? Infinity;
1207
1250
  const compW = out.width;
1208
1251
  const compH = out.height;
1209
1252
  const threeDByName = new Set(sequences.filter((s) => s.name && s.threeD).map((s) => s.name));
@@ -1271,16 +1314,16 @@ function expandTransitions(spec) {
1271
1314
  if (toUses > 1) {
1272
1315
  throw new Error(`pixi-effects: ${tag} sequence "${t.to}" is used as \`to\` in more than one transition`);
1273
1316
  }
1274
- const tStart = chunk4NOTVQ3I_cjs.resolveAt(t.at, parentDuration);
1317
+ const tStart = chunkTVVC4GVK_cjs.resolveAt(t.at, parentDuration);
1275
1318
  const tEnd = tStart + t.duration;
1276
1319
  const fromSeq = fromEntry.seq;
1277
- const fromStart = chunk4NOTVQ3I_cjs.resolveAt(fromSeq.at, parentDuration);
1320
+ const fromStart = chunkTVVC4GVK_cjs.resolveAt(fromSeq.at, parentDuration);
1278
1321
  const fromEnd = fromStart + (fromSeq.duration ?? parentDuration);
1279
1322
  if (tStart < fromStart - EPS || tEnd > fromEnd + EPS) {
1280
1323
  throw new Error(`pixi-effects: ${tag} window [${tStart}, ${tEnd}] is not covered by \`from\` "${t.from}" (lives [${fromStart}, ${fromEnd}], so it ends at ${fromEnd})`);
1281
1324
  }
1282
1325
  const toSeq = toEntry.seq;
1283
- const toStart = chunk4NOTVQ3I_cjs.resolveAt(toSeq.at, parentDuration);
1326
+ const toStart = chunkTVVC4GVK_cjs.resolveAt(toSeq.at, parentDuration);
1284
1327
  const toEnd = toStart + (toSeq.duration ?? parentDuration);
1285
1328
  if (tStart < toStart - EPS || tEnd > toEnd + EPS) {
1286
1329
  throw new Error(`pixi-effects: ${tag} window [${tStart}, ${tEnd}] is not covered by \`to\` "${t.to}" (lives [${toStart}, ${toEnd}], so it starts at ${toStart})`);
@@ -1340,7 +1383,7 @@ function wrapAsFullComposition(seq, compW, compH) {
1340
1383
  delete inner.at;
1341
1384
  delete inner.duration;
1342
1385
  const m = inner.mask;
1343
- const carriesMatte = m !== void 0 && chunkBFERTUXK_cjs.maskSourceOf(m).kind === "refs";
1386
+ const carriesMatte = m !== void 0 && chunk4RC4GXEM_cjs.maskSourceOf(m).kind === "refs";
1344
1387
  if (carriesMatte) delete inner.mask;
1345
1388
  return {
1346
1389
  ...carriesMatte ? { mask: m } : {},
@@ -1605,12 +1648,12 @@ function canvasReport(root) {
1605
1648
  const length = seq.duration ?? 0;
1606
1649
  const moments = [0.15, 0.4, 0.65, 0.9].map((f) => Math.round(f * length * 1e3) / 1e3);
1607
1650
  const { differs, drawMs, threw } = seq.checkDeterminism(moments);
1608
- const name = chunkNEMWRJQS_cjs.describeLayer(seq.spec);
1651
+ const name = chunk2YV7ISX6_cjs.describeLayer(seq.spec);
1609
1652
  const kind = seq.spec.type === "three" ? "three" : "canvas";
1610
1653
  const problem = threw.length ? `${name} could not draw at ${threw.map((t) => `${t} s`).join(", ")} (its ${kind === "three" ? "update or render threw" : "draw threw"}, or the layer failed to build): ${kind === "three" ? "the warning above says why" : "it shows a magenta checkerboard there; the warning above says why"}, so the layer cannot be called deterministic` : differs.length ? `${name} does not draw the same picture for the same time (checked at ${differs.map(describeDiff).join(", ")}; drawn in two orders after one draw that was thrown away): ` + (kind === "three" ? "usually its update uses Math.random, Date.now, a clock, or state that builds up from call to call: a mixer advanced with mixer.update(dt) is one (pose a clip with mixer.setTime(t), or threeGltf(url).pose(t)), and so is a value moved by the time since the last call. A skinned model of your own (not loaded by threeGltf) that moves and casts a shadow does it too: three culls a skinned mesh by a bounding sphere that does not follow it, so set mesh.frustumCulled = false. Set every object from t, so playback, a seek and an export agree" : "usually its draw uses Math.random, Date.now or state that outlives the call (a value it keeps from the last call), so playback, a seek and an export would not agree: use info.random() (seeded) and derive everything from t. If it uses none of these, look at the canvases it draws from: make sprites and masks with info.canvas(w, h) (not document.createElement) and draw them into the picture before the strokes that depend on them") : "";
1611
1654
  out.push({ name: seq.spec.name ?? name, moments, drawMs, ok: !differs.length && !threw.length, problem, kind });
1612
1655
  }
1613
- if (seq instanceof chunkBFERTUXK_cjs.CompositionSequence) walk3(seq);
1656
+ if (seq instanceof chunk4RC4GXEM_cjs.CompositionSequence) walk3(seq);
1614
1657
  };
1615
1658
  const walk3 = (comp) => {
1616
1659
  for (const { seq } of comp.layers()) {
@@ -1662,8 +1705,8 @@ function motionOf(keyframes, local, lives, keys = (k) => GEOMETRY.has(k)) {
1662
1705
  let length = 0;
1663
1706
  if (typeof kf.duration === "number") length = kf.duration;
1664
1707
  else if (kf.duration === "auto") {
1665
- const sp = chunk4NOTVQ3I_cjs.parseSpring(kf.ease);
1666
- length = sp ? Math.round(chunk4NOTVQ3I_cjs.springDuration(sp) * 1e3) / 1e3 : 0.5;
1708
+ const sp = chunkTVVC4GVK_cjs.parseSpring(kf.ease);
1709
+ length = sp ? Math.round(chunkTVVC4GVK_cjs.springDuration(sp) * 1e3) / 1e3 : 0.5;
1667
1710
  }
1668
1711
  if (!(length > 0)) continue;
1669
1712
  const kat = kf.at ?? 0;
@@ -1743,7 +1786,7 @@ function inspectScene(root, frame, time, canvas, opts = {}) {
1743
1786
  if (motion.ever) hasMotion.add(info);
1744
1787
  let shown = bounds ? clip(bounds, view) : null;
1745
1788
  const maskTarget = seq.maskSequence?.target;
1746
- const maskSrc = chunkBFERTUXK_cjs.maskSourceOf(seq.spec.mask);
1789
+ const maskSrc = chunk4RC4GXEM_cjs.maskSourceOf(seq.spec.mask);
1747
1790
  if (shown && maskTarget && !(maskSrc.kind === "inline" && maskSrc.invert)) {
1748
1791
  const m = maskTarget.getBounds();
1749
1792
  shown = clip(shown, { x: m.x, y: m.y, width: m.width, height: m.height });
@@ -1752,7 +1795,7 @@ function inspectScene(root, frame, time, canvas, opts = {}) {
1752
1795
  const group = parentScene ?? (name !== void 0 && blending.has(name) ? name : void 0);
1753
1796
  if (group) scene.set(info, group);
1754
1797
  if (zero || seq.showsNothingYet) scaledToNothing.add(info);
1755
- if (seq instanceof chunkBFERTUXK_cjs.CompositionSequence) walk3(seq, prefix + label + "/", insideThreeD || threeD, visible, group, motion, zero, parentAlpha * alpha);
1798
+ if (seq instanceof chunk4RC4GXEM_cjs.CompositionSequence) walk3(seq, prefix + label + "/", insideThreeD || threeD, visible, group, motion, zero, parentAlpha * alpha);
1756
1799
  });
1757
1800
  };
1758
1801
  walk3(root, "", false, true);
@@ -1882,6 +1925,22 @@ function pickScene(root, x, y, opts = {}, deps = {}) {
1882
1925
  return true;
1883
1926
  };
1884
1927
  const inside = (b, p) => p.x >= b.x && p.x <= b.x + b.width && p.y >= b.y && p.y <= b.y + b.height;
1928
+ const boxOf = (d, masked) => {
1929
+ const v = d.bounds;
1930
+ if (masked && v && Number.isFinite(v.minX) && Number.isFinite(v.maxX)) return { x: v.minX, y: v.minY, width: v.maxX - v.minX, height: v.maxY - v.minY };
1931
+ return d.getLocalBounds();
1932
+ };
1933
+ const inMask = (maskTarget, p) => {
1934
+ const c = maskTarget;
1935
+ if (typeof c.containsPoint === "function") return c.containsPoint(c.toLocal(p));
1936
+ const was = c.measurable;
1937
+ c.measurable = true;
1938
+ try {
1939
+ return inside(c.getBounds(), p);
1940
+ } finally {
1941
+ c.measurable = was;
1942
+ }
1943
+ };
1885
1944
  const hiddenSources = /* @__PURE__ */ new Map();
1886
1945
  const forwards = [];
1887
1946
  const rays = /* @__PURE__ */ new Map();
@@ -1895,7 +1954,7 @@ function pickScene(root, x, y, opts = {}, deps = {}) {
1895
1954
  if (!shows || !inside(lb, local)) return;
1896
1955
  const r = rayOf(seq, d, local, lb);
1897
1956
  const shown = r?.uv ? shows.call(seq, r.material) : null;
1898
- if (r?.uv && shown) forwards.push({ source: shown.source, uv: r.uv, via: { path, object: String(r.object) }, order: orderOf(d) });
1957
+ if (r?.uv && shown) forwards.push({ source: shown.source, at: shown.at(r.uv.u, r.uv.v), via: { path, object: String(r.object) }, order: orderOf(d) });
1899
1958
  };
1900
1959
  const walk3 = (comp, prefix, parentVisible, at = { x, y }, orderBase, via) => {
1901
1960
  comp.layers().forEach(({ seq, display, threeD, carriers, quad }, i) => {
@@ -1908,11 +1967,13 @@ function pickScene(root, x, y, opts = {}, deps = {}) {
1908
1967
  const alive = (threeD ? d.visible : (t?.renderable ?? true) && t?.visible !== false) && carriers.every((c) => c.target?.renderable ?? true);
1909
1968
  const visible = parentVisible && alive && alpha > 0;
1910
1969
  if (!visible) {
1911
- if (seq instanceof chunkBFERTUXK_cjs.CompositionSequence && !threeD && parentVisible && alpha > 0 && t?.visible === false && (t?.renderable ?? true) && carriers.every((c) => c.target?.renderable ?? true)) hiddenSources.set(seq, path);
1970
+ if (seq instanceof chunk4RC4GXEM_cjs.CompositionSequence && !threeD && parentVisible && alpha > 0 && t?.visible === false && (t?.renderable ?? true) && carriers.every((c) => c.target?.renderable ?? true)) hiddenSources.set(seq, path);
1912
1971
  return;
1913
1972
  }
1914
- const isComposition = seq instanceof chunkBFERTUXK_cjs.CompositionSequence;
1973
+ const isComposition = seq instanceof chunk4RC4GXEM_cjs.CompositionSequence;
1915
1974
  if (isComposition && !threeD) {
1975
+ const gm = seq.maskSequence?.target, gs = chunk4RC4GXEM_cjs.maskSourceOf(seq.spec.mask);
1976
+ if (gm && gs.kind === "inline" && !gs.invert && !inMask(gm, at)) return;
1916
1977
  walk3(seq, path + "/", visible, at, orderBase, via);
1917
1978
  return;
1918
1979
  }
@@ -1929,7 +1990,7 @@ function pickScene(root, x, y, opts = {}, deps = {}) {
1929
1990
  if (quad && pointInQuad(quad, d.toLocal(point).x, d.toLocal(point).y)) hit = { name, path, type: seq.spec.type, kind: "card", point };
1930
1991
  } else {
1931
1992
  const local = d.toLocal(point);
1932
- const lb = d.getLocalBounds();
1993
+ const lb = boxOf(d, !!seq.maskSequence);
1933
1994
  if (inside(lb, local)) {
1934
1995
  const isThree = typeof seq.pickAt === "function";
1935
1996
  const ray = isThree ? rayOf(seq, d, local, lb) : void 0;
@@ -1967,8 +2028,8 @@ function pickScene(root, x, y, opts = {}, deps = {}) {
1967
2028
  }
1968
2029
  if (!hit) return;
1969
2030
  const maskTarget = seq.maskSequence?.target;
1970
- const maskSrc = chunkBFERTUXK_cjs.maskSourceOf(seq.spec.mask);
1971
- if (maskTarget && !(maskSrc.kind === "inline" && maskSrc.invert) && !inside(maskTarget.getBounds(), point)) return;
2031
+ const maskSrc = chunk4RC4GXEM_cjs.maskSourceOf(seq.spec.mask);
2032
+ if (maskTarget && !(maskSrc.kind === "inline" && maskSrc.invert) && !(maskSrc.kind === "inline" ? inMask(maskTarget, point) : inside(maskTarget.getBounds(), point))) return;
1972
2033
  found.push({ hit: via ? { ...hit, point: { x, y }, via } : hit, order: orderBase ? [...orderBase, ...orderOf(d)] : orderOf(d) });
1973
2034
  });
1974
2035
  };
@@ -1976,7 +2037,9 @@ function pickScene(root, x, y, opts = {}, deps = {}) {
1976
2037
  for (const f of forwards) {
1977
2038
  const path = hiddenSources.get(f.source);
1978
2039
  if (path === void 0) continue;
1979
- const g = f.source.target.toGlobal({ x: f.uv.u * f.source.intrinsicWidth, y: (1 - f.uv.v) * f.source.intrinsicHeight });
2040
+ const g = f.source.target.toGlobal({ x: f.at.x * f.source.intrinsicWidth, y: f.at.y * f.source.intrinsicHeight });
2041
+ const sm = f.source.maskSequence?.target, ss = chunk4RC4GXEM_cjs.maskSourceOf(f.source.spec.mask);
2042
+ if (sm && ss.kind === "inline" && !ss.invert && !inMask(sm, g)) continue;
1980
2043
  walk3(f.source, path + "/", true, g, f.order, f.via);
1981
2044
  }
1982
2045
  return found.sort((a, b) => above(a.order, b.order)).map((f) => f.hit);
@@ -2011,7 +2074,7 @@ function walk(root, visit) {
2011
2074
  visit(seq);
2012
2075
  const mask = seq.maskSequence;
2013
2076
  if (mask) visit(mask);
2014
- if (seq instanceof chunkBFERTUXK_cjs.CompositionSequence) walk(seq, visit);
2077
+ if (seq instanceof chunk4RC4GXEM_cjs.CompositionSequence) walk(seq, visit);
2015
2078
  }
2016
2079
  }
2017
2080
  function reusePoolOf(old) {
@@ -2112,7 +2175,7 @@ var round = (n) => Math.round(n * 1e6) / 1e6;
2112
2175
  var family = (name) => name.replace(/\d+/g, "#");
2113
2176
  function detailOf(seq) {
2114
2177
  const s = seq.spec;
2115
- const spans = chunk4NOTVQ3I_cjs.spansMeta.get(seq.spec)?.preview;
2178
+ const spans = chunkTVVC4GVK_cjs.spansMeta.get(seq.spec)?.preview;
2116
2179
  if (spans) return spans.length > 60 ? spans.slice(0, 57) + "\u2026" : spans;
2117
2180
  if (typeof s.text === "string") return s.text.length > 60 ? s.text.slice(0, 57) + "\u2026" : s.text;
2118
2181
  if (typeof s.asset === "string") return `asset \u201C${s.asset}\u201D`;
@@ -2128,10 +2191,10 @@ function walk2(comp, parentStart, parentEnd, depth, prefix, rows, transitions, l
2128
2191
  const start = parentStart + (seq.at ?? 0);
2129
2192
  const end = Math.min(parentEnd, seq.duration !== void 0 ? start + seq.duration : parentEnd);
2130
2193
  const span = end - start;
2131
- const keys = (seq.spec.keyframes ?? []).map((kf) => round(start + chunk4NOTVQ3I_cjs.resolveAt(kf.at, span))).sort((a, b) => a - b).slice(0, MAX_KEYS);
2194
+ const keys = (seq.spec.keyframes ?? []).map((kf) => round(start + chunkTVVC4GVK_cjs.resolveAt(kf.at, span))).sort((a, b) => a - b).slice(0, MAX_KEYS);
2132
2195
  const row = { path: prefix + name, name, type: seq.spec.type, depth, start: round(start), end: round(end), keys, detail: detailOf(seq), ...local ? { local } : {} };
2133
2196
  const inner = [];
2134
- const isComp = seq instanceof chunkBFERTUXK_cjs.CompositionSequence;
2197
+ const isComp = seq instanceof chunk4RC4GXEM_cjs.CompositionSequence;
2135
2198
  if (isComp) {
2136
2199
  const label = seq.remapLabel();
2137
2200
  walk2(seq, label ? 0 : start, label ? seq.contentSpan : end, depth + 1, prefix + name + "/", inner, transitions, label ? `${name} ${label}` : local);
@@ -2290,7 +2353,7 @@ function sheetLayout(n, columns, cellW, srcW, srcH, labelH) {
2290
2353
  }
2291
2354
 
2292
2355
  // src/core/vars.ts
2293
- var RESERVED_NAMES = ["w", "h", "W", "H", "GW", "GH", "t", "d", "T", "contain", "cover", "value", ...chunk4NOTVQ3I_cjs.EXPRESSION_NAMES];
2356
+ var RESERVED_NAMES = ["w", "h", "W", "H", "GW", "GH", "t", "d", "T", "contain", "cover", "value", ...chunkTVVC4GVK_cjs.EXPRESSION_NAMES];
2294
2357
  var NAME = /^[A-Za-z_][A-Za-z0-9_]*$/;
2295
2358
  var isPlain = (v) => !!v && typeof v === "object" && (Object.getPrototypeOf(v) === Object.prototype || Object.getPrototypeOf(v) === null);
2296
2359
  function normalizeVars(raw, where, warn = (m) => console.warn(m)) {
@@ -2834,8 +2897,8 @@ var Movie = class {
2834
2897
  this.stops = normalizeStops(applyVars(options.composition, this._vars)?.stops, this.duration, this.frameRate);
2835
2898
  this._posterFrame = poster.frame;
2836
2899
  this.background = options.background ?? "#000000";
2837
- this.timeline = chunk4NOTVQ3I_cjs.createTimeline({ paused: true, defaults: { ease: "none" } });
2838
- chunk4NOTVQ3I_cjs.lengthen(this.timeline, this.duration);
2900
+ this.timeline = chunkTVVC4GVK_cjs.createTimeline({ paused: true, defaults: { ease: "none" } });
2901
+ chunkTVVC4GVK_cjs.lengthen(this.timeline, this.duration);
2839
2902
  const baseAppOptions = {
2840
2903
  width: this.width,
2841
2904
  height: this.height,
@@ -2934,8 +2997,8 @@ var Movie = class {
2934
2997
  height: sizeWins ? height : spec.height ?? height,
2935
2998
  duration: spec.duration ?? this.duration
2936
2999
  } : void 0;
2937
- await chunk4NOTVQ3I_cjs.ensureFilterLibrary(seededComposition);
2938
- if (chunk53DXJEO7_cjs.usesAdvancedBlend(seededComposition)) await chunk53DXJEO7_cjs.enableAdvancedBlend(this.app.renderer);
3000
+ await chunkTVVC4GVK_cjs.ensureFilterLibrary(seededComposition);
3001
+ if (chunkDQVUKP5G_cjs.usesAdvancedBlend(seededComposition)) await chunkDQVUKP5G_cjs.enableAdvancedBlend(this.app.renderer);
2939
3002
  const userComposition = seededComposition ? expandTransitions(seededComposition) : void 0;
2940
3003
  const rootSeqSpec = {
2941
3004
  type: "composition",
@@ -2945,7 +3008,7 @@ var Movie = class {
2945
3008
  ...userComposition
2946
3009
  };
2947
3010
  if (userComposition) carryTransitionWindows(userComposition, rootSeqSpec);
2948
- const sequence = new chunkBFERTUXK_cjs.CompositionSequence(rootSeqSpec, null, rootShape);
3011
+ const sequence = new chunk4RC4GXEM_cjs.CompositionSequence(rootSeqSpec, null, rootShape);
2949
3012
  lp?.begin("build", countLayers(rootSeqSpec));
2950
3013
  try {
2951
3014
  await sequence.build();
@@ -2977,7 +3040,8 @@ var Movie = class {
2977
3040
  lp.part(done);
2978
3041
  await lp.yieldIfDue();
2979
3042
  } : void 0);
2980
- const loudness = this.audioBuffer && this._loudness !== null ? normalizeMix(this.audioBuffer, this._loudness) : void 0;
3043
+ if (this.audioBuffer) cleanMix(this.audioBuffer, audios);
3044
+ const loudness = this.audioBuffer && this._loudness !== null ? await normalizeMix(this.audioBuffer, this._loudness, lp ? () => lp.yieldIfDue() : void 0) : void 0;
2981
3045
  this._mixStats = this.audioBuffer ? { ...limitMix(this.audioBuffer, audios), ...loudness ? { loudness } : {} } : null;
2982
3046
  this._mixCache.set(key, this.audioBuffer, this._mixStats);
2983
3047
  status = "mixed";
@@ -3083,8 +3147,8 @@ var Movie = class {
3083
3147
  if (!changed) return { reason, ms: 0, audio: "none", frame: this.currentFrame, playing: this.isPlaying, unchanged: true };
3084
3148
  sequence = await this._construct(composition, vars, width, height, void 0, this._resized || sizeChanged, reusePoolOf(this._rootSequence));
3085
3149
  alive();
3086
- const timeline = chunk4NOTVQ3I_cjs.createTimeline({ paused: true, defaults: { ease: "none" } });
3087
- chunk4NOTVQ3I_cjs.lengthen(timeline, this.duration);
3150
+ const timeline = chunkTVVC4GVK_cjs.createTimeline({ paused: true, defaults: { ease: "none" } });
3151
+ chunkTVVC4GVK_cjs.lengthen(timeline, this.duration);
3088
3152
  sequence.bindTimeline(timeline);
3089
3153
  const hadSound = this.audioBuffer !== null;
3090
3154
  const audio = await this._mixSound(sequence, this._ensureAudioContext());
@@ -3220,12 +3284,18 @@ var Movie = class {
3220
3284
  const collected = [];
3221
3285
  const clocks = /* @__PURE__ */ new Map();
3222
3286
  collectVideoSequences(this._rootSequence, collected, clocks);
3223
- await Promise.all(collected.map((v) => {
3287
+ const wait = (v) => {
3224
3288
  if (v.selfTimed) return v.awaitFrameAt(t);
3225
3289
  const local = (clocks.get(v)?.() ?? t) - (v.absoluteStart ?? v.at);
3226
3290
  if (local < 0 || local > (v.duration ?? 0)) return Promise.resolve();
3227
3291
  return v.awaitFrameAt(local);
3228
- }));
3292
+ };
3293
+ const late = collected.filter((v) => v.showsLayers === true);
3294
+ await Promise.all(collected.filter((v) => !late.includes(v)).map(wait));
3295
+ if (late.length > 0) {
3296
+ this._updateSpace(t);
3297
+ await Promise.all(late.map(wait));
3298
+ }
3229
3299
  }
3230
3300
  async snapshot(frame = this.currentFrame, opts = {}) {
3231
3301
  chunkTWZNOUM7_cjs.warnUnknownOptions("movie.snapshot()", opts, ["scale", "type", "quality", "as", "motionBlur"]);
@@ -3491,7 +3561,7 @@ var Movie = class {
3491
3561
  chunkTWZNOUM7_cjs.warnUnknownOptions("movie.inspectAudio()", opts, ["window", "scenes"]);
3492
3562
  this._requireReady("inspectAudio");
3493
3563
  const scenes = opts.scenes ?? namedScenes(this.timelineData().rows);
3494
- return chunkSMHTOWXG_cjs.analyzeAudio(this.audioBuffer, this._audioSources, this._mixStats, this.duration, { ...opts, scenes });
3564
+ return chunkZAHCGFGC_cjs.analyzeAudio(this.audioBuffer, this._audioSources, this._mixStats, this.duration, { ...opts, scenes });
3495
3565
  }
3496
3566
  /**
3497
3567
  * Draw the stage now. PixiJS's CullerPlugin only culls inside `app.render()` (the ticker), and we render
@@ -3853,7 +3923,7 @@ function countLayers(spec) {
3853
3923
  n += 1 + countLayers(s);
3854
3924
  const spans = s.spans;
3855
3925
  if (Array.isArray(spans)) n += spans.length;
3856
- const src = chunkBFERTUXK_cjs.maskSourceOf(mask);
3926
+ const src = chunk4RC4GXEM_cjs.maskSourceOf(mask);
3857
3927
  if (src.kind === "inline") n += 1 + countLayers(src.spec);
3858
3928
  }
3859
3929
  return n;
@@ -3942,8 +4012,8 @@ function splitVertical(text, style, ts, right, top, align, by) {
3942
4012
 
3943
4013
  // src/presets/_ease.ts
3944
4014
  function parseEase2(name) {
3945
- chunk4NOTVQ3I_cjs.checkEase(name, "a preset");
3946
- return chunk4NOTVQ3I_cjs.parseEase(name);
4015
+ chunkTVVC4GVK_cjs.checkEase(name, "a preset");
4016
+ return chunkTVVC4GVK_cjs.parseEase(name);
3947
4017
  }
3948
4018
 
3949
4019
  // src/presets/kenBurns.ts
@@ -4078,17 +4148,17 @@ function orbit(opts) {
4078
4148
  throw new Error("pixi-effects: orbit() cannot combine `radius` with `dollyZoom` \u2014 the radius follows the animated fov");
4079
4149
  }
4080
4150
  const [cx, cy] = opts.center ?? [width / 2, height / 2];
4081
- const fixedR = opts.radius ?? chunk4NOTVQ3I_cjs.homeDistance(height, fov ?? chunk4NOTVQ3I_cjs.DEFAULT_FOV);
4151
+ const fixedR = opts.radius ?? chunkTVVC4GVK_cjs.homeDistance(height, fov ?? chunkTVVC4GVK_cjs.DEFAULT_FOV);
4082
4152
  const start = opts.start ?? -degrees / 2;
4083
4153
  const steps = Math.max(1, Math.round(duration * stepsPerSecond));
4084
4154
  const easeFn = parseEase2(ease);
4085
4155
  const dollyEase = dolly ? parseEase2(dolly.ease ?? ease) : null;
4086
4156
  const pos = (i) => {
4087
4157
  const p = i / steps;
4088
- const a = (start + degrees * easeFn(p)) * chunk4NOTVQ3I_cjs.DEG;
4158
+ const a = (start + degrees * easeFn(p)) * chunkTVVC4GVK_cjs.DEG;
4089
4159
  if (!dolly) return { x: cx + fixedR * Math.sin(a), z: fixedR * Math.cos(a) };
4090
4160
  const f = dolly.from + (dolly.to - dolly.from) * dollyEase(p);
4091
- const R = chunk4NOTVQ3I_cjs.homeDistance(height, f);
4161
+ const R = chunkTVVC4GVK_cjs.homeDistance(height, f);
4092
4162
  return { x: cx + R * Math.sin(a), z: R * Math.cos(a), fov: f };
4093
4163
  };
4094
4164
  const keyframes = [];
@@ -4143,7 +4213,7 @@ function particles(opts) {
4143
4213
  const round2 = (v) => Math.round(v * 1e6) / 1e6;
4144
4214
  const out = [];
4145
4215
  for (let i = 0; i < count; i++) {
4146
- const r = chunk4NOTVQ3I_cjs.random(seed * 1000003 + i * 7919 + 17);
4216
+ const r = chunkTVVC4GVK_cjs.random(seed * 1000003 + i * 7919 + 17);
4147
4217
  const pick = ([a2, b]) => a2 + r() * (b - a2);
4148
4218
  const born = at + r() * emit;
4149
4219
  const lifetime = pick(life);
@@ -4156,7 +4226,7 @@ function particles(opts) {
4156
4226
  const turn = spin ? pick(spin) : 0;
4157
4227
  const startAngle = spin ? r() * 360 : 0;
4158
4228
  const swayAmp = sway?.amp ?? 0, swayFreq = sway?.freq ?? 1;
4159
- const n0 = sway ? chunk4NOTVQ3I_cjs.noise(0, seed * 1000003 + i) : 0;
4229
+ const n0 = sway ? chunkTVVC4GVK_cjs.noise(0, seed * 1000003 + i) : 0;
4160
4230
  const at_ = (t) => {
4161
4231
  const axis = (p0, v0, acc) => {
4162
4232
  if (drag > 0) {
@@ -4165,7 +4235,7 @@ function particles(opts) {
4165
4235
  }
4166
4236
  return p0 + v0 * t + 0.5 * acc * t * t;
4167
4237
  };
4168
- const sw = sway ? swayAmp * (chunk4NOTVQ3I_cjs.noise(t * swayFreq, seed * 1000003 + i) - n0) : 0;
4238
+ const sw = sway ? swayAmp * (chunkTVVC4GVK_cjs.noise(t * swayFreq, seed * 1000003 + i) - n0) : 0;
4169
4239
  return { x: axis(x0, vx, wind) + sw, y: axis(y0, vy, gravity) };
4170
4240
  };
4171
4241
  const kfs = [];
@@ -4227,7 +4297,7 @@ function followPath(opts) {
4227
4297
  if (steps > MAX_STEPS) throw new Error(`followPath(): ${steps} samples is too many (max ${MAX_STEPS}); lower frameRate or duration`);
4228
4298
  const segments = [];
4229
4299
  let total = 0;
4230
- for (const line of chunk4NOTVQ3I_cjs.flattenSvgPath(d)) {
4300
+ for (const line of chunkTVVC4GVK_cjs.flattenSvgPath(d)) {
4231
4301
  const p = line.closed ? [...line.pts, line.pts[0], line.pts[1]] : line.pts;
4232
4302
  for (let i = 2; i < p.length; i += 2) {
4233
4303
  const len = Math.hypot(p[i] - p[i - 2], p[i + 1] - p[i - 1]);
@@ -4280,7 +4350,7 @@ function followPath(opts) {
4280
4350
  // src/core/pathSample.ts
4281
4351
  function samplePath(d) {
4282
4352
  if (typeof d !== "string" || d.trim() === "") throw new Error('samplePath(): d must be SVG path data, e.g. "M 40 300 C 200 20 400 20 560 300"');
4283
- const lines = chunk4NOTVQ3I_cjs.flattenSvgPath(d);
4353
+ const lines = chunkTVVC4GVK_cjs.flattenSvgPath(d);
4284
4354
  const segments = [];
4285
4355
  let length = 0;
4286
4356
  for (const line of lines) {
@@ -4311,7 +4381,7 @@ function samplePath(d) {
4311
4381
  return { x: g.x0 + (g.x1 - g.x0) * t, y: g.y0 + (g.y1 - g.y0) * t, angle: Math.atan2(g.y1 - g.y0, g.x1 - g.x0) };
4312
4382
  },
4313
4383
  trim(start = 0, end = 1) {
4314
- return chunk4NOTVQ3I_cjs.trimPolylines(lines, start, end).map((l) => l.closed ? [...l.pts, l.pts[0], l.pts[1]] : l.pts);
4384
+ return chunkTVVC4GVK_cjs.trimPolylines(lines, start, end).map((l) => l.closed ? [...l.pts, l.pts[0], l.pts[1]] : l.pts);
4315
4385
  }
4316
4386
  };
4317
4387
  }
@@ -4391,7 +4461,9 @@ function catmullRom(p0, p1, p2, p3, t) {
4391
4461
  // src/presets/stagger.ts
4392
4462
  var STAGGER_KEYS = ["each", "amount", "from", "grid", "ease", "seed"];
4393
4463
  var FROM_NAMES = ["start", "end", "center", "edges", "random"];
4394
- function stagger(items, options = {}) {
4464
+ function stagger(items, spec = {}) {
4465
+ if (typeof spec !== "number" && (typeof spec !== "object" || spec === null)) throw new Error(`stagger(): the second argument is { each, from, \u2026 } or a number of seconds between neighbours, got ${JSON.stringify(spec)}`);
4466
+ const options = typeof spec === "number" ? { each: spec } : spec;
4395
4467
  const isCount = typeof items === "number";
4396
4468
  const n = isCount ? items : items.length;
4397
4469
  if (!Number.isInteger(n) || n < 0) throw new Error(`stagger(): count must be a whole number of items, got ${n}`);
@@ -4420,7 +4492,7 @@ function distances(n, from, grid, seed) {
4420
4492
  if (n === 0) return [];
4421
4493
  if (from === "random") {
4422
4494
  const order = Array.from({ length: n }, (_, i) => i);
4423
- const r = chunk4NOTVQ3I_cjs.random(seed);
4495
+ const r = chunkTVVC4GVK_cjs.random(seed);
4424
4496
  for (let i = n - 1; i > 0; i--) {
4425
4497
  const j = Math.floor(r() * (i + 1));
4426
4498
  [order[i], order[j]] = [order[j], order[i]];
@@ -4482,7 +4554,7 @@ function wiggle(opts) {
4482
4554
  to[n] = p.around;
4483
4555
  return;
4484
4556
  }
4485
- const off = (chunk4NOTVQ3I_cjs.rand(seed * 7919 + pi * 104729 + s * 31 + 1) * 2 - 1) * p.amp;
4557
+ const off = (chunkTVVC4GVK_cjs.rand(seed * 7919 + pi * 104729 + s * 31 + 1) * 2 - 1) * p.amp;
4486
4558
  to[n] = typeof p.around === "number" ? p.around + off : `(${p.around}) ${off < 0 ? "-" : "+"} ${Math.abs(off)}`;
4487
4559
  });
4488
4560
  out.push({ at: at + s * stepLen, to, duration: stepLen, ease });
@@ -4555,7 +4627,7 @@ function animateText(text, style, options) {
4555
4627
  let idleEnd = len;
4556
4628
  if (outTween) {
4557
4629
  const begin = outTween.at !== void 0 ? void 0 : outTween.duration + (outWave - outDelays[i]);
4558
- const local = outTween.at !== void 0 ? Math.max(0, outTween.at + outDelays[i] - start) : -begin;
4630
+ const local = outTween.at !== void 0 ? Math.max(0, outTween.at - at + (outDelays[i] - delays[i])) : -begin;
4559
4631
  idleEnd = outTween.at !== void 0 ? local : len - begin;
4560
4632
  keyframes.push({
4561
4633
  at: local,
@@ -4570,7 +4642,7 @@ function animateText(text, style, options) {
4570
4642
  for (const k of ["x", "y", "rotation", "scale"]) if (idle[k] !== void 0) props[k] = { around: rest[k], amp: idle[k] };
4571
4643
  const freq = idle.freq ?? 1;
4572
4644
  const from = inTween?.duration ?? 0;
4573
- if (Object.keys(props).length > 0 && idleEnd - from >= 1 / freq) {
4645
+ if (Object.keys(props).length > 0 && idleEnd - from >= 1 / freq - 1e-9) {
4574
4646
  keyframes.push(...wiggle({ at: from, duration: idleEnd - from, freq, seed: (idle.seed ?? 0) * 1e3 + i, props }));
4575
4647
  }
4576
4648
  }
@@ -4621,7 +4693,7 @@ function resolveTween(spec, which) {
4621
4693
  let ease = t.ease ?? base?.ease ?? "power2.out";
4622
4694
  if (which === "out" && t.ease === void 0) ease = ease.replace(".out", ".in");
4623
4695
  const at = which === "out" && typeof t.at === "number" && Number.isFinite(t.at) ? t.at : void 0;
4624
- const wave = which === "out" && t.stagger !== void 0 ? t.stagger === 0 ? { each: 0 } : t.stagger : void 0;
4696
+ const wave = which === "out" && t.stagger !== void 0 ? typeof t.stagger === "number" ? { each: t.stagger } : t.stagger : void 0;
4625
4697
  return { from, to: { ...to }, duration: t.duration ?? base?.duration ?? 0.5, ease, ...at !== void 0 ? { at } : {}, ...wave ? { stagger: wave } : {} };
4626
4698
  }
4627
4699
 
@@ -4787,7 +4859,7 @@ function computeEnvelope(audio, options = {}) {
4787
4859
  re[i] = k >= 0 && k < length ? mono[k] * win[i] : 0;
4788
4860
  im[i] = 0;
4789
4861
  }
4790
- chunkSMHTOWXG_cjs.fft(re, im);
4862
+ chunkZAHCGFGC_cjs.fft(re, im);
4791
4863
  for (const n of names) {
4792
4864
  const [lo, hi] = bands[n];
4793
4865
  let p = 0;
@@ -4953,5 +5025,5 @@ exports.splitText = splitText;
4953
5025
  exports.stagger = stagger;
4954
5026
  exports.wiggle = wiggle;
4955
5027
  exports.withFade = withFade;
4956
- //# sourceMappingURL=chunk-YXIEPTFG.cjs.map
4957
- //# sourceMappingURL=chunk-YXIEPTFG.cjs.map
5028
+ //# sourceMappingURL=chunk-UXPTDNNA.cjs.map
5029
+ //# sourceMappingURL=chunk-UXPTDNNA.cjs.map