pixi-effects 0.45.0 → 0.47.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (72) hide show
  1. package/CHANGELOG.md +17 -0
  2. package/README.md +5 -5
  3. package/ai/chat-template.html +3 -3
  4. package/ai/tools/check.mjs +2 -2
  5. package/ai/tools/lookup.mjs +6 -1
  6. package/ai/tools/pixi-effects.mjs +1 -0
  7. package/ai/tools/render.mjs +17 -1
  8. package/dist/Composition-NSY7A32Y.js +7 -0
  9. package/dist/{Composition-YGBQX3HZ.js.map → Composition-NSY7A32Y.js.map} +1 -1
  10. package/dist/Composition-PME3P5LP.cjs +16 -0
  11. package/dist/{Composition-N6BOROJ2.cjs.map → Composition-PME3P5LP.cjs.map} +1 -1
  12. package/dist/Controller.d.cts +2 -2
  13. package/dist/Controller.d.ts +2 -2
  14. package/dist/{Movie-C-d4zN-N.d.cts → Movie-BqrAWN0g.d.cts} +6 -1
  15. package/dist/{Movie-lMSS9JwX.d.ts → Movie-sV8nJr_f.d.ts} +6 -1
  16. package/dist/{Spans-XTIWYSNO.cjs → Spans-2TZCNREQ.cjs} +9 -9
  17. package/dist/{Spans-XTIWYSNO.cjs.map → Spans-2TZCNREQ.cjs.map} +1 -1
  18. package/dist/{Spans-6L2ZVECX.js → Spans-TESJPQKX.js} +4 -4
  19. package/dist/{Spans-6L2ZVECX.js.map → Spans-TESJPQKX.js.map} +1 -1
  20. package/dist/{chunk-O66JGPVV.cjs → chunk-23SXKSUI.cjs} +18 -4
  21. package/dist/chunk-23SXKSUI.cjs.map +1 -0
  22. package/dist/{chunk-K24FF4BT.js → chunk-4SEL6W7H.js} +18 -4
  23. package/dist/chunk-4SEL6W7H.js.map +1 -0
  24. package/dist/{chunk-3HZV3UHU.cjs → chunk-DKCMJILW.cjs} +67 -65
  25. package/dist/chunk-DKCMJILW.cjs.map +1 -0
  26. package/dist/{chunk-4QVQ5UJJ.js → chunk-DM2OLQOJ.js} +5 -6
  27. package/dist/chunk-DM2OLQOJ.js.map +1 -0
  28. package/dist/{chunk-ICI6ACHY.js → chunk-FPV3NPYC.js} +8 -6
  29. package/dist/chunk-FPV3NPYC.js.map +1 -0
  30. package/dist/{chunk-3IO5RWE4.cjs → chunk-I6VUQ2CR.cjs} +188 -74
  31. package/dist/chunk-I6VUQ2CR.cjs.map +1 -0
  32. package/dist/{chunk-X62H3RBG.cjs → chunk-LALEBV63.cjs} +5 -6
  33. package/dist/chunk-LALEBV63.cjs.map +1 -0
  34. package/dist/{chunk-Z6JQO7GX.js → chunk-W46VWAZC.js} +146 -32
  35. package/dist/chunk-W46VWAZC.js.map +1 -0
  36. package/dist/index.cjs +35 -35
  37. package/dist/index.d.cts +5 -3
  38. package/dist/index.d.ts +5 -3
  39. package/dist/index.js +4 -4
  40. package/dist/loader.css +1 -1
  41. package/dist/present.cjs +4 -4
  42. package/dist/present.d.cts +2 -2
  43. package/dist/present.d.ts +2 -2
  44. package/dist/present.js +4 -4
  45. package/dist/sound.cjs +2 -2
  46. package/dist/sound.js +1 -1
  47. package/dist/three.cjs +194 -15
  48. package/dist/three.cjs.map +1 -1
  49. package/dist/three.d.cts +75 -4
  50. package/dist/three.d.ts +75 -4
  51. package/dist/three.js +191 -13
  52. package/dist/three.js.map +1 -1
  53. package/dist/{types-C5xo0fzb.d.cts → types-CTSrPvY3.d.cts} +64 -58
  54. package/dist/{types-C5xo0fzb.d.ts → types-CTSrPvY3.d.ts} +64 -58
  55. package/docs/api.md +2 -1
  56. package/docs/dsl.md +25 -1
  57. package/llms-full.txt +65 -6
  58. package/package.json +1 -1
  59. package/skills/pixi-effects/SKILL.md +1 -1
  60. package/skills/pixi-effects/reference/cheatsheet.md +3 -3
  61. package/skills/pixi-effects/reference/recipes.md +34 -0
  62. package/skills/pixi-effects/template.html +6 -6
  63. package/dist/Composition-N6BOROJ2.cjs +0 -16
  64. package/dist/Composition-YGBQX3HZ.js +0 -7
  65. package/dist/chunk-3HZV3UHU.cjs.map +0 -1
  66. package/dist/chunk-3IO5RWE4.cjs.map +0 -1
  67. package/dist/chunk-4QVQ5UJJ.js.map +0 -1
  68. package/dist/chunk-ICI6ACHY.js.map +0 -1
  69. package/dist/chunk-K24FF4BT.js.map +0 -1
  70. package/dist/chunk-O66JGPVV.cjs.map +0 -1
  71. package/dist/chunk-X62H3RBG.cjs.map +0 -1
  72. package/dist/chunk-Z6JQO7GX.js.map +0 -1
@@ -1,8 +1,8 @@
1
1
  'use strict';
2
2
 
3
- var chunk3HZV3UHU_cjs = require('./chunk-3HZV3UHU.cjs');
4
- var chunkX62H3RBG_cjs = require('./chunk-X62H3RBG.cjs');
5
- var chunkO66JGPVV_cjs = require('./chunk-O66JGPVV.cjs');
3
+ var chunkDKCMJILW_cjs = require('./chunk-DKCMJILW.cjs');
4
+ var chunkLALEBV63_cjs = require('./chunk-LALEBV63.cjs');
5
+ var chunk23SXKSUI_cjs = require('./chunk-23SXKSUI.cjs');
6
6
  var chunkJN7JHNZR_cjs = require('./chunk-JN7JHNZR.cjs');
7
7
  var chunk3SAS3ZE5_cjs = require('./chunk-3SAS3ZE5.cjs');
8
8
  var chunkTWZNOUM7_cjs = require('./chunk-TWZNOUM7.cjs');
@@ -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 = chunkO66JGPVV_cjs.resampleThrough(a, buffer.getChannelData(0), buffer.numberOfChannels > 1 ? buffer.getChannelData(1) : buffer.getChannelData(0), buffer.sampleRate, sampleRate, totalDuration);
112
+ const r = chunk23SXKSUI_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);
@@ -143,18 +143,86 @@ async function mixdown(audios, totalDuration, sampleRate = 44100, pace) {
143
143
  }
144
144
  var KNEE = 0.9;
145
145
  var LOUDNESS_CEILING_DB = -1;
146
+ var MAX_LIMIT_DB = 10;
147
+ function limitInto(source, out, gain, ceil, sampleRate) {
148
+ const n = source[0]?.length ?? 0;
149
+ const req = new Float32Array(n);
150
+ for (let i = 0; i < n; i++) {
151
+ let p = 0;
152
+ for (const c of source) {
153
+ const a = Math.abs(c[i]) * gain;
154
+ if (a > p) p = a;
155
+ }
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);
170
+ for (let i = 0; i < n; i++) {
171
+ run += mn[i];
172
+ if (i > L) run -= mn[i - L - 1];
173
+ const avg = run / Math.min(i + 1, L + 1);
174
+ const g = Math.min(avg, prev + (1 - prev) * release);
175
+ gains[i] = g;
176
+ prev = g;
177
+ if (g < lowest) {
178
+ lowest = g;
179
+ lowestAt = i;
180
+ }
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;
187
+ }
188
+ }
189
+ return { reductionDb: lowest < 1 ? -20 * Math.log10(lowest) : 0, at: lowestAt / sampleRate };
190
+ }
146
191
  function normalizeMix(buffer, target) {
147
192
  const chans = [];
148
193
  for (let ch = 0; ch < buffer.numberOfChannels; ch++) chans.push(buffer.getChannelData(ch));
149
- const m = chunkO66JGPVV_cjs.measureLoudness(chans, buffer.sampleRate, { truePeak: false });
194
+ const sr = buffer.sampleRate;
195
+ const m = chunk23SXKSUI_cjs.measureLoudness(chans, sr, { truePeak: false });
150
196
  if (m.integratedLufs === null) return { target, gainDb: 0, reachedLufs: null, limited: false };
197
+ const original = chans.map((c) => Float32Array.from(c));
151
198
  let gainDb = target - m.integratedLufs;
152
- const room = LOUDNESS_CEILING_DB - m.samplePeakDb;
153
- const limited = gainDb > room;
154
- if (limited) gainDb = room;
155
- const gain = 10 ** (gainDb / 20);
156
- for (const d of chans) for (let i = 0; i < d.length; i++) d[i] = d[i] * gain;
157
- return { target, gainDb: Math.round(gainDb * 10) / 10, reachedLufs: Math.round((m.integratedLufs + gainDb) * 10) / 10, limited };
199
+ 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;
203
+ if (gainDb > cap) gainDb = cap;
204
+ applied = limitInto(original, chans, 10 ** (gainDb / 20), 10 ** (ceilDb / 20), sr);
205
+ measured = chunk23SXKSUI_cjs.measureLoudness(chans, sr);
206
+ const overTp = measured.truePeakDb - LOUDNESS_CEILING_DB;
207
+ if (overTp > 0.02) {
208
+ ceilDb -= overTp + 0.02;
209
+ continue;
210
+ }
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
+ }
217
+ const limited = measured.integratedLufs < target - 0.15;
218
+ const reduction = Math.round(applied.reductionDb * 10) / 10;
219
+ return {
220
+ target,
221
+ gainDb: Math.round(gainDb * 10) / 10,
222
+ reachedLufs: measured.integratedLufs,
223
+ limited,
224
+ ...reduction >= 0.1 ? { limiterDb: reduction, limiterAt: Math.round(applied.at * 100) / 100 } : {}
225
+ };
158
226
  }
159
227
  function limitMix(buffer, audios) {
160
228
  let peak = 0;
@@ -172,7 +240,7 @@ function limitMix(buffer, audios) {
172
240
  }
173
241
  const peakAt = peakIndex / buffer.sampleRate;
174
242
  if (peak > 1) {
175
- const playing = audios.map((a) => ({ a, ...chunkO66JGPVV_cjs.playSpan(a, buffer.duration) })).filter((s) => s.start <= peakAt && peakAt < s.end).map((s) => s.a.layer ?? "an audio layer");
243
+ const playing = audios.map((a) => ({ a, ...chunk23SXKSUI_cjs.playSpan(a, buffer.duration) })).filter((s) => s.start <= peakAt && peakAt < s.end).map((s) => s.a.layer ?? "an audio layer");
176
244
  console.warn(
177
245
  `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.`
178
246
  );
@@ -1134,8 +1202,8 @@ function expandTransitions(spec) {
1134
1202
  }
1135
1203
  const transitions = out.transitions;
1136
1204
  const sequences = out.sequences ?? [];
1137
- const remap = out.duration !== void 0 ? chunkX62H3RBG_cjs.timeRemapOf(out) : null;
1138
- const parentDuration = remap ? chunkX62H3RBG_cjs.contentLength(remap, out.duration) : out.duration ?? Infinity;
1205
+ const remap = out.duration !== void 0 ? chunkLALEBV63_cjs.timeRemapOf(out) : null;
1206
+ const parentDuration = remap ? chunkLALEBV63_cjs.contentLength(remap, out.duration) : out.duration ?? Infinity;
1139
1207
  const compW = out.width;
1140
1208
  const compH = out.height;
1141
1209
  const threeDByName = new Set(sequences.filter((s) => s.name && s.threeD).map((s) => s.name));
@@ -1203,16 +1271,16 @@ function expandTransitions(spec) {
1203
1271
  if (toUses > 1) {
1204
1272
  throw new Error(`pixi-effects: ${tag} sequence "${t.to}" is used as \`to\` in more than one transition`);
1205
1273
  }
1206
- const tStart = chunkX62H3RBG_cjs.resolveAt(t.at, parentDuration);
1274
+ const tStart = chunkLALEBV63_cjs.resolveAt(t.at, parentDuration);
1207
1275
  const tEnd = tStart + t.duration;
1208
1276
  const fromSeq = fromEntry.seq;
1209
- const fromStart = chunkX62H3RBG_cjs.resolveAt(fromSeq.at, parentDuration);
1277
+ const fromStart = chunkLALEBV63_cjs.resolveAt(fromSeq.at, parentDuration);
1210
1278
  const fromEnd = fromStart + (fromSeq.duration ?? parentDuration);
1211
1279
  if (tStart < fromStart - EPS || tEnd > fromEnd + EPS) {
1212
1280
  throw new Error(`pixi-effects: ${tag} window [${tStart}, ${tEnd}] is not covered by \`from\` "${t.from}" (lives [${fromStart}, ${fromEnd}], so it ends at ${fromEnd})`);
1213
1281
  }
1214
1282
  const toSeq = toEntry.seq;
1215
- const toStart = chunkX62H3RBG_cjs.resolveAt(toSeq.at, parentDuration);
1283
+ const toStart = chunkLALEBV63_cjs.resolveAt(toSeq.at, parentDuration);
1216
1284
  const toEnd = toStart + (toSeq.duration ?? parentDuration);
1217
1285
  if (tStart < toStart - EPS || tEnd > toEnd + EPS) {
1218
1286
  throw new Error(`pixi-effects: ${tag} window [${tStart}, ${tEnd}] is not covered by \`to\` "${t.to}" (lives [${toStart}, ${toEnd}], so it starts at ${toStart})`);
@@ -1272,7 +1340,7 @@ function wrapAsFullComposition(seq, compW, compH) {
1272
1340
  delete inner.at;
1273
1341
  delete inner.duration;
1274
1342
  const m = inner.mask;
1275
- const carriesMatte = m !== void 0 && chunk3HZV3UHU_cjs.maskSourceOf(m).kind === "refs";
1343
+ const carriesMatte = m !== void 0 && chunkDKCMJILW_cjs.maskSourceOf(m).kind === "refs";
1276
1344
  if (carriesMatte) delete inner.mask;
1277
1345
  return {
1278
1346
  ...carriesMatte ? { mask: m } : {},
@@ -1542,7 +1610,7 @@ function canvasReport(root) {
1542
1610
  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") : "";
1543
1611
  out.push({ name: seq.spec.name ?? name, moments, drawMs, ok: !differs.length && !threw.length, problem, kind });
1544
1612
  }
1545
- if (seq instanceof chunk3HZV3UHU_cjs.CompositionSequence) walk3(seq);
1613
+ if (seq instanceof chunkDKCMJILW_cjs.CompositionSequence) walk3(seq);
1546
1614
  };
1547
1615
  const walk3 = (comp) => {
1548
1616
  for (const { seq } of comp.layers()) {
@@ -1594,8 +1662,8 @@ function motionOf(keyframes, local, lives, keys = (k) => GEOMETRY.has(k)) {
1594
1662
  let length = 0;
1595
1663
  if (typeof kf.duration === "number") length = kf.duration;
1596
1664
  else if (kf.duration === "auto") {
1597
- const sp = chunkX62H3RBG_cjs.parseSpring(kf.ease);
1598
- length = sp ? Math.round(chunkX62H3RBG_cjs.springDuration(sp) * 1e3) / 1e3 : 0.5;
1665
+ const sp = chunkLALEBV63_cjs.parseSpring(kf.ease);
1666
+ length = sp ? Math.round(chunkLALEBV63_cjs.springDuration(sp) * 1e3) / 1e3 : 0.5;
1599
1667
  }
1600
1668
  if (!(length > 0)) continue;
1601
1669
  const kat = kf.at ?? 0;
@@ -1634,7 +1702,7 @@ function inspectScene(root, frame, time, canvas, opts = {}) {
1634
1702
  const name = seq.spec.name;
1635
1703
  const label = name ?? `${seq.spec.type}#${i}`;
1636
1704
  const alpha = (t?.alpha ?? 1) * carriers.reduce((a, c) => a * (c.target?.alpha ?? 1), 1);
1637
- const alive = (threeD ? display.visible : t?.renderable ?? true) && carriers.every((c) => c.target?.renderable ?? true);
1705
+ const alive = (threeD ? display.visible : (t?.renderable ?? true) && t?.visible !== false) && carriers.every((c) => c.target?.renderable ?? true);
1638
1706
  const visible = parentVisible && alive && alpha > 0;
1639
1707
  let bounds = null;
1640
1708
  if (!insideThreeD) {
@@ -1675,7 +1743,7 @@ function inspectScene(root, frame, time, canvas, opts = {}) {
1675
1743
  if (motion.ever) hasMotion.add(info);
1676
1744
  let shown = bounds ? clip(bounds, view) : null;
1677
1745
  const maskTarget = seq.maskSequence?.target;
1678
- const maskSrc = chunk3HZV3UHU_cjs.maskSourceOf(seq.spec.mask);
1746
+ const maskSrc = chunkDKCMJILW_cjs.maskSourceOf(seq.spec.mask);
1679
1747
  if (shown && maskTarget && !(maskSrc.kind === "inline" && maskSrc.invert)) {
1680
1748
  const m = maskTarget.getBounds();
1681
1749
  shown = clip(shown, { x: m.x, y: m.y, width: m.width, height: m.height });
@@ -1684,7 +1752,7 @@ function inspectScene(root, frame, time, canvas, opts = {}) {
1684
1752
  const group = parentScene ?? (name !== void 0 && blending.has(name) ? name : void 0);
1685
1753
  if (group) scene.set(info, group);
1686
1754
  if (zero || seq.showsNothingYet) scaledToNothing.add(info);
1687
- if (seq instanceof chunk3HZV3UHU_cjs.CompositionSequence) walk3(seq, prefix + label + "/", insideThreeD || threeD, visible, group, motion, zero, parentAlpha * alpha);
1755
+ if (seq instanceof chunkDKCMJILW_cjs.CompositionSequence) walk3(seq, prefix + label + "/", insideThreeD || threeD, visible, group, motion, zero, parentAlpha * alpha);
1688
1756
  });
1689
1757
  };
1690
1758
  walk3(root, "", false, true);
@@ -1814,7 +1882,22 @@ function pickScene(root, x, y, opts = {}, deps = {}) {
1814
1882
  return true;
1815
1883
  };
1816
1884
  const inside = (b, p) => p.x >= b.x && p.x <= b.x + b.width && p.y >= b.y && p.y <= b.y + b.height;
1817
- const walk3 = (comp, prefix, parentVisible) => {
1885
+ const hiddenSources = /* @__PURE__ */ new Map();
1886
+ const forwards = [];
1887
+ const rays = /* @__PURE__ */ new Map();
1888
+ const rayOf = (seq, d, local, lb) => {
1889
+ if (typeof seq.pickAt !== "function") return null;
1890
+ if (!rays.has(seq)) rays.set(seq, seq.pickAt((local.x - lb.x) / lb.width, (local.y - lb.y) / lb.height));
1891
+ return rays.get(seq);
1892
+ };
1893
+ const forwardThrough = (seq, d, path, local, lb) => {
1894
+ const shows = seq.screenOf;
1895
+ if (!shows || !inside(lb, local)) return;
1896
+ const r = rayOf(seq, d, local, lb);
1897
+ 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) });
1899
+ };
1900
+ const walk3 = (comp, prefix, parentVisible, at = { x, y }, orderBase, via) => {
1818
1901
  comp.layers().forEach(({ seq, display, threeD, carriers, quad }, i) => {
1819
1902
  const d = display;
1820
1903
  if (!d) return;
@@ -1822,16 +1905,25 @@ function pickScene(root, x, y, opts = {}, deps = {}) {
1822
1905
  const name = seq.spec.name;
1823
1906
  const path = prefix + (name ?? `${seq.spec.type}#${i}`);
1824
1907
  const alpha = (t?.alpha ?? 1) * carriers.reduce((a, c) => a * (c.target?.alpha ?? 1), 1);
1825
- const alive = (threeD ? d.visible : t?.renderable ?? true) && carriers.every((c) => c.target?.renderable ?? true);
1908
+ const alive = (threeD ? d.visible : (t?.renderable ?? true) && t?.visible !== false) && carriers.every((c) => c.target?.renderable ?? true);
1826
1909
  const visible = parentVisible && alive && alpha > 0;
1827
- if (!visible) return;
1828
- const isComposition = seq instanceof chunk3HZV3UHU_cjs.CompositionSequence;
1910
+ if (!visible) {
1911
+ if (seq instanceof chunkDKCMJILW_cjs.CompositionSequence && !threeD && parentVisible && alpha > 0 && t?.visible === false && (t?.renderable ?? true) && carriers.every((c) => c.target?.renderable ?? true)) hiddenSources.set(seq, path);
1912
+ return;
1913
+ }
1914
+ const isComposition = seq instanceof chunkDKCMJILW_cjs.CompositionSequence;
1829
1915
  if (isComposition && !threeD) {
1830
- walk3(seq, path + "/", visible);
1916
+ walk3(seq, path + "/", visible, at, orderBase, via);
1831
1917
  return;
1832
1918
  }
1833
- if (!wanted(name, path)) return;
1834
- const point = { x, y };
1919
+ if (!wanted(name, path)) {
1920
+ if (!threeD) {
1921
+ const lp = d.toLocal(at);
1922
+ forwardThrough(seq, d, path, lp, d.getLocalBounds());
1923
+ }
1924
+ return;
1925
+ }
1926
+ const point = at;
1835
1927
  let hit = null;
1836
1928
  if (threeD) {
1837
1929
  if (quad && pointInQuad(quad, d.toLocal(point).x, d.toLocal(point).y)) hit = { name, path, type: seq.spec.type, kind: "card", point };
@@ -1839,7 +1931,10 @@ function pickScene(root, x, y, opts = {}, deps = {}) {
1839
1931
  const local = d.toLocal(point);
1840
1932
  const lb = d.getLocalBounds();
1841
1933
  if (inside(lb, local)) {
1842
- const three = typeof seq.pickAt === "function" ? seq.pickAt((local.x - lb.x) / lb.width, (local.y - lb.y) / lb.height) : void 0;
1934
+ const isThree = typeof seq.pickAt === "function";
1935
+ const ray = isThree ? rayOf(seq, d, local, lb) : void 0;
1936
+ if (isThree) forwardThrough(seq, d, path, local, lb);
1937
+ const three = ray ? { object: ray.object, point: ray.point, distance: ray.distance, object3d: ray.object3d } : isThree ? null : void 0;
1843
1938
  if (three) {
1844
1939
  const planeOf = seq.dragPlane;
1845
1940
  if (planeOf) {
@@ -1872,12 +1967,18 @@ function pickScene(root, x, y, opts = {}, deps = {}) {
1872
1967
  }
1873
1968
  if (!hit) return;
1874
1969
  const maskTarget = seq.maskSequence?.target;
1875
- const maskSrc = chunk3HZV3UHU_cjs.maskSourceOf(seq.spec.mask);
1970
+ const maskSrc = chunkDKCMJILW_cjs.maskSourceOf(seq.spec.mask);
1876
1971
  if (maskTarget && !(maskSrc.kind === "inline" && maskSrc.invert) && !inside(maskTarget.getBounds(), point)) return;
1877
- found.push({ hit, order: orderOf(d) });
1972
+ found.push({ hit: via ? { ...hit, point: { x, y }, via } : hit, order: orderBase ? [...orderBase, ...orderOf(d)] : orderOf(d) });
1878
1973
  });
1879
1974
  };
1880
1975
  walk3(root, "", true);
1976
+ for (const f of forwards) {
1977
+ const path = hiddenSources.get(f.source);
1978
+ 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 });
1980
+ walk3(f.source, path + "/", true, g, f.order, f.via);
1981
+ }
1881
1982
  return found.sort((a, b) => above(a.order, b.order)).map((f) => f.hit);
1882
1983
  }
1883
1984
  var cache = /* @__PURE__ */ new WeakMap();
@@ -1910,7 +2011,7 @@ function walk(root, visit) {
1910
2011
  visit(seq);
1911
2012
  const mask = seq.maskSequence;
1912
2013
  if (mask) visit(mask);
1913
- if (seq instanceof chunk3HZV3UHU_cjs.CompositionSequence) walk(seq, visit);
2014
+ if (seq instanceof chunkDKCMJILW_cjs.CompositionSequence) walk(seq, visit);
1914
2015
  }
1915
2016
  }
1916
2017
  function reusePoolOf(old) {
@@ -2011,7 +2112,7 @@ var round = (n) => Math.round(n * 1e6) / 1e6;
2011
2112
  var family = (name) => name.replace(/\d+/g, "#");
2012
2113
  function detailOf(seq) {
2013
2114
  const s = seq.spec;
2014
- const spans = chunkX62H3RBG_cjs.spansMeta.get(seq.spec)?.preview;
2115
+ const spans = chunkLALEBV63_cjs.spansMeta.get(seq.spec)?.preview;
2015
2116
  if (spans) return spans.length > 60 ? spans.slice(0, 57) + "\u2026" : spans;
2016
2117
  if (typeof s.text === "string") return s.text.length > 60 ? s.text.slice(0, 57) + "\u2026" : s.text;
2017
2118
  if (typeof s.asset === "string") return `asset \u201C${s.asset}\u201D`;
@@ -2027,10 +2128,10 @@ function walk2(comp, parentStart, parentEnd, depth, prefix, rows, transitions, l
2027
2128
  const start = parentStart + (seq.at ?? 0);
2028
2129
  const end = Math.min(parentEnd, seq.duration !== void 0 ? start + seq.duration : parentEnd);
2029
2130
  const span = end - start;
2030
- const keys = (seq.spec.keyframes ?? []).map((kf) => round(start + chunkX62H3RBG_cjs.resolveAt(kf.at, span))).sort((a, b) => a - b).slice(0, MAX_KEYS);
2131
+ const keys = (seq.spec.keyframes ?? []).map((kf) => round(start + chunkLALEBV63_cjs.resolveAt(kf.at, span))).sort((a, b) => a - b).slice(0, MAX_KEYS);
2031
2132
  const row = { path: prefix + name, name, type: seq.spec.type, depth, start: round(start), end: round(end), keys, detail: detailOf(seq), ...local ? { local } : {} };
2032
2133
  const inner = [];
2033
- const isComp = seq instanceof chunk3HZV3UHU_cjs.CompositionSequence;
2134
+ const isComp = seq instanceof chunkDKCMJILW_cjs.CompositionSequence;
2034
2135
  if (isComp) {
2035
2136
  const label = seq.remapLabel();
2036
2137
  walk2(seq, label ? 0 : start, label ? seq.contentSpan : end, depth + 1, prefix + name + "/", inner, transitions, label ? `${name} ${label}` : local);
@@ -2189,7 +2290,7 @@ function sheetLayout(n, columns, cellW, srcW, srcH, labelH) {
2189
2290
  }
2190
2291
 
2191
2292
  // src/core/vars.ts
2192
- var RESERVED_NAMES = ["w", "h", "W", "H", "GW", "GH", "t", "d", "T", "contain", "cover", "value", ...chunkX62H3RBG_cjs.EXPRESSION_NAMES];
2293
+ var RESERVED_NAMES = ["w", "h", "W", "H", "GW", "GH", "t", "d", "T", "contain", "cover", "value", ...chunkLALEBV63_cjs.EXPRESSION_NAMES];
2193
2294
  var NAME = /^[A-Za-z_][A-Za-z0-9_]*$/;
2194
2295
  var isPlain = (v) => !!v && typeof v === "object" && (Object.getPrototypeOf(v) === Object.prototype || Object.getPrototypeOf(v) === null);
2195
2296
  function normalizeVars(raw, where, warn = (m) => console.warn(m)) {
@@ -2733,8 +2834,8 @@ var Movie = class {
2733
2834
  this.stops = normalizeStops(applyVars(options.composition, this._vars)?.stops, this.duration, this.frameRate);
2734
2835
  this._posterFrame = poster.frame;
2735
2836
  this.background = options.background ?? "#000000";
2736
- this.timeline = chunkX62H3RBG_cjs.createTimeline({ paused: true, defaults: { ease: "none" } });
2737
- chunkX62H3RBG_cjs.lengthen(this.timeline, this.duration);
2837
+ this.timeline = chunkLALEBV63_cjs.createTimeline({ paused: true, defaults: { ease: "none" } });
2838
+ chunkLALEBV63_cjs.lengthen(this.timeline, this.duration);
2738
2839
  const baseAppOptions = {
2739
2840
  width: this.width,
2740
2841
  height: this.height,
@@ -2820,6 +2921,7 @@ var Movie = class {
2820
2921
  frameRate: this.frameRate,
2821
2922
  vars: numericVars(vars),
2822
2923
  ...gpuField,
2924
+ ...this.app?.renderer ? { pixiRenderer: this.app.renderer } : {},
2823
2925
  ...reuse ? { reuse } : {},
2824
2926
  ...lp ? { onLayerBuilt: async () => {
2825
2927
  lp.tick();
@@ -2832,7 +2934,7 @@ var Movie = class {
2832
2934
  height: sizeWins ? height : spec.height ?? height,
2833
2935
  duration: spec.duration ?? this.duration
2834
2936
  } : void 0;
2835
- await chunkX62H3RBG_cjs.ensureFilterLibrary(seededComposition);
2937
+ await chunkLALEBV63_cjs.ensureFilterLibrary(seededComposition);
2836
2938
  if (chunkJN7JHNZR_cjs.usesAdvancedBlend(seededComposition)) await chunkJN7JHNZR_cjs.enableAdvancedBlend(this.app.renderer);
2837
2939
  const userComposition = seededComposition ? expandTransitions(seededComposition) : void 0;
2838
2940
  const rootSeqSpec = {
@@ -2843,7 +2945,7 @@ var Movie = class {
2843
2945
  ...userComposition
2844
2946
  };
2845
2947
  if (userComposition) carryTransitionWindows(userComposition, rootSeqSpec);
2846
- const sequence = new chunk3HZV3UHU_cjs.CompositionSequence(rootSeqSpec, null, rootShape);
2948
+ const sequence = new chunkDKCMJILW_cjs.CompositionSequence(rootSeqSpec, null, rootShape);
2847
2949
  lp?.begin("build", countLayers(rootSeqSpec));
2848
2950
  try {
2849
2951
  await sequence.build();
@@ -2981,8 +3083,8 @@ var Movie = class {
2981
3083
  if (!changed) return { reason, ms: 0, audio: "none", frame: this.currentFrame, playing: this.isPlaying, unchanged: true };
2982
3084
  sequence = await this._construct(composition, vars, width, height, void 0, this._resized || sizeChanged, reusePoolOf(this._rootSequence));
2983
3085
  alive();
2984
- const timeline = chunkX62H3RBG_cjs.createTimeline({ paused: true, defaults: { ease: "none" } });
2985
- chunkX62H3RBG_cjs.lengthen(timeline, this.duration);
3086
+ const timeline = chunkLALEBV63_cjs.createTimeline({ paused: true, defaults: { ease: "none" } });
3087
+ chunkLALEBV63_cjs.lengthen(timeline, this.duration);
2986
3088
  sequence.bindTimeline(timeline);
2987
3089
  const hadSound = this.audioBuffer !== null;
2988
3090
  const audio = await this._mixSound(sequence, this._ensureAudioContext());
@@ -3389,7 +3491,7 @@ var Movie = class {
3389
3491
  chunkTWZNOUM7_cjs.warnUnknownOptions("movie.inspectAudio()", opts, ["window", "scenes"]);
3390
3492
  this._requireReady("inspectAudio");
3391
3493
  const scenes = opts.scenes ?? namedScenes(this.timelineData().rows);
3392
- return chunkO66JGPVV_cjs.analyzeAudio(this.audioBuffer, this._audioSources, this._mixStats, this.duration, { ...opts, scenes });
3494
+ return chunk23SXKSUI_cjs.analyzeAudio(this.audioBuffer, this._audioSources, this._mixStats, this.duration, { ...opts, scenes });
3393
3495
  }
3394
3496
  /**
3395
3497
  * Draw the stage now. PixiJS's CullerPlugin only culls inside `app.render()` (the ticker), and we render
@@ -3751,7 +3853,7 @@ function countLayers(spec) {
3751
3853
  n += 1 + countLayers(s);
3752
3854
  const spans = s.spans;
3753
3855
  if (Array.isArray(spans)) n += spans.length;
3754
- const src = chunk3HZV3UHU_cjs.maskSourceOf(mask);
3856
+ const src = chunkDKCMJILW_cjs.maskSourceOf(mask);
3755
3857
  if (src.kind === "inline") n += 1 + countLayers(src.spec);
3756
3858
  }
3757
3859
  return n;
@@ -3840,8 +3942,8 @@ function splitVertical(text, style, ts, right, top, align, by) {
3840
3942
 
3841
3943
  // src/presets/_ease.ts
3842
3944
  function parseEase2(name) {
3843
- chunkX62H3RBG_cjs.checkEase(name, "a preset");
3844
- return chunkX62H3RBG_cjs.parseEase(name);
3945
+ chunkLALEBV63_cjs.checkEase(name, "a preset");
3946
+ return chunkLALEBV63_cjs.parseEase(name);
3845
3947
  }
3846
3948
 
3847
3949
  // src/presets/kenBurns.ts
@@ -3976,17 +4078,17 @@ function orbit(opts) {
3976
4078
  throw new Error("pixi-effects: orbit() cannot combine `radius` with `dollyZoom` \u2014 the radius follows the animated fov");
3977
4079
  }
3978
4080
  const [cx, cy] = opts.center ?? [width / 2, height / 2];
3979
- const fixedR = opts.radius ?? chunkX62H3RBG_cjs.homeDistance(height, fov ?? chunkX62H3RBG_cjs.DEFAULT_FOV);
4081
+ const fixedR = opts.radius ?? chunkLALEBV63_cjs.homeDistance(height, fov ?? chunkLALEBV63_cjs.DEFAULT_FOV);
3980
4082
  const start = opts.start ?? -degrees / 2;
3981
4083
  const steps = Math.max(1, Math.round(duration * stepsPerSecond));
3982
4084
  const easeFn = parseEase2(ease);
3983
4085
  const dollyEase = dolly ? parseEase2(dolly.ease ?? ease) : null;
3984
4086
  const pos = (i) => {
3985
4087
  const p = i / steps;
3986
- const a = (start + degrees * easeFn(p)) * chunkX62H3RBG_cjs.DEG;
4088
+ const a = (start + degrees * easeFn(p)) * chunkLALEBV63_cjs.DEG;
3987
4089
  if (!dolly) return { x: cx + fixedR * Math.sin(a), z: fixedR * Math.cos(a) };
3988
4090
  const f = dolly.from + (dolly.to - dolly.from) * dollyEase(p);
3989
- const R = chunkX62H3RBG_cjs.homeDistance(height, f);
4091
+ const R = chunkLALEBV63_cjs.homeDistance(height, f);
3990
4092
  return { x: cx + R * Math.sin(a), z: R * Math.cos(a), fov: f };
3991
4093
  };
3992
4094
  const keyframes = [];
@@ -4041,7 +4143,7 @@ function particles(opts) {
4041
4143
  const round2 = (v) => Math.round(v * 1e6) / 1e6;
4042
4144
  const out = [];
4043
4145
  for (let i = 0; i < count; i++) {
4044
- const r = chunkX62H3RBG_cjs.random(seed * 1000003 + i * 7919 + 17);
4146
+ const r = chunkLALEBV63_cjs.random(seed * 1000003 + i * 7919 + 17);
4045
4147
  const pick = ([a2, b]) => a2 + r() * (b - a2);
4046
4148
  const born = at + r() * emit;
4047
4149
  const lifetime = pick(life);
@@ -4054,7 +4156,7 @@ function particles(opts) {
4054
4156
  const turn = spin ? pick(spin) : 0;
4055
4157
  const startAngle = spin ? r() * 360 : 0;
4056
4158
  const swayAmp = sway?.amp ?? 0, swayFreq = sway?.freq ?? 1;
4057
- const n0 = sway ? chunkX62H3RBG_cjs.noise(0, seed * 1000003 + i) : 0;
4159
+ const n0 = sway ? chunkLALEBV63_cjs.noise(0, seed * 1000003 + i) : 0;
4058
4160
  const at_ = (t) => {
4059
4161
  const axis = (p0, v0, acc) => {
4060
4162
  if (drag > 0) {
@@ -4063,7 +4165,7 @@ function particles(opts) {
4063
4165
  }
4064
4166
  return p0 + v0 * t + 0.5 * acc * t * t;
4065
4167
  };
4066
- const sw = sway ? swayAmp * (chunkX62H3RBG_cjs.noise(t * swayFreq, seed * 1000003 + i) - n0) : 0;
4168
+ const sw = sway ? swayAmp * (chunkLALEBV63_cjs.noise(t * swayFreq, seed * 1000003 + i) - n0) : 0;
4067
4169
  return { x: axis(x0, vx, wind) + sw, y: axis(y0, vy, gravity) };
4068
4170
  };
4069
4171
  const kfs = [];
@@ -4125,7 +4227,7 @@ function followPath(opts) {
4125
4227
  if (steps > MAX_STEPS) throw new Error(`followPath(): ${steps} samples is too many (max ${MAX_STEPS}); lower frameRate or duration`);
4126
4228
  const segments = [];
4127
4229
  let total = 0;
4128
- for (const line of chunkX62H3RBG_cjs.flattenSvgPath(d)) {
4230
+ for (const line of chunkLALEBV63_cjs.flattenSvgPath(d)) {
4129
4231
  const p = line.closed ? [...line.pts, line.pts[0], line.pts[1]] : line.pts;
4130
4232
  for (let i = 2; i < p.length; i += 2) {
4131
4233
  const len = Math.hypot(p[i] - p[i - 2], p[i + 1] - p[i - 1]);
@@ -4178,7 +4280,7 @@ function followPath(opts) {
4178
4280
  // src/core/pathSample.ts
4179
4281
  function samplePath(d) {
4180
4282
  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"');
4181
- const lines = chunkX62H3RBG_cjs.flattenSvgPath(d);
4283
+ const lines = chunkLALEBV63_cjs.flattenSvgPath(d);
4182
4284
  const segments = [];
4183
4285
  let length = 0;
4184
4286
  for (const line of lines) {
@@ -4209,7 +4311,7 @@ function samplePath(d) {
4209
4311
  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) };
4210
4312
  },
4211
4313
  trim(start = 0, end = 1) {
4212
- return chunkX62H3RBG_cjs.trimPolylines(lines, start, end).map((l) => l.closed ? [...l.pts, l.pts[0], l.pts[1]] : l.pts);
4314
+ return chunkLALEBV63_cjs.trimPolylines(lines, start, end).map((l) => l.closed ? [...l.pts, l.pts[0], l.pts[1]] : l.pts);
4213
4315
  }
4214
4316
  };
4215
4317
  }
@@ -4318,7 +4420,7 @@ function distances(n, from, grid, seed) {
4318
4420
  if (n === 0) return [];
4319
4421
  if (from === "random") {
4320
4422
  const order = Array.from({ length: n }, (_, i) => i);
4321
- const r = chunkX62H3RBG_cjs.random(seed);
4423
+ const r = chunkLALEBV63_cjs.random(seed);
4322
4424
  for (let i = n - 1; i > 0; i--) {
4323
4425
  const j = Math.floor(r() * (i + 1));
4324
4426
  [order[i], order[j]] = [order[j], order[i]];
@@ -4380,7 +4482,7 @@ function wiggle(opts) {
4380
4482
  to[n] = p.around;
4381
4483
  return;
4382
4484
  }
4383
- const off = (chunkX62H3RBG_cjs.rand(seed * 7919 + pi * 104729 + s * 31 + 1) * 2 - 1) * p.amp;
4485
+ const off = (chunkLALEBV63_cjs.rand(seed * 7919 + pi * 104729 + s * 31 + 1) * 2 - 1) * p.amp;
4384
4486
  to[n] = typeof p.around === "number" ? p.around + off : `(${p.around}) ${off < 0 ? "-" : "+"} ${Math.abs(off)}`;
4385
4487
  });
4386
4488
  out.push({ at: at + s * stepLen, to, duration: stepLen, ease });
@@ -4414,20 +4516,25 @@ function animateText(text, style, options) {
4414
4516
  const pieces = splitText(text, style, { by, x, y, align, ...vertical ? { vertical } : {} });
4415
4517
  if (pieces.length === 0) return [];
4416
4518
  const lineHeight = measureText("M", style).height;
4417
- const inTween = options.in === false ? null : resolveTween(options.in ?? "rise", "in");
4418
- const outTween = options.out === void 0 || options.out === false ? null : resolveTween(options.out, "out");
4519
+ const given = (v) => v === false || v === "none" ? void 0 : v;
4520
+ const inSpec = options.in === void 0 ? "rise" : given(options.in), outSpec = given(options.out);
4521
+ const inTween = inSpec === void 0 ? null : resolveTween(inSpec, "in");
4522
+ const outTween = outSpec === void 0 ? null : resolveTween(outSpec, "out");
4419
4523
  const delays = stagger(pieces.length, options.stagger ?? { each: 0.04 });
4420
4524
  const wave = Math.max(...delays);
4421
- const need = (inTween?.duration ?? 0) + (outTween?.duration ?? 0) + wave;
4525
+ const outDelays = outTween?.stagger ? stagger(pieces.length, outTween.stagger) : delays;
4526
+ const outWave = Math.max(...outDelays);
4527
+ const lead = Math.max(...delays.map((d, i) => d - outDelays[i]));
4528
+ const need = (inTween?.duration ?? 0) + (outTween ? outTween.duration + outWave + lead : wave);
4422
4529
  if (duration < need - 1e-9) {
4423
4530
  throw new Error(`animateText(): duration ${duration} is too short for this wave: the entrance${outTween ? ", the exit" : ""} and the stagger need at least ${+need.toFixed(3)} s`);
4424
4531
  }
4425
4532
  const end = at + duration;
4426
4533
  if (outTween?.at !== void 0) {
4427
4534
  if (outTween.at < at - 1e-9) console.warn(`pixi-effects: animateText(): out.at ${outTween.at} is before the layer starts (at ${at}): the exit starts with the piece; use out.at \u2265 ${at}`);
4428
- const latest = end - outTween.duration - wave;
4429
- if (outTween.at > latest + 1e-9) console.warn(`pixi-effects: animateText(): out.at ${outTween.at} leaves after the layer ends (the exit and the wave take ${+(outTween.duration + wave).toFixed(3)} s and the layer ends at ${+end.toFixed(3)} s): use out.at \u2264 ${+latest.toFixed(3)}, or a longer duration`);
4430
- if (inTween && outTween.at - at < inTween.duration - 1e-9) console.warn(`pixi-effects: animateText(): out.at ${outTween.at} is before the entrance has finished (it takes ${inTween.duration} s from at ${at}): use out.at \u2265 ${+(at + inTween.duration).toFixed(3)}`);
4535
+ const latest = end - outTween.duration - outWave;
4536
+ if (outTween.at > latest + 1e-9) console.warn(`pixi-effects: animateText(): out.at ${outTween.at} leaves after the layer ends (the exit and the wave take ${+(outTween.duration + outWave).toFixed(3)} s and the layer ends at ${+end.toFixed(3)} s): use out.at \u2264 ${+latest.toFixed(3)}, or a longer duration`);
4537
+ if (inTween && outTween.at - at < inTween.duration + lead - 1e-9) console.warn(`pixi-effects: animateText(): out.at ${outTween.at} is before the entrance has finished (it takes ${inTween.duration} s from at ${at}): use out.at \u2265 ${+(at + inTween.duration + lead).toFixed(3)}`);
4431
4538
  }
4432
4539
  return pieces.map((p, i) => {
4433
4540
  const cx = p.cx ?? p.x + p.width / 2, cy = p.cy ?? p.y + lineHeight / 2;
@@ -4447,8 +4554,8 @@ function animateText(text, style, options) {
4447
4554
  }
4448
4555
  let idleEnd = len;
4449
4556
  if (outTween) {
4450
- const begin = outTween.at !== void 0 ? void 0 : outTween.duration + (wave - delays[i]);
4451
- const local = outTween.at !== void 0 ? Math.max(0, outTween.at - at) : -begin;
4557
+ 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;
4452
4559
  idleEnd = outTween.at !== void 0 ? local : len - begin;
4453
4560
  keyframes.push({
4454
4561
  at: local,
@@ -4484,6 +4591,10 @@ function resolveTween(spec, which) {
4484
4591
  if (typeof spec === "object" && spec) {
4485
4592
  for (const key of Object.keys(spec)) {
4486
4593
  if (TWEEN_KEYS.includes(key)) continue;
4594
+ if (key === "stagger") {
4595
+ if (which === "in") console.warn("pixi-effects: animateText(): in.stagger is ignored: the entrance wave is the options' `stagger` (only `out` can have a wave of its own: out: { stagger: 0 } leaves all at once)");
4596
+ continue;
4597
+ }
4487
4598
  if (which === "out" && key === "at") {
4488
4599
  if (typeof spec.at !== "number" || !Number.isFinite(spec.at)) console.warn(`pixi-effects: animateText(): out.at must be a number of seconds (the composition second the exit starts at), got ${JSON.stringify(spec.at)}; ignored`);
4489
4600
  continue;
@@ -4495,7 +4606,7 @@ function resolveTween(spec, which) {
4495
4606
  }
4496
4607
  if (t.preset !== void 0 && !(t.preset in PRESETS)) {
4497
4608
  const guess = chunkTWZNOUM7_cjs.suggestName(String(t.preset), PRESET_NAMES);
4498
- throw new Error(`animateText(): ${which} "${t.preset}" is not a preset${guess ? `; did you mean "${guess}"?` : ""} (presets: ${PRESET_NAMES.map((n) => `"${n}"`).join(", ")})`);
4609
+ throw new Error(`animateText(): ${which} "${t.preset}" is not a preset${guess ? `; did you mean "${guess}"?` : ""} (presets: ${PRESET_NAMES.map((n) => `"${n}"`).join(", ")}; for no ${which === "out" ? "exit: omit `out` or write out: false (the pieces stay until the layer ends)" : "entrance: in: false"})`);
4499
4610
  }
4500
4611
  const base = t.preset ? PRESETS[t.preset] : void 0;
4501
4612
  const from = { ...base?.from ?? {}, ...t.from ?? {} };
@@ -4504,11 +4615,14 @@ function resolveTween(spec, which) {
4504
4615
  for (const k of Object.keys(from)) {
4505
4616
  if (!known.has(k) && to[k] === void 0) throw new Error(`animateText(): ${which}.from has "${k}", which has no resting value; give ${which}.to: { ${k}: \u2026 }`);
4506
4617
  }
4507
- if (Object.keys(from).length === 0) throw new Error(`animateText(): ${which} needs a preset or a from`);
4618
+ if (Object.keys(from).length === 0) {
4619
+ throw new Error(`animateText(): ${which} needs a preset or a from${which === "out" && Object.keys(to).length ? ` \u2014 ${which} takes \`from\`: the state the piece leaves to (out: { from: { y: 18, alpha: 0 } }); \`to\` is only the resting value of a property that has none` : ""}`);
4620
+ }
4508
4621
  let ease = t.ease ?? base?.ease ?? "power2.out";
4509
4622
  if (which === "out" && t.ease === void 0) ease = ease.replace(".out", ".in");
4510
4623
  const at = which === "out" && typeof t.at === "number" && Number.isFinite(t.at) ? t.at : void 0;
4511
- return { from, to: { ...to }, duration: t.duration ?? base?.duration ?? 0.5, ease, ...at !== void 0 ? { at } : {} };
4624
+ const wave = which === "out" && t.stagger !== void 0 ? t.stagger === 0 ? { each: 0 } : t.stagger : void 0;
4625
+ return { from, to: { ...to }, duration: t.duration ?? base?.duration ?? 0.5, ease, ...at !== void 0 ? { at } : {}, ...wave ? { stagger: wave } : {} };
4512
4626
  }
4513
4627
 
4514
4628
  // src/presets/react.ts
@@ -4673,7 +4787,7 @@ function computeEnvelope(audio, options = {}) {
4673
4787
  re[i] = k >= 0 && k < length ? mono[k] * win[i] : 0;
4674
4788
  im[i] = 0;
4675
4789
  }
4676
- chunkO66JGPVV_cjs.fft(re, im);
4790
+ chunk23SXKSUI_cjs.fft(re, im);
4677
4791
  for (const n of names) {
4678
4792
  const [lo, hi] = bands[n];
4679
4793
  let p = 0;
@@ -4839,5 +4953,5 @@ exports.splitText = splitText;
4839
4953
  exports.stagger = stagger;
4840
4954
  exports.wiggle = wiggle;
4841
4955
  exports.withFade = withFade;
4842
- //# sourceMappingURL=chunk-3IO5RWE4.cjs.map
4843
- //# sourceMappingURL=chunk-3IO5RWE4.cjs.map
4956
+ //# sourceMappingURL=chunk-I6VUQ2CR.cjs.map
4957
+ //# sourceMappingURL=chunk-I6VUQ2CR.cjs.map