pixi-effects 0.22.0 → 0.26.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 (92) hide show
  1. package/CHANGELOG.md +37 -0
  2. package/README.md +17 -15
  3. package/ai/CHAT.md +2 -2
  4. package/ai/chat-template.html +3 -3
  5. package/ai/tools/check.mjs +10 -3
  6. package/ai/tools/pixi-effects.mjs +48 -0
  7. package/ai/tools/render.mjs +3 -3
  8. package/ai/tools/view.mjs +3 -3
  9. package/dist/{Base-gu-5X0Cv.d.ts → Base-BgnRXbtJ.d.ts} +1 -1
  10. package/dist/{Base-BzlPgd-D.d.cts → Base-DHVKqA46.d.cts} +1 -1
  11. package/dist/Composition-2GJSCHPP.cjs +16 -0
  12. package/dist/{Composition-BK5SH6J7.cjs.map → Composition-2GJSCHPP.cjs.map} +1 -1
  13. package/dist/Composition-C4FSWJZF.js +7 -0
  14. package/dist/{Composition-Q5TVG3HM.js.map → Composition-C4FSWJZF.js.map} +1 -1
  15. package/dist/Controller.cjs +2 -2
  16. package/dist/Controller.d.cts +2 -2
  17. package/dist/Controller.d.ts +2 -2
  18. package/dist/Controller.js +1 -1
  19. package/dist/{Movie--_TJvdJa.d.ts → Movie-DP5p1r4E.d.ts} +5 -1
  20. package/dist/{Movie-DMxOoTdW.d.cts → Movie-DXReF4-W.d.cts} +5 -1
  21. package/dist/Presenter.cjs +2 -2
  22. package/dist/Presenter.d.cts +2 -2
  23. package/dist/Presenter.d.ts +2 -2
  24. package/dist/Presenter.js +1 -1
  25. package/dist/{blendModes-HCP5XTNP.js → blendModes-G3NOAU3T.js} +5 -5
  26. package/dist/{blendModes-HCP5XTNP.js.map → blendModes-G3NOAU3T.js.map} +1 -1
  27. package/dist/{blendModes-5E4G2ZZQ.cjs → blendModes-YV43K6E7.cjs} +6 -6
  28. package/dist/{blendModes-5E4G2ZZQ.cjs.map → blendModes-YV43K6E7.cjs.map} +1 -1
  29. package/dist/{chunk-K4JVI5HK.cjs → chunk-3DLYAPK7.cjs} +7 -9
  30. package/dist/{chunk-K4JVI5HK.cjs.map → chunk-3DLYAPK7.cjs.map} +1 -1
  31. package/dist/{chunk-YEPDQ7I3.js → chunk-AXZUKNXF.js} +6 -6
  32. package/dist/{chunk-YEPDQ7I3.js.map → chunk-AXZUKNXF.js.map} +1 -1
  33. package/dist/{chunk-NHICCDTC.js → chunk-BXOCEZ4U.js} +12 -3
  34. package/dist/chunk-BXOCEZ4U.js.map +1 -0
  35. package/dist/{chunk-IFZOI3JQ.cjs → chunk-DP37TTMO.cjs} +842 -40
  36. package/dist/chunk-DP37TTMO.cjs.map +1 -0
  37. package/dist/{chunk-BPTW7L25.cjs → chunk-ECA2WNRX.cjs} +23 -9
  38. package/dist/chunk-ECA2WNRX.cjs.map +1 -0
  39. package/dist/{chunk-DC4JPY6A.js → chunk-PRZ4U6JN.js} +814 -22
  40. package/dist/chunk-PRZ4U6JN.js.map +1 -0
  41. package/dist/chunk-SY27HZC5.cjs +1727 -0
  42. package/dist/chunk-SY27HZC5.cjs.map +1 -0
  43. package/dist/{chunk-E7RONUZ7.js → chunk-TLYTYBWX.js} +20 -6
  44. package/dist/chunk-TLYTYBWX.js.map +1 -0
  45. package/dist/{chunk-UZTKEYFP.cjs → chunk-TWZNOUM7.cjs} +12 -3
  46. package/dist/chunk-TWZNOUM7.cjs.map +1 -0
  47. package/dist/chunk-XS6GSJCN.js +1724 -0
  48. package/dist/chunk-XS6GSJCN.js.map +1 -0
  49. package/dist/index.cjs +234 -77
  50. package/dist/index.cjs.map +1 -1
  51. package/dist/index.d.cts +5 -5
  52. package/dist/index.d.ts +5 -5
  53. package/dist/index.js +173 -16
  54. package/dist/index.js.map +1 -1
  55. package/dist/loader.css +1 -1
  56. package/dist/{music-FUN7N225.js → music-AF4VTFAF.js} +3 -3
  57. package/dist/{music-FUN7N225.js.map → music-AF4VTFAF.js.map} +1 -1
  58. package/dist/{music-GEAH2XCT.cjs → music-KB7YZLHP.cjs} +7 -7
  59. package/dist/{music-GEAH2XCT.cjs.map → music-KB7YZLHP.cjs.map} +1 -1
  60. package/dist/{sfx-2DYTYNYW.cjs → sfx-2O6UQEUP.cjs} +8 -8
  61. package/dist/{sfx-2DYTYNYW.cjs.map → sfx-2O6UQEUP.cjs.map} +1 -1
  62. package/dist/{sfx-3IDAJ3UZ.js → sfx-VUKARHDX.js} +3 -3
  63. package/dist/{sfx-3IDAJ3UZ.js.map → sfx-VUKARHDX.js.map} +1 -1
  64. package/dist/three.cjs +7 -7
  65. package/dist/three.d.cts +2 -2
  66. package/dist/three.d.ts +2 -2
  67. package/dist/three.js +3 -3
  68. package/dist/{types-DBbZVKGS.d.cts → types-CoT0ypcx.d.cts} +60 -4
  69. package/dist/{types-DBbZVKGS.d.ts → types-CoT0ypcx.d.ts} +60 -4
  70. package/docs/api.md +2 -2
  71. package/docs/dsl.md +140 -7
  72. package/llms-full.txt +328 -39
  73. package/llms.txt +6 -5
  74. package/package.json +3 -3
  75. package/{ai → skills/pixi-effects}/SKILL.md +16 -12
  76. package/{ai → skills/pixi-effects}/reference/cheatsheet.md +10 -7
  77. package/{ai → skills/pixi-effects}/reference/pitfalls.md +22 -4
  78. package/{ai → skills/pixi-effects}/reference/recipes.md +133 -2
  79. package/{ai/tools → skills/pixi-effects/scripts}/save-image.py +1 -1
  80. package/{ai → skills/pixi-effects}/template.html +6 -6
  81. package/dist/Composition-BK5SH6J7.cjs +0 -16
  82. package/dist/Composition-Q5TVG3HM.js +0 -7
  83. package/dist/chunk-BPTW7L25.cjs.map +0 -1
  84. package/dist/chunk-DC4JPY6A.js.map +0 -1
  85. package/dist/chunk-E7RONUZ7.js.map +0 -1
  86. package/dist/chunk-IFZOI3JQ.cjs.map +0 -1
  87. package/dist/chunk-MHOO2CVU.cjs +0 -840
  88. package/dist/chunk-MHOO2CVU.cjs.map +0 -1
  89. package/dist/chunk-NHICCDTC.js.map +0 -1
  90. package/dist/chunk-UQDEDYVM.js +0 -838
  91. package/dist/chunk-UQDEDYVM.js.map +0 -1
  92. package/dist/chunk-UZTKEYFP.cjs.map +0 -1
package/dist/index.cjs CHANGED
@@ -1,10 +1,10 @@
1
1
  'use strict';
2
2
 
3
- var chunkMHOO2CVU_cjs = require('./chunk-MHOO2CVU.cjs');
4
- var chunkIFZOI3JQ_cjs = require('./chunk-IFZOI3JQ.cjs');
5
- var chunkK4JVI5HK_cjs = require('./chunk-K4JVI5HK.cjs');
6
- var chunkBPTW7L25_cjs = require('./chunk-BPTW7L25.cjs');
7
- var chunkUZTKEYFP_cjs = require('./chunk-UZTKEYFP.cjs');
3
+ var chunkSY27HZC5_cjs = require('./chunk-SY27HZC5.cjs');
4
+ var chunkDP37TTMO_cjs = require('./chunk-DP37TTMO.cjs');
5
+ var chunk3DLYAPK7_cjs = require('./chunk-3DLYAPK7.cjs');
6
+ var chunkECA2WNRX_cjs = require('./chunk-ECA2WNRX.cjs');
7
+ var chunkTWZNOUM7_cjs = require('./chunk-TWZNOUM7.cjs');
8
8
  var PIXI = require('pixi.js');
9
9
  var gsap = require('gsap');
10
10
  var PixiPlugin = require('gsap/PixiPlugin');
@@ -717,7 +717,7 @@ var DEFAULTS = { samples: 8, shutter: 0.5 };
717
717
  function resolveMotionBlur(spec, where) {
718
718
  if (spec === void 0 || spec === null || spec === false) return null;
719
719
  const o = spec === true ? {} : typeof spec === "number" ? { samples: spec } : spec;
720
- if (typeof spec === "object") chunkUZTKEYFP_cjs.warnUnknownOptions(`${where} motionBlur`, o, ["samples", "shutter"]);
720
+ if (typeof spec === "object") chunkTWZNOUM7_cjs.warnUnknownOptions(`${where} motionBlur`, o, ["samples", "shutter"]);
721
721
  const samples = o.samples ?? DEFAULTS.samples, shutter = o.shutter ?? DEFAULTS.shutter;
722
722
  if (!(Number.isInteger(samples) && samples >= 2 && samples <= 64)) throw new Error(`pixi-effects: ${where}: motionBlur samples must be a whole number from 2 to 64, got ${samples}`);
723
723
  if (!(Number.isFinite(shutter) && shutter > 0 && shutter <= 1)) throw new Error(`pixi-effects: ${where}: motionBlur shutter must be a fraction of the frame time above 0 and up to 1 (0.5 is a 180\xB0 shutter), got ${shutter}`);
@@ -919,10 +919,10 @@ var MODE_CODES = {
919
919
  var GL_FRAGMENT = `
920
920
  in vec2 vTextureCoord;
921
921
  uniform sampler2D uTexture;
922
- uniform vec4 uInputSize; // PIXI-bound: xy = sprite render texture size (with padding)
923
- uniform vec4 uInputClamp; // PIXI-bound: xy = min uv of unpadded content, zw = max uv
924
- uniform vec4 uOutputFrame; // PIXI-bound: xy = sprite top-left on output, zw = bbox size in px
925
- uniform vec4 uGlobalFrame; // PIXI-bound: xy = global frame offset, zw = canvas size in px
922
+ uniform highp vec4 uInputSize; // PIXI-bound: xy = sprite render texture size (with padding)
923
+ uniform highp vec4 uInputClamp; // PIXI-bound: xy = min uv of unpadded content, zw = max uv
924
+ uniform highp vec4 uOutputFrame; // PIXI-bound: xy = sprite top-left on output, zw = bbox size in px
925
+ uniform highp vec4 uGlobalFrame; // PIXI-bound: xy = global frame offset, zw = canvas size in px
926
926
  uniform float uProgress;
927
927
  uniform float uSmoothing;
928
928
  uniform float uMode;
@@ -1240,6 +1240,133 @@ var TransitionMaskFilter = class extends PIXI.Filter {
1240
1240
  this.resources.transitionUniforms.uniforms.uSeed = v;
1241
1241
  }
1242
1242
  };
1243
+ var LUMA_MAPS = ["linear", "diagonal", "radial"];
1244
+ var GL = `in vec2 vTextureCoord;
1245
+ uniform sampler2D uTexture;
1246
+ uniform sampler2D uMap;
1247
+ uniform highp vec4 uInputClamp;
1248
+ uniform highp vec4 uOutputFrame;
1249
+ uniform highp vec4 uGlobalFrame;
1250
+ uniform float uProgress;
1251
+ uniform float uSoftness;
1252
+ uniform float uInvert;
1253
+ uniform float uFlip;
1254
+ uniform float uMode;
1255
+ out vec4 finalColor;
1256
+ float lumaOf(vec2 cuv, float aspect) {
1257
+ if (uMode > 2.5) { vec2 p = (cuv - 0.5) * vec2(aspect, 1.0); return clamp(length(p) / (0.5 * length(vec2(aspect, 1.0))), 0.0, 1.0); }
1258
+ if (uMode > 1.5) return clamp((cuv.x + cuv.y) * 0.5, 0.0, 1.0);
1259
+ if (uMode > 0.5) return clamp(cuv.x, 0.0, 1.0);
1260
+ return dot(texture(uMap, cuv).rgb, vec3(0.2126, 0.7152, 0.0722));
1261
+ }
1262
+ void main(void) {
1263
+ vec4 raw = texture(uTexture, vTextureCoord);
1264
+ vec2 bboxUV = (vTextureCoord - uInputClamp.xy) / (uInputClamp.zw - uInputClamp.xy);
1265
+ vec2 cuv = (uOutputFrame.xy + bboxUV * uOutputFrame.zw) / uGlobalFrame.zw;
1266
+ float l = lumaOf(cuv, uGlobalFrame.z / uGlobalFrame.w);
1267
+ if (uFlip > 0.5) l = 1.0 - l;
1268
+ float s = max(uSoftness, 0.0001);
1269
+ float ep = uProgress * (1.0 + 2.0 * s) - s;
1270
+ float reveal = 1.0 - smoothstep(ep - s, ep + s, l);
1271
+ if (uInvert > 0.5) reveal = 1.0 - step(0.99, reveal);
1272
+ finalColor = raw * reveal;
1273
+ }`;
1274
+ var WGSL = `
1275
+ struct GlobalFilterUniforms {
1276
+ uInputSize:vec4<f32>, uInputPixel:vec4<f32>, uInputClamp:vec4<f32>, uOutputFrame:vec4<f32>, uGlobalFrame:vec4<f32>, uOutputTexture:vec4<f32>,
1277
+ };
1278
+ struct LumaUniforms { uProgress:f32, uSoftness:f32, uInvert:f32, uFlip:f32, uMode:f32, };
1279
+ @group(0) @binding(0) var<uniform> gfu: GlobalFilterUniforms;
1280
+ @group(0) @binding(1) var uTexture: texture_2d<f32>;
1281
+ @group(0) @binding(2) var uSampler : sampler;
1282
+ @group(1) @binding(0) var<uniform> lumaUniforms : LumaUniforms;
1283
+ @group(1) @binding(1) var uMap: texture_2d<f32>;
1284
+ @group(1) @binding(2) var uMapSampler : sampler;
1285
+ struct VSOutput {
1286
+ @builtin(position) position: vec4<f32>,
1287
+ @location(0) uv : vec2<f32>,
1288
+ };
1289
+ @vertex
1290
+ fn mainVertex(
1291
+ @location(0) aPosition : vec2<f32>,
1292
+ ) -> VSOutput {
1293
+ var position = aPosition * gfu.uOutputFrame.zw + gfu.uOutputFrame.xy;
1294
+ position.x = position.x * (2.0 / gfu.uOutputTexture.x) - 1.0;
1295
+ position.y = position.y * (2.0 * gfu.uOutputTexture.z / gfu.uOutputTexture.y) - gfu.uOutputTexture.z;
1296
+ return VSOutput(vec4<f32>(position, 0.0, 1.0), aPosition * (gfu.uOutputFrame.zw * gfu.uInputSize.zw));
1297
+ }
1298
+ fn lumaOf(cuv: vec2<f32>, aspect: f32) -> f32 {
1299
+ let mode = lumaUniforms.uMode;
1300
+ if (mode > 2.5) {
1301
+ let p = (cuv - vec2<f32>(0.5)) * vec2<f32>(aspect, 1.0);
1302
+ return clamp(length(p) / (0.5 * length(vec2<f32>(aspect, 1.0))), 0.0, 1.0);
1303
+ }
1304
+ if (mode > 1.5) { return clamp((cuv.x + cuv.y) * 0.5, 0.0, 1.0); }
1305
+ if (mode > 0.5) { return clamp(cuv.x, 0.0, 1.0); }
1306
+ let m = textureSampleLevel(uMap, uMapSampler, cuv, 0.0).rgb;
1307
+ return dot(m, vec3<f32>(0.2126, 0.7152, 0.0722));
1308
+ }
1309
+ @fragment
1310
+ fn mainFragment(@location(0) uv : vec2<f32>) -> @location(0) vec4<f32> {
1311
+ let raw = textureSample(uTexture, uSampler, uv);
1312
+ let bboxUV = (uv - gfu.uInputClamp.xy) / (gfu.uInputClamp.zw - gfu.uInputClamp.xy);
1313
+ let cuv = (gfu.uOutputFrame.xy + bboxUV * gfu.uOutputFrame.zw) / gfu.uGlobalFrame.zw;
1314
+ var l = lumaOf(cuv, gfu.uGlobalFrame.z / gfu.uGlobalFrame.w);
1315
+ if (lumaUniforms.uFlip > 0.5) { l = 1.0 - l; }
1316
+ let s = max(lumaUniforms.uSoftness, 0.0001);
1317
+ let ep = lumaUniforms.uProgress * (1.0 + 2.0 * s) - s;
1318
+ var reveal = 1.0 - smoothstep(ep - s, ep + s, l);
1319
+ if (lumaUniforms.uInvert > 0.5) { reveal = 1.0 - step(0.99, reveal); }
1320
+ return raw * reveal;
1321
+ }`;
1322
+ var LumaWipeFilter = class extends PIXI.Filter {
1323
+ _mapName;
1324
+ _builtIn;
1325
+ _resolved = false;
1326
+ _warned = false;
1327
+ constructor({ map, softness = 0.1, progress = 0, invert = false, flip = false }) {
1328
+ const t = PIXI.Texture.WHITE;
1329
+ const mode = LUMA_MAPS.indexOf(map) + 1;
1330
+ super({
1331
+ glProgram: PIXI.GlProgram.from({ vertex: PIXI.defaultFilterVert, fragment: GL, name: "pe-luma-wipe" }),
1332
+ gpuProgram: PIXI.GpuProgram.from({ vertex: { source: WGSL, entryPoint: "mainVertex" }, fragment: { source: WGSL, entryPoint: "mainFragment" } }),
1333
+ resources: {
1334
+ lumaUniforms: new PIXI.UniformGroup({
1335
+ uProgress: { value: progress, type: "f32" },
1336
+ uSoftness: { value: softness, type: "f32" },
1337
+ uInvert: { value: invert ? 1 : 0, type: "f32" },
1338
+ uFlip: { value: flip ? 1 : 0, type: "f32" },
1339
+ uMode: { value: mode, type: "f32" }
1340
+ }),
1341
+ uMap: t.source,
1342
+ uMapSampler: t.source.style
1343
+ }
1344
+ });
1345
+ this._mapName = map;
1346
+ this._builtIn = mode > 0;
1347
+ }
1348
+ get uProgress() {
1349
+ return this.resources.lumaUniforms.uniforms.uProgress;
1350
+ }
1351
+ set uProgress(v) {
1352
+ this.resources.lumaUniforms.uniforms.uProgress = v;
1353
+ }
1354
+ apply(fm, input, output, clear) {
1355
+ if (!this._builtIn && !this._resolved) {
1356
+ const t = PIXI.Assets.get(this._mapName);
1357
+ if (t) {
1358
+ this.resources.uMap = t.source;
1359
+ this.resources.uMapSampler = t.source.style;
1360
+ this._resolved = true;
1361
+ } else if (!this._warned) {
1362
+ this._warned = true;
1363
+ const guess = chunkTWZNOUM7_cjs.suggestName(this._mapName, LUMA_MAPS);
1364
+ console.warn(`pixi-effects: luma transition map "${this._mapName}" is not a built-in map (${LUMA_MAPS.join(", ")}) and no image asset has that name${guess ? `; did you mean "${guess}"?` : ""}; the wipe follows a white map (everything changes at once)`);
1365
+ }
1366
+ }
1367
+ fm.applyFilter(this, input, output, clear);
1368
+ }
1369
+ };
1243
1370
 
1244
1371
  // src/core/Transitions.ts
1245
1372
  var EPS = 1e-6;
@@ -1280,8 +1407,8 @@ function expandTransitions(spec) {
1280
1407
  }
1281
1408
  const transitions = out.transitions;
1282
1409
  const sequences = out.sequences ?? [];
1283
- const remap = out.duration !== void 0 ? chunkIFZOI3JQ_cjs.timeRemapOf(out) : null;
1284
- const parentDuration = remap ? chunkIFZOI3JQ_cjs.contentLength(remap, out.duration) : out.duration ?? Infinity;
1410
+ const remap = out.duration !== void 0 ? chunkDP37TTMO_cjs.timeRemapOf(out) : null;
1411
+ const parentDuration = remap ? chunkDP37TTMO_cjs.contentLength(remap, out.duration) : out.duration ?? Infinity;
1285
1412
  const compW = out.width;
1286
1413
  const compH = out.height;
1287
1414
  const threeDByName = new Set(sequences.filter((s) => s.name && s.threeD).map((s) => s.name));
@@ -1294,7 +1421,7 @@ function expandTransitions(spec) {
1294
1421
  });
1295
1422
  const masksParticipants = /* @__PURE__ */ new Set();
1296
1423
  for (const t of transitions) {
1297
- if (t.kind === "wipe" || t.kind === "iris" || t.kind === "dissolve" || t.kind === "zoom") {
1424
+ if (t.kind === "wipe" || t.kind === "iris" || t.kind === "dissolve" || t.kind === "zoom" || t.kind === "luma") {
1298
1425
  masksParticipants.add(t.from);
1299
1426
  masksParticipants.add(t.to);
1300
1427
  }
@@ -1349,16 +1476,16 @@ function expandTransitions(spec) {
1349
1476
  if (toUses > 1) {
1350
1477
  throw new Error(`pixi-effects: ${tag} sequence "${t.to}" is used as \`to\` in more than one transition`);
1351
1478
  }
1352
- const tStart = chunkIFZOI3JQ_cjs.resolveAt(t.at, parentDuration);
1479
+ const tStart = chunkDP37TTMO_cjs.resolveAt(t.at, parentDuration);
1353
1480
  const tEnd = tStart + t.duration;
1354
1481
  const fromSeq = fromEntry.seq;
1355
- const fromStart = chunkIFZOI3JQ_cjs.resolveAt(fromSeq.at, parentDuration);
1482
+ const fromStart = chunkDP37TTMO_cjs.resolveAt(fromSeq.at, parentDuration);
1356
1483
  const fromEnd = fromStart + (fromSeq.duration ?? parentDuration);
1357
1484
  if (tStart < fromStart - EPS || tEnd > fromEnd + EPS) {
1358
1485
  throw new Error(`pixi-effects: ${tag} window [${tStart}, ${tEnd}] is not covered by \`from\` "${t.from}" (lives [${fromStart}, ${fromEnd}], so it ends at ${fromEnd})`);
1359
1486
  }
1360
1487
  const toSeq = toEntry.seq;
1361
- const toStart = chunkIFZOI3JQ_cjs.resolveAt(toSeq.at, parentDuration);
1488
+ const toStart = chunkDP37TTMO_cjs.resolveAt(toSeq.at, parentDuration);
1362
1489
  const toEnd = toStart + (toSeq.duration ?? parentDuration);
1363
1490
  if (tStart < toStart - EPS || tEnd > toEnd + EPS) {
1364
1491
  throw new Error(`pixi-effects: ${tag} window [${tStart}, ${tEnd}] is not covered by \`to\` "${t.to}" (lives [${toStart}, ${toEnd}], so it starts at ${toStart})`);
@@ -1391,6 +1518,9 @@ function expandTransitions(spec) {
1391
1518
  case "dissolve":
1392
1519
  expandDissolve(out, abs, fromEntry.seq, toEntry.seq, i);
1393
1520
  break;
1521
+ case "luma":
1522
+ expandLuma(out, abs, fromEntry.seq, toEntry.seq, i);
1523
+ break;
1394
1524
  }
1395
1525
  rebaseNewKeyframes(fromEntry.seq, nFrom, fromStart);
1396
1526
  rebaseNewKeyframes(toEntry.seq, nTo, toStart);
@@ -1414,7 +1544,11 @@ function wrapAsFullComposition(seq, compW, compH) {
1414
1544
  delete inner.name;
1415
1545
  delete inner.at;
1416
1546
  delete inner.duration;
1547
+ const m = inner.mask;
1548
+ const carriesMatte = m !== void 0 && chunkSY27HZC5_cjs.maskSourceOf(m).kind === "refs";
1549
+ if (carriesMatte) delete inner.mask;
1417
1550
  return {
1551
+ ...carriesMatte ? { mask: m } : {},
1418
1552
  type: "composition",
1419
1553
  name: seq.name,
1420
1554
  at: seq.at,
@@ -1641,6 +1775,20 @@ function expandDissolve(comp, t, fromSeq, toSeq, transitionIndex) {
1641
1775
  });
1642
1776
  fromSeq.filterArea = fullArea;
1643
1777
  }
1778
+ function expandLuma(comp, t, fromSeq, toSeq, transitionIndex) {
1779
+ const ease = t.ease ?? "none";
1780
+ const inName = transitionFilterName(transitionIndex);
1781
+ const outName = `${transitionFilterName(transitionIndex)}-out`;
1782
+ const fullArea = { x: 0, y: 0, width: comp.width ?? 0, height: comp.height ?? 0 };
1783
+ const opts = { map: t.map ?? "linear", softness: t.softness ?? 0.1, flip: t.flip ?? false };
1784
+ for (const [seq, name, invert] of [[toSeq, inName, false], [fromSeq, outName, true]]) {
1785
+ const filters = ensureFilters(seq);
1786
+ rejectReservedNameCollision(filters, seq.name);
1787
+ filters.push({ type: "custom", name, filter: new LumaWipeFilter({ ...opts, invert }) });
1788
+ ensureKeyframes(seq).push({ at: t.at, to: { [`filters.${name}.uProgress`]: 1 }, duration: t.duration, ease });
1789
+ seq.filterArea = fullArea;
1790
+ }
1791
+ }
1644
1792
  var OWN_NAME_PATTERN = /^_pe-transition-\d+(-out)?$/;
1645
1793
  function rejectReservedNameCollision(filters, seqName) {
1646
1794
  for (const f of filters) {
@@ -1680,7 +1828,7 @@ function inspectScene(root, frame, time, canvas, opts = {}) {
1680
1828
  blending.add(w.to);
1681
1829
  }
1682
1830
  }
1683
- comp.layers().forEach(({ seq, display, threeD, carriers, depthBlur }, i) => {
1831
+ comp.layers().forEach(({ seq, display, threeD, carriers, depthBlur, light }, i) => {
1684
1832
  const t = seq.target;
1685
1833
  const name = seq.spec.name;
1686
1834
  const label = name ?? `${seq.spec.type}#${i}`;
@@ -1699,9 +1847,9 @@ function inspectScene(root, frame, time, canvas, opts = {}) {
1699
1847
  onCanvas = inter === 0 && area > 0 ? "none" : inter >= area - 1e-6 ? "full" : "partial";
1700
1848
  if (area === 0) onCanvas = "none";
1701
1849
  }
1702
- const keys = chunkBPTW7L25_cjs.collectPropKeys(seq.spec);
1850
+ const keys = chunkECA2WNRX_cjs.collectPropKeys(seq.spec);
1703
1851
  const carried = carriers.some((c) => {
1704
- const k = chunkBPTW7L25_cjs.collectPropKeys(c.spec);
1852
+ const k = chunkECA2WNRX_cjs.collectPropKeys(c.spec);
1705
1853
  return (c.spec.keyframes ?? []).length > 0 && (k.has("x") || k.has("y") || k.has("rotation") || k.has("scale"));
1706
1854
  });
1707
1855
  const moving = parentMoving || carried || (seq.spec.keyframes ?? []).length > 0 && (keys.has("x") || keys.has("y"));
@@ -1712,6 +1860,7 @@ function inspectScene(root, frame, time, canvas, opts = {}) {
1712
1860
  const zero = parentZero || zeroScale(t) || carriers.some((c) => zeroScale(c.target));
1713
1861
  const info = { path: prefix + label, name, type: seq.spec.type, threeD, visible, alpha, moving, bounds, onCanvas };
1714
1862
  if (threeD) info.depthBlur = Number(depthBlur.toFixed(2));
1863
+ if (threeD && light !== void 0) info.light = Number(light.toFixed(2));
1715
1864
  layers.push(info);
1716
1865
  let shown = bounds ? clip(bounds, view) : null;
1717
1866
  const maskTarget = seq.maskSequence?.target;
@@ -1723,7 +1872,7 @@ function inspectScene(root, frame, time, canvas, opts = {}) {
1723
1872
  const group = parentScene ?? (name !== void 0 && blending.has(name) ? name : void 0);
1724
1873
  if (group) scene.set(info, group);
1725
1874
  if (zero || seq.showsNothingYet) scaledToNothing.add(info);
1726
- if (seq instanceof chunkMHOO2CVU_cjs.CompositionSequence) walk2(seq, prefix + label + "/", insideThreeD || threeD, visible, group, moving, zero);
1875
+ if (seq instanceof chunkSY27HZC5_cjs.CompositionSequence) walk2(seq, prefix + label + "/", insideThreeD || threeD, visible, group, moving, zero);
1727
1876
  });
1728
1877
  };
1729
1878
  walk2(root, "", false, true);
@@ -1784,10 +1933,10 @@ function walk(comp, parentStart, parentEnd, depth, prefix, rows, transitions, lo
1784
1933
  const start = parentStart + (seq.at ?? 0);
1785
1934
  const end = Math.min(parentEnd, seq.duration !== void 0 ? start + seq.duration : parentEnd);
1786
1935
  const span = end - start;
1787
- const keys = (seq.spec.keyframes ?? []).map((kf) => round(start + chunkIFZOI3JQ_cjs.resolveAt(kf.at, span))).sort((a, b) => a - b).slice(0, MAX_KEYS);
1936
+ const keys = (seq.spec.keyframes ?? []).map((kf) => round(start + chunkDP37TTMO_cjs.resolveAt(kf.at, span))).sort((a, b) => a - b).slice(0, MAX_KEYS);
1788
1937
  const row = { path: prefix + name, name, type: seq.spec.type, depth, start: round(start), end: round(end), keys, detail: detailOf(seq), ...local ? { local } : {} };
1789
1938
  const inner = [];
1790
- const isComp = seq instanceof chunkMHOO2CVU_cjs.CompositionSequence;
1939
+ const isComp = seq instanceof chunkSY27HZC5_cjs.CompositionSequence;
1791
1940
  if (isComp) {
1792
1941
  const label = seq.remapLabel();
1793
1942
  walk(seq, label ? 0 : start, label ? seq.contentSpan : end, depth + 1, prefix + name + "/", inner, transitions, label ? `${name} ${label}` : local);
@@ -1850,6 +1999,7 @@ var COLORS = {
1850
1999
  composition: "#8ea4c8",
1851
2000
  three: "#ff9f68",
1852
2001
  camera: "#b0b0b0",
2002
+ light: "#b0b0b0",
1853
2003
  null: "#b0b0b0"
1854
2004
  };
1855
2005
  var esc = (s) => s.replace(/&/g, "&amp;").replace(/</g, "&lt;").replace(/>/g, "&gt;").replace(/"/g, "&quot;");
@@ -1970,6 +2120,9 @@ function startWhenRunning(ctx, start, gesture) {
1970
2120
  return stop;
1971
2121
  }
1972
2122
  var GESTURES = ["pointerdown", "mousedown", "touchend", "keydown"];
2123
+ function audioIsBlocked(ctx, hasAudio) {
2124
+ return !!ctx && hasAudio && ctx.state !== "running";
2125
+ }
1973
2126
 
1974
2127
  // src/core/poster.ts
1975
2128
  function normalizePoster(poster, duration, frameRate) {
@@ -2070,7 +2223,7 @@ function normalizeStops(raw, duration, frameRate) {
2070
2223
  console.warn(`pixi-effects: stops[${i}] ${JSON.stringify(r)} is not a time in seconds (or { at, page?, notes?, advance?, pdf? }); ignored`);
2071
2224
  return;
2072
2225
  }
2073
- if (typeof r === "object") chunkUZTKEYFP_cjs.warnUnknownOptions(`stops[${i}]`, r, KEYS);
2226
+ if (typeof r === "object") chunkTWZNOUM7_cjs.warnUnknownOptions(`stops[${i}]`, r, KEYS);
2074
2227
  const at = spec.at < 0 ? duration + spec.at : spec.at;
2075
2228
  if (at < -1e-9 || at > duration + 1e-9) {
2076
2229
  console.warn(`pixi-effects: stops[${i}] at ${spec.at} s is outside the movie (0 to ${duration} s); ignored`);
@@ -2262,6 +2415,10 @@ var Movie = class {
2262
2415
  get isReady() {
2263
2416
  return this._initState === "ready";
2264
2417
  }
2418
+ /** True while the movie has sound but the browser keeps it silent until the viewer taps (iOS Safari, a page nobody has touched): a player can show "tap for sound"; the sound starts at a tap or key press that arrives while play() is pending, or at the next play() made from a tap. */
2419
+ get audioBlocked() {
2420
+ return audioIsBlocked(this._audioContext, !!this.audioBuffer);
2421
+ }
2265
2422
  /** The poster time as a frame number, or null. */
2266
2423
  get posterFrame() {
2267
2424
  return this._posterFrame;
@@ -2300,7 +2457,7 @@ var Movie = class {
2300
2457
  return this._audioContext;
2301
2458
  }
2302
2459
  async init(options = {}) {
2303
- chunkUZTKEYFP_cjs.warnUnknownOptions("movie.init()", options, ["width", "height", "duration", "frameRate", "background", "canvas", "assets", "composition", "poster", "motionBlur", "loader"]);
2460
+ chunkTWZNOUM7_cjs.warnUnknownOptions("movie.init()", options, ["width", "height", "duration", "frameRate", "background", "canvas", "assets", "composition", "poster", "motionBlur", "loader"]);
2304
2461
  this._initState = "pending";
2305
2462
  try {
2306
2463
  this.width = options.width ?? 1920;
@@ -2334,7 +2491,7 @@ var Movie = class {
2334
2491
  this.app = new PIXI.Application();
2335
2492
  await this.app.init({ ...baseAppOptions, preference: "webgl" });
2336
2493
  }
2337
- if (chunkK4JVI5HK_cjs.usesAdvancedBlend(options.composition)) await chunkK4JVI5HK_cjs.enableAdvancedBlend(this.app.renderer);
2494
+ if (chunk3DLYAPK7_cjs.usesAdvancedBlend(options.composition)) await chunk3DLYAPK7_cjs.enableAdvancedBlend(this.app.renderer);
2338
2495
  const root = new PIXI.Container();
2339
2496
  root.cullable = true;
2340
2497
  root.cullableChildren = true;
@@ -2343,14 +2500,14 @@ var Movie = class {
2343
2500
  this._rootContainer = root;
2344
2501
  const audioContext = this._ensureAudioContext();
2345
2502
  await loadAssetBundle(options.assets ?? [], audioContext);
2346
- const rootShape = { width: this.width, height: this.height, duration: this.duration };
2503
+ const rootShape = { width: this.width, height: this.height, duration: this.duration, frameRate: this.frameRate };
2347
2504
  const seededComposition = options.composition ? {
2348
2505
  ...options.composition,
2349
2506
  width: options.composition.width ?? this.width,
2350
2507
  height: options.composition.height ?? this.height,
2351
2508
  duration: options.composition.duration ?? this.duration
2352
2509
  } : void 0;
2353
- await chunkIFZOI3JQ_cjs.ensureFilterLibrary(seededComposition);
2510
+ await chunkDP37TTMO_cjs.ensureFilterLibrary(seededComposition);
2354
2511
  const userComposition = seededComposition ? expandTransitions(seededComposition) : void 0;
2355
2512
  const rootSeqSpec = {
2356
2513
  type: "composition",
@@ -2360,7 +2517,7 @@ var Movie = class {
2360
2517
  ...userComposition
2361
2518
  };
2362
2519
  if (userComposition) carryTransitionWindows(userComposition, rootSeqSpec);
2363
- const composition = new chunkMHOO2CVU_cjs.CompositionSequence(rootSeqSpec, null, rootShape);
2520
+ const composition = new chunkSY27HZC5_cjs.CompositionSequence(rootSeqSpec, null, rootShape);
2364
2521
  await composition.build();
2365
2522
  this._rootSequence = composition;
2366
2523
  if (composition.target) root.addChild(composition.target);
@@ -2444,7 +2601,7 @@ var Movie = class {
2444
2601
  }));
2445
2602
  }
2446
2603
  async snapshot(frame = this.currentFrame, opts = {}) {
2447
- chunkUZTKEYFP_cjs.warnUnknownOptions("movie.snapshot()", opts, ["scale", "type", "as", "motionBlur"]);
2604
+ chunkTWZNOUM7_cjs.warnUnknownOptions("movie.snapshot()", opts, ["scale", "type", "as", "motionBlur"]);
2448
2605
  this._requireReady("snapshot");
2449
2606
  return this._captureFrame(clampFrame(frame, this.totalFrames), { scale: opts.scale ?? 1, type: opts.type ?? "image/png", as: opts.as ?? "blob", motionBlur: opts.motionBlur });
2450
2607
  }
@@ -2473,7 +2630,7 @@ var Movie = class {
2473
2630
  return encodeCanvas(out, o.type, o.as, o.quality);
2474
2631
  }
2475
2632
  async stopImages(opts = {}) {
2476
- chunkUZTKEYFP_cjs.warnUnknownOptions("movie.stopImages()", opts, ["which", "pick", "scale", "type", "quality", "as", "motionBlur", "pdf", "onImage"]);
2633
+ chunkTWZNOUM7_cjs.warnUnknownOptions("movie.stopImages()", opts, ["which", "pick", "scale", "type", "quality", "as", "motionBlur", "pdf", "onImage"]);
2477
2634
  this._requireReady("stopImages");
2478
2635
  if (this.stops.length === 0) return [];
2479
2636
  const picks = pictureStops(this.stops, { which: opts.which, pick: opts.pick, pdf: opts.pdf });
@@ -2505,7 +2662,7 @@ var Movie = class {
2505
2662
  * animation. `issues` says which stop, which page and what to do; leaves the playhead where it was.
2506
2663
  */
2507
2664
  async inspectStops(opts = {}) {
2508
- chunkUZTKEYFP_cjs.warnUnknownOptions("movie.inspectStops()", opts, ["lookback", "tolerance"]);
2665
+ chunkTWZNOUM7_cjs.warnUnknownOptions("movie.inspectStops()", opts, ["lookback", "tolerance"]);
2509
2666
  this._requireReady("inspectStops");
2510
2667
  const out = { stops: [], issues: [] };
2511
2668
  if (this.stops.length === 0) return out;
@@ -2539,7 +2696,7 @@ var Movie = class {
2539
2696
  * JPEG at the canvas size. Needs `composition.stops`. A transparent background comes out black: give the movie a `background`.
2540
2697
  */
2541
2698
  async exportPDF(opts = {}) {
2542
- chunkUZTKEYFP_cjs.warnUnknownOptions("movie.exportPDF()", opts, ["which", "pick", "scale", "quality", "title", "motionBlur", "onImage"]);
2699
+ chunkTWZNOUM7_cjs.warnUnknownOptions("movie.exportPDF()", opts, ["which", "pick", "scale", "quality", "title", "motionBlur", "onImage"]);
2543
2700
  this._requireReady("exportPDF");
2544
2701
  if (this.stops.length === 0) throw new Error("pixi-effects: movie.exportPDF(): this movie has no stops, so there are no pages; add `stops` to the composition (or build it with deck())");
2545
2702
  const scale = opts.scale ?? 1;
@@ -2562,7 +2719,7 @@ var Movie = class {
2562
2719
  return new Blob([buildPdf(pages, { title: opts.title })], { type: "application/pdf" });
2563
2720
  }
2564
2721
  async onionSkin(opts = {}) {
2565
- chunkUZTKEYFP_cjs.warnUnknownOptions("movie.onionSkin()", opts, ["from", "to", "count", "scale", "as", "motionBlur"]);
2722
+ chunkTWZNOUM7_cjs.warnUnknownOptions("movie.onionSkin()", opts, ["from", "to", "count", "scale", "as", "motionBlur"]);
2566
2723
  this._requireReady("onionSkin");
2567
2724
  const from = opts.from ?? 0, to = opts.to ?? this.duration, count = opts.count ?? 8, scale = opts.scale ?? 1;
2568
2725
  if (!(Number.isInteger(count) && count >= 1 && count <= 64)) throw new Error(`pixi-effects: movie.onionSkin(): count must be a whole number from 1 to 64, got ${count}`);
@@ -2593,7 +2750,7 @@ var Movie = class {
2593
2750
  return encodeCanvas(out, "image/png", opts.as ?? "blob");
2594
2751
  }
2595
2752
  async contactSheet(opts = {}) {
2596
- chunkUZTKEYFP_cjs.warnUnknownOptions("movie.contactSheet()", opts, ["frames", "times", "count", "columns", "cellWidth", "as", "motionBlur"]);
2753
+ chunkTWZNOUM7_cjs.warnUnknownOptions("movie.contactSheet()", opts, ["frames", "times", "count", "columns", "cellWidth", "as", "motionBlur"]);
2597
2754
  this._requireReady("contactSheet");
2598
2755
  const mb = this._blurFor(opts.motionBlur, "movie.contactSheet()");
2599
2756
  const blurred = mb ? this._blurCanvas() : null;
@@ -2630,7 +2787,7 @@ var Movie = class {
2630
2787
  * the canvas, cut by an edge, empty, or overlapping other text. Seeks there and stays there.
2631
2788
  */
2632
2789
  async inspect(frame = this.currentFrame, opts = { layers: "visible" }) {
2633
- chunkUZTKEYFP_cjs.warnUnknownOptions("movie.inspect()", opts, ["layers"]);
2790
+ chunkTWZNOUM7_cjs.warnUnknownOptions("movie.inspect()", opts, ["layers"]);
2634
2791
  this._requireReady("inspect");
2635
2792
  const f = clampFrame(frame, this.totalFrames);
2636
2793
  await this.gotoFrame(f, true);
@@ -2646,12 +2803,12 @@ var Movie = class {
2646
2803
  }
2647
2804
  /** The timeline as one self-contained HTML page (an inline SVG, no scripts): layers as bars on a time axis, ◆ keyframes, transition bands. Open it in a browser. */
2648
2805
  timelineChart(opts = {}) {
2649
- chunkUZTKEYFP_cjs.warnUnknownOptions("movie.timelineChart()", opts, ["title"]);
2806
+ chunkTWZNOUM7_cjs.warnUnknownOptions("movie.timelineChart()", opts, ["title"]);
2650
2807
  return timelineHtml(this.timelineData(), opts);
2651
2808
  }
2652
2809
  /** Just the chart: one `<svg>` whose geometry is in `data-*` attributes (a viewer puts a playhead on it: `pixi-effects-view`). */
2653
2810
  timelineSvg(opts = {}) {
2654
- chunkUZTKEYFP_cjs.warnUnknownOptions("movie.timelineSvg()", opts, ["chartWidth", "labels"]);
2811
+ chunkTWZNOUM7_cjs.warnUnknownOptions("movie.timelineSvg()", opts, ["chartWidth", "labels"]);
2655
2812
  return timelineSvg(this.timelineData(), opts);
2656
2813
  }
2657
2814
  /**
@@ -2668,7 +2825,7 @@ var Movie = class {
2668
2825
  * scene's first and last frame and a frame every 0.25 s), fonts and the sound. See `ReviewReport`. Page warnings are in `window.__logs`. The movie is left as it was.
2669
2826
  */
2670
2827
  async review(opts = {}) {
2671
- chunkUZTKEYFP_cjs.warnUnknownOptions("movie.review()", opts, ["at", "strict"]);
2828
+ chunkTWZNOUM7_cjs.warnUnknownOptions("movie.review()", opts, ["at", "strict"]);
2672
2829
  this._requireReady("review");
2673
2830
  const back = this.currentFrame, wasAtPoster = this._atPoster;
2674
2831
  try {
@@ -2688,7 +2845,7 @@ var Movie = class {
2688
2845
  return inspectFonts(this._rootSequence);
2689
2846
  }
2690
2847
  inspectAudio(opts = {}) {
2691
- chunkUZTKEYFP_cjs.warnUnknownOptions("movie.inspectAudio()", opts, ["window", "scenes"]);
2848
+ chunkTWZNOUM7_cjs.warnUnknownOptions("movie.inspectAudio()", opts, ["window", "scenes"]);
2692
2849
  this._requireReady("inspectAudio");
2693
2850
  const scenes = opts.scenes ?? namedScenes(this.timelineData().rows);
2694
2851
  return analyzeAudio(this.audioBuffer, this._audioSources, this._mixStats, this.duration, { ...opts, scenes });
@@ -2966,7 +3123,7 @@ var Movie = class {
2966
3123
  return this.muted;
2967
3124
  }
2968
3125
  async render(options) {
2969
- chunkUZTKEYFP_cjs.warnUnknownOptions("movie.render()", options, ["format", "video", "audio", "motionBlur", "range", "scale", "draft"]);
3126
+ chunkTWZNOUM7_cjs.warnUnknownOptions("movie.render()", options, ["format", "video", "audio", "motionBlur", "range", "scale", "draft"]);
2970
3127
  try {
2971
3128
  return await this._quietly(() => exportFrames(this, options));
2972
3129
  } catch (err) {
@@ -3024,16 +3181,16 @@ function measureText(text, style = {}) {
3024
3181
  return { width: m.width, height: m.height, lines: text.split("\n").length };
3025
3182
  }
3026
3183
  function splitText(text, style = {}, opts = {}) {
3027
- chunkUZTKEYFP_cjs.warnUnknownOptions("splitText()", opts, ["by", "x", "y", "align"]);
3184
+ chunkTWZNOUM7_cjs.warnUnknownOptions("splitText()", opts, ["by", "x", "y", "align"]);
3028
3185
  let by = opts.by ?? "chars";
3029
3186
  if (!BY.includes(by)) {
3030
- const g = chunkUZTKEYFP_cjs.suggestName(String(by), BY);
3187
+ const g = chunkTWZNOUM7_cjs.suggestName(String(by), BY);
3031
3188
  console.warn(`pixi-effects: splitText(): by "${by}" is not one of ${BY.map((b) => `"${b}"`).join(", ")}${g ? ` \u2014 did you mean "${g}"?` : ""}; using "chars"`);
3032
3189
  by = "chars";
3033
3190
  }
3034
3191
  let align = opts.align ?? "left";
3035
3192
  if (!ALIGN.includes(align)) {
3036
- const g = chunkUZTKEYFP_cjs.suggestName(String(align), ALIGN);
3193
+ const g = chunkTWZNOUM7_cjs.suggestName(String(align), ALIGN);
3037
3194
  console.warn(`pixi-effects: splitText(): align "${align}" is not one of ${ALIGN.join(", ")}${g ? ` \u2014 did you mean "${g}"?` : ""}; using "left"`);
3038
3195
  align = "left";
3039
3196
  }
@@ -3070,7 +3227,7 @@ function splitText(text, style = {}, opts = {}) {
3070
3227
  // src/presets/kenBurns.ts
3071
3228
  var KEN_BURNS_KEYS = ["asset", "name", "at", "duration", "fit", "ease", "motion", "origin", "zoom", "direction", "angle", "from", "to"];
3072
3229
  function kenBurns(opts) {
3073
- chunkUZTKEYFP_cjs.warnUnknownOptions("kenBurns()", opts, KEN_BURNS_KEYS);
3230
+ chunkTWZNOUM7_cjs.warnUnknownOptions("kenBurns()", opts, KEN_BURNS_KEYS);
3074
3231
  const {
3075
3232
  asset,
3076
3233
  name,
@@ -3180,14 +3337,14 @@ function clamp(v, lo, hi) {
3180
3337
  return Math.max(lo, Math.min(hi, v));
3181
3338
  }
3182
3339
  function parseEase(name) {
3183
- chunkIFZOI3JQ_cjs.checkEase(name, "a preset");
3340
+ chunkDP37TTMO_cjs.checkEase(name, "a preset");
3184
3341
  return gsap.gsap.parseEase(name);
3185
3342
  }
3186
3343
 
3187
3344
  // src/presets/orbit.ts
3188
3345
  var ORBIT_KEYS = ["duration", "degrees", "start", "radius", "center", "width", "height", "fov", "ease", "dollyZoom", "at", "name", "stepsPerSecond"];
3189
3346
  function orbit(opts) {
3190
- chunkUZTKEYFP_cjs.warnUnknownOptions("orbit()", opts, ORBIT_KEYS);
3347
+ chunkTWZNOUM7_cjs.warnUnknownOptions("orbit()", opts, ORBIT_KEYS);
3191
3348
  const {
3192
3349
  duration,
3193
3350
  degrees,
@@ -3203,17 +3360,17 @@ function orbit(opts) {
3203
3360
  throw new Error("pixi-effects: orbit() cannot combine `radius` with `dollyZoom` \u2014 the radius follows the animated fov");
3204
3361
  }
3205
3362
  const [cx, cy] = opts.center ?? [width / 2, height / 2];
3206
- const fixedR = opts.radius ?? chunkIFZOI3JQ_cjs.homeDistance(height, fov ?? chunkIFZOI3JQ_cjs.DEFAULT_FOV);
3363
+ const fixedR = opts.radius ?? chunkDP37TTMO_cjs.homeDistance(height, fov ?? chunkDP37TTMO_cjs.DEFAULT_FOV);
3207
3364
  const start = opts.start ?? -degrees / 2;
3208
3365
  const steps = Math.max(1, Math.round(duration * stepsPerSecond));
3209
3366
  const easeFn = parseEase(ease);
3210
3367
  const dollyEase = dolly ? parseEase(dolly.ease ?? ease) : null;
3211
3368
  const pos = (i) => {
3212
3369
  const p = i / steps;
3213
- const a = (start + degrees * easeFn(p)) * chunkIFZOI3JQ_cjs.DEG;
3370
+ const a = (start + degrees * easeFn(p)) * chunkDP37TTMO_cjs.DEG;
3214
3371
  if (!dolly) return { x: cx + fixedR * Math.sin(a), z: fixedR * Math.cos(a) };
3215
3372
  const f = dolly.from + (dolly.to - dolly.from) * dollyEase(p);
3216
- const R = chunkIFZOI3JQ_cjs.homeDistance(height, f);
3373
+ const R = chunkDP37TTMO_cjs.homeDistance(height, f);
3217
3374
  return { x: cx + R * Math.sin(a), z: R * Math.cos(a), fov: f };
3218
3375
  };
3219
3376
  const keyframes = [];
@@ -3245,7 +3402,7 @@ var KEYS2 = ["count", "at", "emit", "life", "area", "angle", "speed", "gravity",
3245
3402
  var SHAPES = ["circle", "rect", "star"];
3246
3403
  var MAX_KEYFRAMES = 3e4;
3247
3404
  function particles(opts) {
3248
- chunkUZTKEYFP_cjs.warnUnknownOptions("particles()", opts, KEYS2);
3405
+ chunkTWZNOUM7_cjs.warnUnknownOptions("particles()", opts, KEYS2);
3249
3406
  const { count, at = 0, emit = 0, gravity = 0, wind = 0, drag = 0, sway, fade, colors = ["#ffffff"], shape = "circle", seed = 0, name = "particle", template, sampleRate = 20 } = opts;
3250
3407
  if (!(Number.isInteger(count) && count > 0)) throw new Error(`particles(): count must be a whole number of particles (at least 1), got ${count}`);
3251
3408
  const life = range("life", opts.life, 1e-6);
@@ -3253,7 +3410,7 @@ function particles(opts) {
3253
3410
  if (!(Number.isFinite(emit) && emit >= 0)) throw new Error(`particles(): emit must be a number of seconds >= 0, got ${emit}`);
3254
3411
  if (!(Number.isFinite(sampleRate) && sampleRate > 0)) throw new Error(`particles(): sampleRate must be a positive number (samples per second), got ${sampleRate}`);
3255
3412
  if (!SHAPES.includes(shape)) {
3256
- const g = chunkUZTKEYFP_cjs.suggestName(String(shape), SHAPES);
3413
+ const g = chunkTWZNOUM7_cjs.suggestName(String(shape), SHAPES);
3257
3414
  throw new Error(`particles(): shape "${shape}" is not one of ${SHAPES.map((s) => `"${s}"`).join(", ")}${g ? `; did you mean "${g}"?` : ""}`);
3258
3415
  }
3259
3416
  const angle = range("angle", opts.angle ?? [0, 360], -Infinity);
@@ -3268,7 +3425,7 @@ function particles(opts) {
3268
3425
  const round3 = (v) => Math.round(v * 1e6) / 1e6;
3269
3426
  const out = [];
3270
3427
  for (let i = 0; i < count; i++) {
3271
- const r = chunkIFZOI3JQ_cjs.random(seed * 1000003 + i * 7919 + 17);
3428
+ const r = chunkDP37TTMO_cjs.random(seed * 1000003 + i * 7919 + 17);
3272
3429
  const pick = ([a2, b]) => a2 + r() * (b - a2);
3273
3430
  const born = at + r() * emit;
3274
3431
  const lifetime = pick(life);
@@ -3281,7 +3438,7 @@ function particles(opts) {
3281
3438
  const turn = spin ? pick(spin) : 0;
3282
3439
  const startAngle = spin ? r() * 360 : 0;
3283
3440
  const swayAmp = sway?.amp ?? 0, swayFreq = sway?.freq ?? 1;
3284
- const n0 = sway ? chunkIFZOI3JQ_cjs.noise(0, seed * 1000003 + i) : 0;
3441
+ const n0 = sway ? chunkDP37TTMO_cjs.noise(0, seed * 1000003 + i) : 0;
3285
3442
  const at_ = (t) => {
3286
3443
  const axis = (p0, v0, acc) => {
3287
3444
  if (drag > 0) {
@@ -3290,7 +3447,7 @@ function particles(opts) {
3290
3447
  }
3291
3448
  return p0 + v0 * t + 0.5 * acc * t * t;
3292
3449
  };
3293
- const sw = sway ? swayAmp * (chunkIFZOI3JQ_cjs.noise(t * swayFreq, seed * 1000003 + i) - n0) : 0;
3450
+ const sw = sway ? swayAmp * (chunkDP37TTMO_cjs.noise(t * swayFreq, seed * 1000003 + i) - n0) : 0;
3294
3451
  return { x: axis(x0, vx, wind) + sw, y: axis(y0, vy, gravity) };
3295
3452
  };
3296
3453
  const kfs = [];
@@ -3340,7 +3497,7 @@ function range(name, v, min) {
3340
3497
  var KEYS3 = ["d", "at", "duration", "ease", "from", "to", "orient", "rotate", "frameRate"];
3341
3498
  var MAX_STEPS = 2e3;
3342
3499
  function followPath(opts) {
3343
- chunkUZTKEYFP_cjs.warnUnknownOptions("followPath()", opts, KEYS3);
3500
+ chunkTWZNOUM7_cjs.warnUnknownOptions("followPath()", opts, KEYS3);
3344
3501
  const { d, at = 0, duration, ease = "none", from = 0, to = 1, orient = false, rotate = 0, frameRate = 30 } = opts;
3345
3502
  if (typeof d !== "string" || d.trim() === "") throw new Error('followPath(): d must be SVG path data, e.g. "M 100 600 C 300 100 900 100 1180 600"');
3346
3503
  if (!(Number.isFinite(duration) && duration > 0)) throw new Error(`followPath(): duration must be a positive number of seconds, got ${duration}`);
@@ -3352,7 +3509,7 @@ function followPath(opts) {
3352
3509
  if (steps > MAX_STEPS) throw new Error(`followPath(): ${steps} samples is too many (max ${MAX_STEPS}); lower frameRate or duration`);
3353
3510
  const segments = [];
3354
3511
  let total = 0;
3355
- for (const line of chunkIFZOI3JQ_cjs.flattenSvgPath(d)) {
3512
+ for (const line of chunkDP37TTMO_cjs.flattenSvgPath(d)) {
3356
3513
  const p = line.closed ? [...line.pts, line.pts[0], line.pts[1]] : line.pts;
3357
3514
  for (let i = 2; i < p.length; i += 2) {
3358
3515
  const len = Math.hypot(p[i] - p[i - 2], p[i + 1] - p[i - 1]);
@@ -3407,7 +3564,7 @@ var KEYS4 = ["points", "at", "duration", "ease", "look", "lookAhead", "smooth",
3407
3564
  var MAX_STEPS2 = 4e3;
3408
3565
  var PER_SPAN = 48;
3409
3566
  function cameraPath(opts) {
3410
- chunkUZTKEYFP_cjs.warnUnknownOptions("cameraPath()", opts, KEYS4);
3567
+ chunkTWZNOUM7_cjs.warnUnknownOptions("cameraPath()", opts, KEYS4);
3411
3568
  const { points, at = 0, duration, ease = "none", look = "ahead", lookAhead = 0.08, smooth = true, from = 0, to = 1, frameRate = 30 } = opts;
3412
3569
  if (!Array.isArray(points) || points.length < 2) throw new Error("cameraPath(): points needs at least two points: [[x, y, z], [x, y, z], \u2026]");
3413
3570
  points.forEach((p, i) => {
@@ -3481,7 +3638,7 @@ function stagger(items, options = {}) {
3481
3638
  const isCount = typeof items === "number";
3482
3639
  const n = isCount ? items : items.length;
3483
3640
  if (!Number.isInteger(n) || n < 0) throw new Error(`stagger(): count must be a whole number of items, got ${n}`);
3484
- chunkUZTKEYFP_cjs.warnUnknownOptions("stagger()", options, STAGGER_KEYS);
3641
+ chunkTWZNOUM7_cjs.warnUnknownOptions("stagger()", options, STAGGER_KEYS);
3485
3642
  const { each, amount, from = "start", grid, ease = "none", seed = 0 } = options;
3486
3643
  if (each !== void 0 && amount !== void 0) throw new Error("stagger(): give `each` (seconds between neighbours) or `amount` (seconds in total), not both");
3487
3644
  if (each !== void 0 && !(Number.isFinite(each) && each >= 0)) throw new Error(`stagger(): each must be a number of seconds >= 0, got ${each}`);
@@ -3506,7 +3663,7 @@ function distances(n, from, grid, seed) {
3506
3663
  if (n === 0) return [];
3507
3664
  if (from === "random") {
3508
3665
  const order = Array.from({ length: n }, (_, i) => i);
3509
- const r = chunkIFZOI3JQ_cjs.random(seed);
3666
+ const r = chunkDP37TTMO_cjs.random(seed);
3510
3667
  for (let i = n - 1; i > 0; i--) {
3511
3668
  const j = Math.floor(r() * (i + 1));
3512
3669
  [order[i], order[j]] = [order[j], order[i]];
@@ -3543,7 +3700,7 @@ function distances(n, from, grid, seed) {
3543
3700
  var WIGGLE_KEYS = ["at", "duration", "freq", "seed", "ease", "props"];
3544
3701
  var MAX_STEPS3 = 2e3;
3545
3702
  function wiggle(opts) {
3546
- chunkUZTKEYFP_cjs.warnUnknownOptions("wiggle()", opts, WIGGLE_KEYS);
3703
+ chunkTWZNOUM7_cjs.warnUnknownOptions("wiggle()", opts, WIGGLE_KEYS);
3547
3704
  const { at = 0, duration, freq = 3, seed = 0, ease = "sine.inOut", props } = opts;
3548
3705
  if (!(Number.isFinite(duration) && duration > 0)) throw new Error(`wiggle(): duration must be a positive number of seconds, got ${duration}`);
3549
3706
  if (!(Number.isFinite(freq) && freq > 0)) throw new Error(`wiggle(): freq must be a positive number (steps per second), got ${freq}`);
@@ -3568,7 +3725,7 @@ function wiggle(opts) {
3568
3725
  to[n] = p.around;
3569
3726
  return;
3570
3727
  }
3571
- const off = (chunkIFZOI3JQ_cjs.rand(seed * 7919 + pi * 104729 + s * 31 + 1) * 2 - 1) * p.amp;
3728
+ const off = (chunkDP37TTMO_cjs.rand(seed * 7919 + pi * 104729 + s * 31 + 1) * 2 - 1) * p.amp;
3572
3729
  to[n] = typeof p.around === "number" ? p.around + off : `(${p.around}) ${off < 0 ? "-" : "+"} ${Math.abs(off)}`;
3573
3730
  });
3574
3731
  out.push({ at: at + s * stepLen, to, duration: stepLen, ease });
@@ -3591,7 +3748,7 @@ var PRESETS = {
3591
3748
  };
3592
3749
  var PRESET_NAMES = Object.keys(PRESETS);
3593
3750
  function animateText(text, style, options) {
3594
- chunkUZTKEYFP_cjs.warnUnknownOptions("animateText()", options, KEYS5);
3751
+ chunkTWZNOUM7_cjs.warnUnknownOptions("animateText()", options, KEYS5);
3595
3752
  const { by = "chars", x = 0, y = 0, align = "left", name = "text", at = 0, duration, idle, styleFor } = options;
3596
3753
  for (const [n, v] of [["x", x], ["y", y]]) {
3597
3754
  if (typeof v !== "number" || !Number.isFinite(v)) {
@@ -3667,11 +3824,11 @@ function resolveTween(spec, which) {
3667
3824
  if (TWEEN_KEYS.includes(key)) continue;
3668
3825
  if (TIME_KEYS.includes(key)) {
3669
3826
  console.warn(`pixi-effects: animateText(): ${which}.${key} ("${key}") is ignored: ${which === "out" ? "an out leaves from the END of the layer (at + duration), so to make the text gone at a time T set duration to T \u2212 at" : "the entrance starts when the piece starts (the options' at, plus the stagger)"}`);
3670
- } else chunkUZTKEYFP_cjs.warnUnknownOptions(`animateText() ${which}`, { [key]: 1 }, TWEEN_KEYS);
3827
+ } else chunkTWZNOUM7_cjs.warnUnknownOptions(`animateText() ${which}`, { [key]: 1 }, TWEEN_KEYS);
3671
3828
  }
3672
3829
  }
3673
3830
  if (t.preset !== void 0 && !(t.preset in PRESETS)) {
3674
- const guess = chunkUZTKEYFP_cjs.suggestName(String(t.preset), PRESET_NAMES);
3831
+ const guess = chunkTWZNOUM7_cjs.suggestName(String(t.preset), PRESET_NAMES);
3675
3832
  throw new Error(`animateText(): ${which} "${t.preset}" is not a preset${guess ? `; did you mean "${guess}"?` : ""} (presets: ${PRESET_NAMES.map((n) => `"${n}"`).join(", ")})`);
3676
3833
  }
3677
3834
  const base = t.preset ? PRESETS[t.preset] : void 0;
@@ -3692,7 +3849,7 @@ var KEYS6 = ["at", "duration", "props", "audioOffset", "loop", "frameRate"];
3692
3849
  var PROP_KEYS = ["base", "amount", "band", "beats", "decay", "attack", "release", "invert", "curve"];
3693
3850
  var MAX_STEPS4 = 4e3;
3694
3851
  function react(env, options) {
3695
- chunkUZTKEYFP_cjs.warnUnknownOptions("react()", options, KEYS6);
3852
+ chunkTWZNOUM7_cjs.warnUnknownOptions("react()", options, KEYS6);
3696
3853
  const { at = 0, duration, props, audioOffset = 0, loop = false } = options;
3697
3854
  const frameRate = options.frameRate ?? env.frameRate;
3698
3855
  if (!(Number.isFinite(duration) && duration > 0)) throw new Error(`react(): duration must be a positive number of seconds, got ${duration}`);
@@ -3701,7 +3858,7 @@ function react(env, options) {
3701
3858
  if (names.length === 0) throw new Error("react(): props is empty; name at least one property, e.g. props: { scale: { base: 1, amount: 0.3 } }");
3702
3859
  for (const n of names) {
3703
3860
  const p = props[n];
3704
- chunkUZTKEYFP_cjs.warnUnknownOptions(`react() props.${n}`, p, PROP_KEYS);
3861
+ chunkTWZNOUM7_cjs.warnUnknownOptions(`react() props.${n}`, p, PROP_KEYS);
3705
3862
  if (!Number.isFinite(p.amount)) throw new Error(`react(): props.${n}.amount must be a number, got ${p.amount}`);
3706
3863
  if (!p.beats && p.band !== void 0 && !env.series[p.band]) throw new Error(`react(): props.${n}: band "${p.band}" is not in this envelope (it has: ${Object.keys(env.series).join(", ")})`);
3707
3864
  if (p.beats && !(p.decay === void 0 || Number.isFinite(p.decay) && p.decay > 0)) throw new Error(`react(): props.${n}.decay must be a positive number of seconds, got ${p.decay}`);
@@ -3808,7 +3965,7 @@ function tempo(beats) {
3808
3965
  return Math.round(60 / median * 10) / 10;
3809
3966
  }
3810
3967
  function computeEnvelope(audio, options = {}) {
3811
- chunkUZTKEYFP_cjs.warnUnknownOptions("audioEnvelope()", options, OPTION_KEYS);
3968
+ chunkTWZNOUM7_cjs.warnUnknownOptions("audioEnvelope()", options, OPTION_KEYS);
3812
3969
  const frameRate = options.frameRate ?? 30;
3813
3970
  if (!(Number.isFinite(frameRate) && frameRate > 0)) throw new Error(`pixi-effects: audioEnvelope(): frameRate must be a positive number, got ${frameRate}`);
3814
3971
  const bands = options.bands ?? DEFAULT_BANDS;
@@ -3932,7 +4089,7 @@ async function audioEnvelope(source, options = {}) {
3932
4089
 
3933
4090
  // src/audio/musicEnvelope.ts
3934
4091
  async function musicEnvelope(music, options = {}) {
3935
- const { resolveMusic, renderMusic, musicLength, musicBeatTimes, TempoMap } = await import('./music-GEAH2XCT.cjs');
4092
+ const { resolveMusic, renderMusic, musicLength, musicBeatTimes, TempoMap } = await import('./music-KB7YZLHP.cjs');
3936
4093
  const messages = [];
3937
4094
  const m = resolveMusic(music, "musicEnvelope()", (msg) => messages.push(msg));
3938
4095
  if (!m) throw new Error(`pixi-effects: musicEnvelope(): ${messages.join(" ") || "the music has nothing to play"}`);
@@ -3945,7 +4102,7 @@ async function musicEnvelope(music, options = {}) {
3945
4102
 
3946
4103
  // src/audio/musicBuffer.ts
3947
4104
  async function musicBuffer(music, options = {}) {
3948
- const { resolveMusic, renderMusic, musicLength } = await import('./music-GEAH2XCT.cjs');
4105
+ const { resolveMusic, renderMusic, musicLength } = await import('./music-KB7YZLHP.cjs');
3949
4106
  const messages = [];
3950
4107
  const m = resolveMusic(music, "musicBuffer()", (msg) => messages.push(msg));
3951
4108
  if (!m) throw new Error(`pixi-effects: musicBuffer(): ${messages.join(" ") || "the music has nothing to play"}`);
@@ -3965,11 +4122,11 @@ var PAGE_KEYS = ["name", "duration", "sequences", "stops", "notes", "advance", "
3965
4122
  var DECK_KEYS = ["pages", "transition", "width", "height", "frameRate", "background", "canvas", "assets", "poster", "motionBlur", "loader"];
3966
4123
  function deck(options) {
3967
4124
  if (!options || !Array.isArray(options.pages) || options.pages.length === 0) throw new Error("deck(): pages must be a list with at least one page: { pages: [{ name, duration, stops, sequences }] }");
3968
- chunkUZTKEYFP_cjs.warnUnknownOptions("deck()", options, DECK_KEYS);
4125
+ chunkTWZNOUM7_cjs.warnUnknownOptions("deck()", options, DECK_KEYS);
3969
4126
  const { pages, transition, ...movieOptions } = options;
3970
4127
  pages.forEach((p, i) => {
3971
4128
  if (!p || typeof p !== "object") throw new Error(`deck(): pages[${i}] must be an object`);
3972
- chunkUZTKEYFP_cjs.warnUnknownOptions(`deck() pages[${i}]`, p, PAGE_KEYS);
4129
+ chunkTWZNOUM7_cjs.warnUnknownOptions(`deck() pages[${i}]`, p, PAGE_KEYS);
3973
4130
  if (!(typeof p.duration === "number" && Number.isFinite(p.duration) && p.duration > 0)) throw new Error(`deck(): pages[${i}].duration must be a positive number of seconds, got ${String(p.duration)}`);
3974
4131
  if (!Array.isArray(p.sequences)) throw new Error(`deck(): pages[${i}].sequences must be a list of layers (it may be empty), got ${typeof p.sequences}`);
3975
4132
  });
@@ -3989,7 +4146,7 @@ function deck(options) {
3989
4146
  const label = page.name ? `"${page.name}"` : `pages[${i}]`;
3990
4147
  const isLast = i === pages.length - 1;
3991
4148
  const entries = (page.stops ?? [isLast ? page.duration : page.duration - overlap]).map((e, k) => {
3992
- if (e && typeof e === "object") chunkUZTKEYFP_cjs.warnUnknownOptions(`deck() page ${label} stops[${k}]`, e, ["at", "pdf"]);
4149
+ if (e && typeof e === "object") chunkTWZNOUM7_cjs.warnUnknownOptions(`deck() page ${label} stops[${k}]`, e, ["at", "pdf"]);
3993
4150
  return typeof e === "object" && e !== null ? e : { at: e };
3994
4151
  });
3995
4152
  const local = entries.map((e) => e.at);
@@ -4023,7 +4180,7 @@ var round2 = (v) => Math.round(v * 1e6) / 1e6;
4023
4180
 
4024
4181
  // src/transforms/withFade.ts
4025
4182
  function withFade(spec, opts) {
4026
- chunkUZTKEYFP_cjs.warnUnknownOptions("withFade()", opts, ["in", "out"]);
4183
+ chunkTWZNOUM7_cjs.warnUnknownOptions("withFade()", opts, ["in", "out"]);
4027
4184
  if (opts.in != null && opts.in > 0) {
4028
4185
  spec.initial = { ...spec.initial ?? {}, alpha: 0 };
4029
4186
  const kf = { at: 0, to: { alpha: 1 }, duration: opts.in };
@@ -4043,19 +4200,19 @@ function withFade(spec, opts) {
4043
4200
 
4044
4201
  Object.defineProperty(exports, "noise", {
4045
4202
  enumerable: true,
4046
- get: function () { return chunkIFZOI3JQ_cjs.noise; }
4203
+ get: function () { return chunkDP37TTMO_cjs.noise; }
4047
4204
  });
4048
4205
  Object.defineProperty(exports, "rand", {
4049
4206
  enumerable: true,
4050
- get: function () { return chunkIFZOI3JQ_cjs.rand; }
4207
+ get: function () { return chunkDP37TTMO_cjs.rand; }
4051
4208
  });
4052
4209
  Object.defineProperty(exports, "random", {
4053
4210
  enumerable: true,
4054
- get: function () { return chunkIFZOI3JQ_cjs.random; }
4211
+ get: function () { return chunkDP37TTMO_cjs.random; }
4055
4212
  });
4056
4213
  Object.defineProperty(exports, "registerSequenceType", {
4057
4214
  enumerable: true,
4058
- get: function () { return chunkIFZOI3JQ_cjs.registerSequenceType; }
4215
+ get: function () { return chunkDP37TTMO_cjs.registerSequenceType; }
4059
4216
  });
4060
4217
  exports.Movie = Movie;
4061
4218
  exports.animateText = animateText;