pixi-effects 0.26.0 → 0.26.1

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 (46) hide show
  1. package/CHANGELOG.md +10 -0
  2. package/README.md +5 -5
  3. package/ai/chat-template.html +3 -3
  4. package/ai/tools/check.mjs +12 -0
  5. package/dist/{Base-BgnRXbtJ.d.ts → Base-CpdDs4HT.d.ts} +1 -1
  6. package/dist/{Base-DHVKqA46.d.cts → Base-TbBhHPrG.d.cts} +1 -1
  7. package/dist/Composition-THQPG4E5.cjs +16 -0
  8. package/dist/{Composition-2GJSCHPP.cjs.map → Composition-THQPG4E5.cjs.map} +1 -1
  9. package/dist/Composition-Z2VY3KWZ.js +7 -0
  10. package/dist/{Composition-C4FSWJZF.js.map → Composition-Z2VY3KWZ.js.map} +1 -1
  11. package/dist/Controller.d.cts +2 -2
  12. package/dist/Controller.d.ts +2 -2
  13. package/dist/{Movie-DP5p1r4E.d.ts → Movie-DVeDTdnc.d.ts} +23 -2
  14. package/dist/{Movie-DXReF4-W.d.cts → Movie-i05Oh9mJ.d.cts} +23 -2
  15. package/dist/Presenter.d.cts +2 -2
  16. package/dist/Presenter.d.ts +2 -2
  17. package/dist/{chunk-DP37TTMO.cjs → chunk-EABL3UKJ.cjs} +5 -3
  18. package/dist/chunk-EABL3UKJ.cjs.map +1 -0
  19. package/dist/{chunk-SY27HZC5.cjs → chunk-H3RHEH5W.cjs} +59 -59
  20. package/dist/{chunk-SY27HZC5.cjs.map → chunk-H3RHEH5W.cjs.map} +1 -1
  21. package/dist/{chunk-PRZ4U6JN.js → chunk-JHJNEIDI.js} +5 -3
  22. package/dist/chunk-JHJNEIDI.js.map +1 -0
  23. package/dist/{chunk-XS6GSJCN.js → chunk-YEVAUQ6Y.js} +3 -3
  24. package/dist/{chunk-XS6GSJCN.js.map → chunk-YEVAUQ6Y.js.map} +1 -1
  25. package/dist/index.cjs +186 -32
  26. package/dist/index.cjs.map +1 -1
  27. package/dist/index.d.cts +4 -4
  28. package/dist/index.d.ts +4 -4
  29. package/dist/index.js +162 -8
  30. package/dist/index.js.map +1 -1
  31. package/dist/loader.css +35 -2
  32. package/dist/three.cjs +5 -5
  33. package/dist/three.d.cts +2 -2
  34. package/dist/three.d.ts +2 -2
  35. package/dist/three.js +1 -1
  36. package/dist/{types-CoT0ypcx.d.ts → types-DSGwN5w1.d.cts} +2 -0
  37. package/dist/{types-CoT0ypcx.d.cts → types-DSGwN5w1.d.ts} +2 -0
  38. package/docs/api.md +3 -2
  39. package/llms-full.txt +4 -3
  40. package/package.json +1 -1
  41. package/skills/pixi-effects/SKILL.md +2 -2
  42. package/skills/pixi-effects/template.html +6 -5
  43. package/dist/Composition-2GJSCHPP.cjs +0 -16
  44. package/dist/Composition-C4FSWJZF.js +0 -7
  45. package/dist/chunk-DP37TTMO.cjs.map +0 -1
  46. package/dist/chunk-PRZ4U6JN.js.map +0 -1
package/dist/index.d.cts CHANGED
@@ -1,7 +1,7 @@
1
- export { A as AudioInspectOptions, a as AudioReport, b as AudioSourceReport, C as ContactSheetOptions, F as FontReport, c as FrameEvent, I as InspectOptions, d as InspectReport, e as IssueGroup, L as LayerInfo, f as LoaderOption, M as MotionBlurOptions, g as MotionBlurSpec, h as Movie, i as MovieErrorEvent, j as MovieOptions, O as OnionSkinOptions, P as ProgressEvent, R as RenderOptions, k as ReviewOptions, l as ReviewReport, S as SnapshotOptions, m as SoundMeasure, n as StopEvent, o as StopImage, p as StopImagesOptions, T as TimelineData, q as TimelineHtmlOptions, r as TimelineRow, s as TimelineSvgOptions, t as TimelineTransition, V as VolumeEvent, u as dismissLoader, v as failLoader, w as resolveLoader } from './Movie-DXReF4-W.cjs';
2
- import { S as SequenceSpec, C as CompositionShape, I as ImageSequenceSpec, a as CameraSequenceSpec, K as Keyframe, T as TextSequenceSpec, M as MusicOptions, P as Props, b as TransitionSpec, c as CompositionSpec, d as SequenceCommon } from './types-CoT0ypcx.cjs';
3
- export { A as ArcShapeSpec, e as AssetSpec, f as AudioAssetSpec, g as AudioMusicSpec, h as AudioSequenceSpec, i as AudioSfxSpec, B as BlendModeName, j as ChromaKeyFilterSpec, k as CircleShapeSpec, l as CompositionSequenceSpec, m as CrossfadeTransition, n as CustomFilterSpec, D as DipTransition, o as DissolveTransition, E as EllipseShapeSpec, p as Expr, F as FilterSpec, G as GradientSpec, q as IrisTransition, L as LightSequenceSpec, r as LineShapeSpec, s as LumaTransition, t as MaskRefSpec, u as MatteRefSpec, v as MusicDrum, w as MusicDrums, x as MusicInstrument, y as MusicLevel, z as MusicTrack, N as NullSequenceSpec, H as PathShapeSpec, J as PolygonShapeSpec, O as PropValue, R as RectShapeSpec, Q as SfxKnobs, U as SfxOptions, V as SfxPreset, W as SfxVoice, X as ShaderSequenceSpec, Y as ShapeSequenceSpec, Z as SlideTransition, _ as Stop, $ as StopSpec, a0 as TransitionCommon, a1 as VideoSequenceSpec, a2 as WipeTransition, a3 as ZoomTransition, a4 as normalizeStops, a5 as pictureStops } from './types-CoT0ypcx.cjs';
4
- import { S as Sequence } from './Base-DHVKqA46.cjs';
1
+ export { A as AudioInspectOptions, a as AudioReport, b as AudioSourceReport, C as ContactSheetOptions, F as FontReport, c as FrameEvent, I as InspectOptions, d as InspectReport, e as IssueGroup, L as LayerInfo, f as LoadProgressState, g as LoadStage, h as LoaderOption, M as MotionBlurOptions, i as MotionBlurSpec, j as Movie, k as MovieErrorEvent, l as MovieOptions, O as OnionSkinOptions, P as ProgressEvent, R as RenderOptions, m as ReviewOptions, n as ReviewReport, S as SnapshotOptions, o as SoundMeasure, p as StopEvent, q as StopImage, r as StopImagesOptions, T as TimelineData, s as TimelineHtmlOptions, t as TimelineRow, u as TimelineSvgOptions, v as TimelineTransition, V as VolumeEvent, w as dismissLoader, x as failLoader, y as resolveLoader } from './Movie-i05Oh9mJ.cjs';
2
+ import { S as SequenceSpec, C as CompositionShape, I as ImageSequenceSpec, a as CameraSequenceSpec, K as Keyframe, T as TextSequenceSpec, M as MusicOptions, P as Props, b as TransitionSpec, c as CompositionSpec, d as SequenceCommon } from './types-DSGwN5w1.cjs';
3
+ export { A as ArcShapeSpec, e as AssetSpec, f as AudioAssetSpec, g as AudioMusicSpec, h as AudioSequenceSpec, i as AudioSfxSpec, B as BlendModeName, j as ChromaKeyFilterSpec, k as CircleShapeSpec, l as CompositionSequenceSpec, m as CrossfadeTransition, n as CustomFilterSpec, D as DipTransition, o as DissolveTransition, E as EllipseShapeSpec, p as Expr, F as FilterSpec, G as GradientSpec, q as IrisTransition, L as LightSequenceSpec, r as LineShapeSpec, s as LumaTransition, t as MaskRefSpec, u as MatteRefSpec, v as MusicDrum, w as MusicDrums, x as MusicInstrument, y as MusicLevel, z as MusicTrack, N as NullSequenceSpec, H as PathShapeSpec, J as PolygonShapeSpec, O as PropValue, R as RectShapeSpec, Q as SfxKnobs, U as SfxOptions, V as SfxPreset, W as SfxVoice, X as ShaderSequenceSpec, Y as ShapeSequenceSpec, Z as SlideTransition, _ as Stop, $ as StopSpec, a0 as TransitionCommon, a1 as VideoSequenceSpec, a2 as WipeTransition, a3 as ZoomTransition, a4 as normalizeStops, a5 as pictureStops } from './types-DSGwN5w1.cjs';
4
+ import { S as Sequence } from './Base-TbBhHPrG.cjs';
5
5
  import 'pixi.js';
6
6
  import 'gsap';
7
7
 
package/dist/index.d.ts CHANGED
@@ -1,7 +1,7 @@
1
- export { A as AudioInspectOptions, a as AudioReport, b as AudioSourceReport, C as ContactSheetOptions, F as FontReport, c as FrameEvent, I as InspectOptions, d as InspectReport, e as IssueGroup, L as LayerInfo, f as LoaderOption, M as MotionBlurOptions, g as MotionBlurSpec, h as Movie, i as MovieErrorEvent, j as MovieOptions, O as OnionSkinOptions, P as ProgressEvent, R as RenderOptions, k as ReviewOptions, l as ReviewReport, S as SnapshotOptions, m as SoundMeasure, n as StopEvent, o as StopImage, p as StopImagesOptions, T as TimelineData, q as TimelineHtmlOptions, r as TimelineRow, s as TimelineSvgOptions, t as TimelineTransition, V as VolumeEvent, u as dismissLoader, v as failLoader, w as resolveLoader } from './Movie-DP5p1r4E.js';
2
- import { S as SequenceSpec, C as CompositionShape, I as ImageSequenceSpec, a as CameraSequenceSpec, K as Keyframe, T as TextSequenceSpec, M as MusicOptions, P as Props, b as TransitionSpec, c as CompositionSpec, d as SequenceCommon } from './types-CoT0ypcx.js';
3
- export { A as ArcShapeSpec, e as AssetSpec, f as AudioAssetSpec, g as AudioMusicSpec, h as AudioSequenceSpec, i as AudioSfxSpec, B as BlendModeName, j as ChromaKeyFilterSpec, k as CircleShapeSpec, l as CompositionSequenceSpec, m as CrossfadeTransition, n as CustomFilterSpec, D as DipTransition, o as DissolveTransition, E as EllipseShapeSpec, p as Expr, F as FilterSpec, G as GradientSpec, q as IrisTransition, L as LightSequenceSpec, r as LineShapeSpec, s as LumaTransition, t as MaskRefSpec, u as MatteRefSpec, v as MusicDrum, w as MusicDrums, x as MusicInstrument, y as MusicLevel, z as MusicTrack, N as NullSequenceSpec, H as PathShapeSpec, J as PolygonShapeSpec, O as PropValue, R as RectShapeSpec, Q as SfxKnobs, U as SfxOptions, V as SfxPreset, W as SfxVoice, X as ShaderSequenceSpec, Y as ShapeSequenceSpec, Z as SlideTransition, _ as Stop, $ as StopSpec, a0 as TransitionCommon, a1 as VideoSequenceSpec, a2 as WipeTransition, a3 as ZoomTransition, a4 as normalizeStops, a5 as pictureStops } from './types-CoT0ypcx.js';
4
- import { S as Sequence } from './Base-BgnRXbtJ.js';
1
+ export { A as AudioInspectOptions, a as AudioReport, b as AudioSourceReport, C as ContactSheetOptions, F as FontReport, c as FrameEvent, I as InspectOptions, d as InspectReport, e as IssueGroup, L as LayerInfo, f as LoadProgressState, g as LoadStage, h as LoaderOption, M as MotionBlurOptions, i as MotionBlurSpec, j as Movie, k as MovieErrorEvent, l as MovieOptions, O as OnionSkinOptions, P as ProgressEvent, R as RenderOptions, m as ReviewOptions, n as ReviewReport, S as SnapshotOptions, o as SoundMeasure, p as StopEvent, q as StopImage, r as StopImagesOptions, T as TimelineData, s as TimelineHtmlOptions, t as TimelineRow, u as TimelineSvgOptions, v as TimelineTransition, V as VolumeEvent, w as dismissLoader, x as failLoader, y as resolveLoader } from './Movie-DVeDTdnc.js';
2
+ import { S as SequenceSpec, C as CompositionShape, I as ImageSequenceSpec, a as CameraSequenceSpec, K as Keyframe, T as TextSequenceSpec, M as MusicOptions, P as Props, b as TransitionSpec, c as CompositionSpec, d as SequenceCommon } from './types-DSGwN5w1.js';
3
+ export { A as ArcShapeSpec, e as AssetSpec, f as AudioAssetSpec, g as AudioMusicSpec, h as AudioSequenceSpec, i as AudioSfxSpec, B as BlendModeName, j as ChromaKeyFilterSpec, k as CircleShapeSpec, l as CompositionSequenceSpec, m as CrossfadeTransition, n as CustomFilterSpec, D as DipTransition, o as DissolveTransition, E as EllipseShapeSpec, p as Expr, F as FilterSpec, G as GradientSpec, q as IrisTransition, L as LightSequenceSpec, r as LineShapeSpec, s as LumaTransition, t as MaskRefSpec, u as MatteRefSpec, v as MusicDrum, w as MusicDrums, x as MusicInstrument, y as MusicLevel, z as MusicTrack, N as NullSequenceSpec, H as PathShapeSpec, J as PolygonShapeSpec, O as PropValue, R as RectShapeSpec, Q as SfxKnobs, U as SfxOptions, V as SfxPreset, W as SfxVoice, X as ShaderSequenceSpec, Y as ShapeSequenceSpec, Z as SlideTransition, _ as Stop, $ as StopSpec, a0 as TransitionCommon, a1 as VideoSequenceSpec, a2 as WipeTransition, a3 as ZoomTransition, a4 as normalizeStops, a5 as pictureStops } from './types-DSGwN5w1.js';
4
+ import { S as Sequence } from './Base-CpdDs4HT.js';
5
5
  import 'pixi.js';
6
6
  import 'gsap';
7
7
 
package/dist/index.js CHANGED
@@ -1,6 +1,6 @@
1
- import { CompositionSequence, maskSourceOf } from './chunk-XS6GSJCN.js';
2
- import { ensureFilterLibrary, homeDistance, DEFAULT_FOV, random, noise, flattenSvgPath, rand, DEG, timeRemapOf, contentLength, resolveAt, checkEase } from './chunk-PRZ4U6JN.js';
3
- export { noise, rand, random, registerSequenceType } from './chunk-PRZ4U6JN.js';
1
+ import { CompositionSequence, maskSourceOf } from './chunk-YEVAUQ6Y.js';
2
+ import { ensureFilterLibrary, homeDistance, DEFAULT_FOV, random, noise, flattenSvgPath, rand, DEG, timeRemapOf, contentLength, resolveAt, checkEase } from './chunk-JHJNEIDI.js';
3
+ export { noise, rand, random, registerSequenceType } from './chunk-JHJNEIDI.js';
4
4
  import { usesAdvancedBlend, enableAdvancedBlend } from './chunk-AXZUKNXF.js';
5
5
  import { collectPropKeys } from './chunk-TLYTYBWX.js';
6
6
  import { warnUnknownOptions, suggestName } from './chunk-BXOCEZ4U.js';
@@ -60,11 +60,11 @@ function ensureLoadersRegistered(audioContext) {
60
60
  }
61
61
  });
62
62
  }
63
- async function loadAssetBundle(assets, audioContext) {
63
+ async function loadAssetBundle(assets, audioContext, onProgress) {
64
64
  ensureLoadersRegistered(audioContext);
65
65
  const bundle = assets.map((a) => ({ alias: a.name, src: a.src }));
66
66
  Assets.addBundle("pixi-effects", bundle);
67
- return await Assets.loadBundle("pixi-effects");
67
+ return await Assets.loadBundle("pixi-effects", onProgress);
68
68
  }
69
69
 
70
70
  // src/core/audioRemap.ts
@@ -2282,6 +2282,100 @@ function changedFraction(a, b, threshold = 12) {
2282
2282
  return changed / n;
2283
2283
  }
2284
2284
 
2285
+ // src/core/loadProgress.ts
2286
+ var STAGE_LABEL = {
2287
+ assets: "LOADING ASSETS",
2288
+ build: "BUILDING LAYERS",
2289
+ sound: "MIXING SOUND",
2290
+ frames: "PREPARING"
2291
+ };
2292
+ var ORDER = ["assets", "build", "sound", "frames"];
2293
+ var WEIGHT = { assets: 0.15, build: 0.6, sound: 0.2, frames: 0.05 };
2294
+ var YIELD_EVERY_MS = 50;
2295
+ function defaultYielder() {
2296
+ if (typeof MessageChannel === "undefined") return new Promise((resolve) => setTimeout(resolve, 0));
2297
+ return new Promise((resolve) => {
2298
+ const ch = new MessageChannel();
2299
+ ch.port1.onmessage = () => {
2300
+ ch.port1.close();
2301
+ resolve();
2302
+ };
2303
+ ch.port2.postMessage(0);
2304
+ });
2305
+ }
2306
+ var LoadProgress = class {
2307
+ constructor(_onChange, opts = {}) {
2308
+ this._onChange = _onChange;
2309
+ this._now = opts.now ?? (() => typeof performance !== "undefined" ? performance.now() : Date.now());
2310
+ this._yielder = opts.yielder ?? defaultYielder;
2311
+ this._hidden = opts.hidden ?? (() => typeof document !== "undefined" && document.hidden === true);
2312
+ this._every = opts.yieldEveryMs ?? YIELD_EVERY_MS;
2313
+ this._lastYield = this._now();
2314
+ }
2315
+ _onChange;
2316
+ _stage = null;
2317
+ _loaded = 0;
2318
+ _total = 0;
2319
+ _high = 0;
2320
+ _started = 0;
2321
+ _ms = { assets: 0, build: 0, sound: 0, frames: 0 };
2322
+ _lastYield;
2323
+ _now;
2324
+ _yielder;
2325
+ _every;
2326
+ _hidden;
2327
+ get progress() {
2328
+ return this._high;
2329
+ }
2330
+ /** Start a stage with `total` items (0 = nothing to wait for). Every earlier stage is done. */
2331
+ begin(stage, total) {
2332
+ this._closeTiming();
2333
+ this._stage = stage;
2334
+ this._loaded = 0;
2335
+ this._total = Math.max(0, total);
2336
+ this._started = this._now();
2337
+ this._emit();
2338
+ }
2339
+ tick(n = 1) {
2340
+ if (!this._stage) return;
2341
+ this._loaded = Math.min(this._total, this._loaded + n);
2342
+ this._emit();
2343
+ }
2344
+ /** Everything is done: 1, whatever stage it was in. */
2345
+ finish() {
2346
+ this._closeTiming();
2347
+ this._stage = this._stage ?? "frames";
2348
+ this._loaded = this._total;
2349
+ this._high = 1;
2350
+ this._onChange({ stage: this._stage, loaded: this._loaded, total: this._total, progress: 1 });
2351
+ }
2352
+ /** Milliseconds each stage took (a stage never begun is 0). */
2353
+ timings() {
2354
+ return { ...this._ms };
2355
+ }
2356
+ /** Await one macrotask when 50 ms of work have gone by since the last time, so the page can paint the bar. Cheap when it is not due. */
2357
+ async yieldIfDue() {
2358
+ const t = this._now();
2359
+ if (t - this._lastYield < this._every || this._hidden()) return;
2360
+ await this._yielder();
2361
+ this._lastYield = this._now();
2362
+ }
2363
+ _closeTiming() {
2364
+ if (this._stage) this._ms[this._stage] += Math.max(0, this._now() - this._started);
2365
+ }
2366
+ _emit() {
2367
+ const stage = this._stage;
2368
+ let done = 0;
2369
+ for (const s of ORDER) {
2370
+ if (s === stage) break;
2371
+ done += WEIGHT[s];
2372
+ }
2373
+ const within = this._total > 0 ? this._loaded / this._total : 1;
2374
+ this._high = Math.min(1, Math.max(this._high, done + WEIGHT[stage] * within));
2375
+ this._onChange({ stage, loaded: this._loaded, total: this._total, progress: this._high });
2376
+ }
2377
+ };
2378
+
2285
2379
  // src/core/loader.ts
2286
2380
  var FADE_FALLBACK_MS = 900;
2287
2381
  function resolveLoader(option, canvas) {
@@ -2307,15 +2401,31 @@ function dismissLoader(loader) {
2307
2401
  loader.remove();
2308
2402
  }
2309
2403
  };
2310
- loader.addEventListener("transitionend", remove, { once: true });
2404
+ loader.addEventListener("transitionend", (e) => {
2405
+ if (e.target === loader && e.propertyName === "opacity") remove();
2406
+ });
2311
2407
  setTimeout(remove, FADE_FALLBACK_MS);
2312
2408
  }
2313
2409
  function failLoader(loader, message) {
2314
2410
  if (!loader) return;
2315
2411
  loader.setAttribute("data-state", "error");
2412
+ for (const a of ["data-stage", "data-percent"]) loader.removeAttribute(a);
2413
+ loader.querySelector(".pe-loader__bar")?.remove();
2316
2414
  if (loader.hasAttribute("data-label")) loader.setAttribute("data-label", message);
2317
2415
  loader.setAttribute("aria-busy", "false");
2318
2416
  }
2417
+ function setLoaderProgress(loader, state) {
2418
+ if (!loader || loader.getAttribute("data-state") === "error" || loader.classList.contains("pe-loader--done")) return;
2419
+ if (!loader.querySelector(".pe-loader__bar")) {
2420
+ const bar = loader.ownerDocument.createElement("i");
2421
+ bar.className = "pe-loader__bar";
2422
+ bar.setAttribute("aria-hidden", "true");
2423
+ loader.appendChild(bar);
2424
+ }
2425
+ loader.style.setProperty("--pe-progress", String(Math.round(state.progress * 1e3) / 1e3));
2426
+ loader.setAttribute("data-stage", STAGE_LABEL[state.stage]);
2427
+ loader.setAttribute("data-percent", String(Math.round(state.progress * 100)));
2428
+ }
2319
2429
 
2320
2430
  // src/core/Movie.ts
2321
2431
  extensions.add(CullerPlugin);
@@ -2395,6 +2505,11 @@ var Movie = class {
2395
2505
  get isReady() {
2396
2506
  return this._initState === "ready";
2397
2507
  }
2508
+ /** Milliseconds `init()` spent in each stage (assets, build, sound, frames), or null before it has finished: `check` says which stage is the slow one. */
2509
+ get loadStages() {
2510
+ return this._loadStages;
2511
+ }
2512
+ _loadStages = null;
2398
2513
  /** 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. */
2399
2514
  get audioBlocked() {
2400
2515
  return audioIsBlocked(this._audioContext, !!this.audioBuffer);
@@ -2479,8 +2594,32 @@ var Movie = class {
2479
2594
  this.app.stage.addChild(root);
2480
2595
  this._rootContainer = root;
2481
2596
  const audioContext = this._ensureAudioContext();
2482
- await loadAssetBundle(options.assets ?? [], audioContext);
2483
- const rootShape = { width: this.width, height: this.height, duration: this.duration, frameRate: this.frameRate };
2597
+ let loaderEl;
2598
+ const lp = new LoadProgress((state) => {
2599
+ if (loaderEl === void 0) loaderEl = resolveLoader(options.loader, options.canvas ?? this.app?.canvas ?? null);
2600
+ setLoaderProgress(loaderEl, state);
2601
+ this.emit("loadprogress", state);
2602
+ }, { yieldEveryMs: globalThis.__peLoadYieldMs });
2603
+ const assetCount = (options.assets ?? []).length;
2604
+ lp.begin("assets", assetCount);
2605
+ let assetsSeen = 0;
2606
+ await loadAssetBundle(options.assets ?? [], audioContext, (p) => {
2607
+ const n = Math.round(p * assetCount);
2608
+ if (n > assetsSeen) {
2609
+ lp.tick(n - assetsSeen);
2610
+ assetsSeen = n;
2611
+ }
2612
+ });
2613
+ const rootShape = {
2614
+ width: this.width,
2615
+ height: this.height,
2616
+ duration: this.duration,
2617
+ frameRate: this.frameRate,
2618
+ onLayerBuilt: async () => {
2619
+ lp.tick();
2620
+ await lp.yieldIfDue();
2621
+ }
2622
+ };
2484
2623
  const seededComposition = options.composition ? {
2485
2624
  ...options.composition,
2486
2625
  width: options.composition.width ?? this.width,
@@ -2498,6 +2637,7 @@ var Movie = class {
2498
2637
  };
2499
2638
  if (userComposition) carryTransitionWindows(userComposition, rootSeqSpec);
2500
2639
  const composition = new CompositionSequence(rootSeqSpec, null, rootShape);
2640
+ lp.begin("build", countLayers(rootSeqSpec));
2501
2641
  await composition.build();
2502
2642
  this._rootSequence = composition;
2503
2643
  if (composition.target) root.addChild(composition.target);
@@ -2505,15 +2645,20 @@ var Movie = class {
2505
2645
  this._timeFilters = collectTimeFilters(composition);
2506
2646
  const audios = [];
2507
2647
  composition.collectAudio(audios, 0);
2648
+ lp.begin("sound", audios.length);
2508
2649
  if (audios.length > 0) {
2509
2650
  this.audioBuffer = await mixdown(audios, this.duration, audioContext.sampleRate);
2651
+ lp.tick(audios.length);
2510
2652
  if (this.audioBuffer) this._mixStats = limitMix(this.audioBuffer, audios);
2511
2653
  }
2512
2654
  this._audioSources = audios;
2513
2655
  this.timeline.progress(1).progress(0);
2656
+ lp.begin("frames", 0);
2514
2657
  await this._awaitVideoFrames();
2515
2658
  this._updateSpace();
2516
2659
  this._renderNow();
2660
+ lp.finish();
2661
+ this._loadStages = lp.timings();
2517
2662
  this._initState = "ready";
2518
2663
  if (this._posterFrame !== null) await this._showPoster();
2519
2664
  this.emit("ready");
@@ -3153,6 +3298,15 @@ function collectVideoSequences(seq, out, clocks, clock = null) {
3153
3298
  collectVideoSequences(child, out, clocks, own);
3154
3299
  }
3155
3300
  }
3301
+ function countLayers(spec) {
3302
+ let n = 0;
3303
+ for (const s of spec?.sequences ?? []) {
3304
+ const mask = s.mask;
3305
+ n += 1 + countLayers(s);
3306
+ for (const m of Array.isArray(mask) ? mask : [mask]) if (m && typeof m === "object" && "type" in m) n += 1 + countLayers(m);
3307
+ }
3308
+ return n;
3309
+ }
3156
3310
  var BY = ["chars", "words", "lines"];
3157
3311
  var ALIGN = ["left", "center", "right"];
3158
3312
  var widthOf = (text, style) => CanvasTextMetrics.measureText(text, style).width;