pixi-effects 0.35.0 → 0.36.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 (75) hide show
  1. package/CHANGELOG.md +18 -0
  2. package/README.md +5 -5
  3. package/ai/chat-template.html +3 -3
  4. package/ai/tools/check.mjs +26 -7
  5. package/ai/tools/lookup.mjs +14 -2
  6. package/dist/{Base-CXsMdmJB.d.ts → Base-CdF7AJop.d.ts} +1 -1
  7. package/dist/{Base-pNq6sVGl.d.cts → Base-CmiFiwQM.d.cts} +1 -1
  8. package/dist/Composition-BMRCE5FH.js +7 -0
  9. package/dist/{Composition-NDXKQHB4.js.map → Composition-BMRCE5FH.js.map} +1 -1
  10. package/dist/Composition-XRLYR6GL.cjs +16 -0
  11. package/dist/{Composition-OLH7RC5Z.cjs.map → Composition-XRLYR6GL.cjs.map} +1 -1
  12. package/dist/Controller.d.cts +2 -2
  13. package/dist/Controller.d.ts +2 -2
  14. package/dist/{Movie-VYtUN4TS.d.ts → Movie-Bh1HPdKw.d.cts} +6 -1
  15. package/dist/{Movie-Db4QnMIH.d.cts → Movie-D5WFfZdZ.d.ts} +6 -1
  16. package/dist/{Spans-QRMN2EZO.cjs → Spans-4IRBSOLI.cjs} +11 -11
  17. package/dist/{Spans-QRMN2EZO.cjs.map → Spans-4IRBSOLI.cjs.map} +1 -1
  18. package/dist/{Spans-QHHWC2PB.js → Spans-MWP2FX3T.js} +6 -6
  19. package/dist/{Spans-QHHWC2PB.js.map → Spans-MWP2FX3T.js.map} +1 -1
  20. package/dist/{blendModes-WIUCYIMC.cjs → blendModes-KU7XLV7U.cjs} +5 -5
  21. package/dist/{blendModes-WIUCYIMC.cjs.map → blendModes-KU7XLV7U.cjs.map} +1 -1
  22. package/dist/{blendModes-PP5NPBXY.js → blendModes-OOI3T6BE.js} +4 -4
  23. package/dist/{blendModes-PP5NPBXY.js.map → blendModes-OOI3T6BE.js.map} +1 -1
  24. package/dist/{chunk-ITDYFBB6.js → chunk-2JFFKHXO.js} +5 -5
  25. package/dist/chunk-2JFFKHXO.js.map +1 -0
  26. package/dist/{chunk-GL2AQFLQ.cjs → chunk-42ML5AKN.cjs} +5 -5
  27. package/dist/chunk-42ML5AKN.cjs.map +1 -0
  28. package/dist/{chunk-XV4L72FW.js → chunk-AZ6AINSW.js} +5 -5
  29. package/dist/{chunk-XV4L72FW.js.map → chunk-AZ6AINSW.js.map} +1 -1
  30. package/dist/{chunk-7TTCPHAV.cjs → chunk-CC7SUR7Q.cjs} +153 -48
  31. package/dist/chunk-CC7SUR7Q.cjs.map +1 -0
  32. package/dist/{chunk-Y253JQOH.js → chunk-G3DX65HO.js} +110 -10
  33. package/dist/chunk-G3DX65HO.js.map +1 -0
  34. package/dist/{chunk-LEECUCTG.cjs → chunk-KJSEQVBV.cjs} +129 -29
  35. package/dist/chunk-KJSEQVBV.cjs.map +1 -0
  36. package/dist/{chunk-IMQJVRMT.cjs → chunk-NEUPEENI.cjs} +5 -5
  37. package/dist/{chunk-IMQJVRMT.cjs.map → chunk-NEUPEENI.cjs.map} +1 -1
  38. package/dist/{chunk-2GNK6DPV.js → chunk-VF56PRAA.js} +121 -16
  39. package/dist/chunk-VF56PRAA.js.map +1 -0
  40. package/dist/{chunk-RTJD3ZSW.js → chunk-WIIU42FH.js} +4 -4
  41. package/dist/{chunk-RTJD3ZSW.js.map → chunk-WIIU42FH.js.map} +1 -1
  42. package/dist/{chunk-5IJENZOG.cjs → chunk-Y5UWFH6Y.cjs} +91 -91
  43. package/dist/{chunk-5IJENZOG.cjs.map → chunk-Y5UWFH6Y.cjs.map} +1 -1
  44. package/dist/index.cjs +33 -33
  45. package/dist/index.d.cts +20 -6
  46. package/dist/index.d.ts +20 -6
  47. package/dist/index.js +5 -5
  48. package/dist/loader.css +1 -1
  49. package/dist/present.cjs +5 -5
  50. package/dist/present.d.cts +2 -2
  51. package/dist/present.d.ts +2 -2
  52. package/dist/present.js +5 -5
  53. package/dist/three.cjs +6 -6
  54. package/dist/three.d.cts +2 -2
  55. package/dist/three.d.ts +2 -2
  56. package/dist/three.js +2 -2
  57. package/dist/{types-CavnUvd5.d.ts → types-B6zDTU2V.d.cts} +14 -1
  58. package/dist/{types-CavnUvd5.d.cts → types-B6zDTU2V.d.ts} +14 -1
  59. package/docs/api.md +5 -4
  60. package/docs/dsl.md +7 -5
  61. package/llms-full.txt +85 -18
  62. package/package.json +1 -1
  63. package/skills/pixi-effects/SKILL.md +3 -3
  64. package/skills/pixi-effects/reference/cheatsheet.md +7 -6
  65. package/skills/pixi-effects/reference/pitfalls.md +3 -0
  66. package/skills/pixi-effects/reference/recipes.md +60 -0
  67. package/skills/pixi-effects/template.html +6 -6
  68. package/dist/Composition-NDXKQHB4.js +0 -7
  69. package/dist/Composition-OLH7RC5Z.cjs +0 -16
  70. package/dist/chunk-2GNK6DPV.js.map +0 -1
  71. package/dist/chunk-7TTCPHAV.cjs.map +0 -1
  72. package/dist/chunk-GL2AQFLQ.cjs.map +0 -1
  73. package/dist/chunk-ITDYFBB6.js.map +0 -1
  74. package/dist/chunk-LEECUCTG.cjs.map +0 -1
  75. package/dist/chunk-Y253JQOH.js.map +0 -1
package/CHANGELOG.md CHANGED
@@ -1,5 +1,23 @@
1
1
  # Changelog
2
2
 
3
+ ## 0.36.0
4
+
5
+ **Vertical writing, `check` that sees pictures over text, holes in paths, and the fixes found by 18 fresh authors (the claims that were verified on the real build)**
6
+
7
+ - **New: vertical writing (tategaki).** `animateText(..., { vertical: true })` and `splitText(..., { vertical: true })`: characters go down a column and a new line is a new column to the left (`x` is the right edge of the first column). 、。 sit at the upper right of their cell, small kana a little up and right, and the long vowel mark, dashes, waves and brackets are turned a quarter clockwise (`rotation: 90`); pieces carry `cx` / `cy` / `rotation`. No tate-chu-yoko and no automatic line breaking.
8
+ - **New: `check` lists an opaque shape or image drawn over part of a text, and a thin line drawn over its body** (review list, with the other overlaps; a shadow, glow, ring or backdrop is not reported), **`check --at … --crop X,Y,W,H`** writes each picture as that part of the canvas, enlarged up to 4x, and **`help`** finds a heading written with spaces (`motionBlur`), a table row (`path`) and a call (`steps`).
9
+ - **New: `format: { time: 'hh:mm' }`** (also `'hh:mm:ss'`, `'h:mm:ss'`, `'mm:ss'`, `'m:ss'`) prints a counter's value, in seconds, as a clock; **`resolution`** on a text layer (1–8) draws it at N× the pixels so text inside a group scaled up N× stays sharp; **image `scaleMode: 'nearest'`** for pixel art; **`inspectAudio`** gives each source `belowMixDb` (how far below the mix around its loudest moment it is) and `check` gives ONE note when short sfx sit more than 12 dB under the mix ("buried", naming the worst).
10
+ - **Guide demos load this repository's build** (`../../dist/…`), like the gallery pages, so a demo shows the code of the same commit.
11
+ - **Fixed: `initial: { fill }` on a text layer was dropped without a word** (only a `fill` keyframe worked). It is now the colour from the start (it wins over `style.fill`).
12
+ - **Fixed: a filter option set to `undefined` (a misspelt variable) overwrote the filter's own default**: `{ type: 'bloom', strength: undefined }` made `movie.init` fail with "Cannot read properties of undefined (reading 'x')". The key is now left out and a warning names the filter and the options.
13
+ - **`check --at`** no longer leaves the pictures of an earlier run in `frames/` (it cleared nothing, so an old frame could be taken for the current one). The music read-back no longer says "ends early" for a track that is a few millionths of a beat shorter (decimal note lengths add up with float noise).
14
+ - **`pixi-effects new --size`** sizes the shown page for a tall or square movie (it was 960 px wide, so a 9:16 page was 1700 px tall).
15
+ - **New: `fillRule: 'evenodd'` on a `path`** (a donut written as two circles in one `d` was a solid disc: sub-paths were never holes). The biggest sub-path is the shape, every other one is cut out of it, and the stroke goes round all of them.
16
+ - **Changed: a morph between two outlines with the same number of corners is paired corner to corner** (it resampled both to 128 points along the edges, so a morph between two quads whose edges are not in proportion rounded the corners half way). Outlines with different corner counts, and curves, behave as before.
17
+ - Docs: a keyframe's `from` holds from the start of the layer and beats `initial` (cheatsheet, pitfall 114: no warning, because `initial` = the resting value and `from` = where it starts is what the recipes do); a path's `x` / `y`, when given, place its middle.
18
+ - Docs: `--out DIR` writes straight into DIR (no `<name>/` inside); pitfalls 112 (text under a scaled-up parent is soft) and 113 (an `sfx` lasts at most 10 s, a `music` layer plays 16 tracks).
19
+ - Gallery and guide: 59 pieces (18 new, 17 swapped, 11 removed); the guide no longer shows gallery pieces (its demos are built from its own recipes, `scripts/build-guide-demos.mjs`); `scripts/gallery-swap.mjs` also adds (`--add`) and removes (`--remove`).
20
+
3
21
  ## 0.35.0
4
22
 
5
23
  **The player's seek-bar preview (`thumbnails: true`, off by default) and `movie.thumbnail()`**
package/README.md CHANGED
@@ -1,6 +1,6 @@
1
1
  # pixi-effects
2
2
 
3
- > **Status**: experimental — current release `0.35.0`. The API may still change between minor versions; see the [CHANGELOG](./CHANGELOG.md) for what each release changed.
3
+ > **Status**: experimental — current release `0.36.0`. The API may still change between minor versions; see the [CHANGELOG](./CHANGELOG.md) for what each release changed.
4
4
 
5
5
  **[Guide →](https://yjmtmtk.github.io/pixi-effects/guide/)** · **[Gallery →](https://yjmtmtk.github.io/pixi-effects/examples/gallery/)** · 52 portfolio pieces written as plain data by AI models · **[Examples →](https://yjmtmtk.github.io/pixi-effects/examples/)** · 15 numbered examples, a music lab + an in-browser playground.
6
6
 
@@ -49,8 +49,8 @@ Drop the imports into an [importmap](https://developer.mozilla.org/docs/Web/HTML
49
49
  "imports": {
50
50
  "pixi.js": "https://esm.sh/pixi.js@8.22.0?bundle-deps",
51
51
  "mediabunny": "https://esm.sh/mediabunny",
52
- "pixi-effects": "https://cdn.jsdelivr.net/npm/pixi-effects@0.35.0/dist/index.js",
53
- "pixi-effects/controller": "https://cdn.jsdelivr.net/npm/pixi-effects@0.35.0/dist/Controller.js"
52
+ "pixi-effects": "https://cdn.jsdelivr.net/npm/pixi-effects@0.36.0/dist/index.js",
53
+ "pixi-effects/controller": "https://cdn.jsdelivr.net/npm/pixi-effects@0.36.0/dist/Controller.js"
54
54
  }
55
55
  }
56
56
  </script>
@@ -61,7 +61,7 @@ Drop the imports into an [importmap](https://developer.mozilla.org/docs/Web/HTML
61
61
  </script>
62
62
  ```
63
63
 
64
- Load the `dist/` files **as they are** (jsDelivr's `/npm/…/dist/…`, or unpkg's `https://unpkg.com/pixi-effects@0.35.0/dist/index.js`) rather than a CDN-rebundled build such as `esm.sh/pixi-effects` or jsDelivr's `+esm`: the entries (`pixi-effects`, `…/controller`, `…/three`) share internal chunks, which only works when each file is served untouched.
64
+ Load the `dist/` files **as they are** (jsDelivr's `/npm/…/dist/…`, or unpkg's `https://unpkg.com/pixi-effects@0.36.0/dist/index.js`) rather than a CDN-rebundled build such as `esm.sh/pixi-effects` or jsDelivr's `+esm`: the entries (`pixi-effects`, `…/controller`, `…/three`) share internal chunks, which only works when each file is served untouched.
65
65
 
66
66
  > **Using three.js?** Add two more entries (`three` and `pixi-effects/three`) to this importmap — see [Adding three.js](#adding-threejs-optional) below.
67
67
  >
@@ -77,7 +77,7 @@ Add two more entries to the importmap above: three.js itself, and the `pixi-effe
77
77
  "imports": {
78
78
  "...": "(everything from the importmap above)",
79
79
  "three": "https://esm.sh/three@0.178.0",
80
- "pixi-effects/three": "https://cdn.jsdelivr.net/npm/pixi-effects@0.35.0/dist/three.js"
80
+ "pixi-effects/three": "https://cdn.jsdelivr.net/npm/pixi-effects@0.36.0/dist/three.js"
81
81
  }
82
82
  }
83
83
  </script>
@@ -59,9 +59,9 @@
59
59
  "imports": {
60
60
  "pixi.js": "https://cdn.jsdelivr.net/npm/pixi.js@8.22.0/dist/pixi.min.mjs",
61
61
  "mediabunny": "https://cdn.jsdelivr.net/npm/mediabunny@1.61.3/+esm",
62
- "pixi-effects": "https://cdn.jsdelivr.net/npm/pixi-effects@0.35.0/dist/index.js",
63
- "pixi-effects/controller": "https://cdn.jsdelivr.net/npm/pixi-effects@0.35.0/dist/Controller.js",
64
- "pixi-effects/present": "https://cdn.jsdelivr.net/npm/pixi-effects@0.35.0/dist/present.js"
62
+ "pixi-effects": "https://cdn.jsdelivr.net/npm/pixi-effects@0.36.0/dist/index.js",
63
+ "pixi-effects/controller": "https://cdn.jsdelivr.net/npm/pixi-effects@0.36.0/dist/Controller.js",
64
+ "pixi-effects/present": "https://cdn.jsdelivr.net/npm/pixi-effects@0.36.0/dist/present.js"
65
65
  }
66
66
  }
67
67
  </script>
@@ -7,7 +7,7 @@
7
7
  * It starts a private static server and a private headless Chrome (nothing shared with other sessions), opens the page,
8
8
  * waits for `window.__ready === true`, then checks: console warnings / errors, `movie.inspect` over the whole timeline
9
9
  * (grouped), `movie.inspectAudio()`, a contact sheet (a PNG you can look at), and a real export (`movie.render`) that it
10
- * decodes again: video size / length, audio length, loudness per second, silence. Output goes to `check-out/<name>/`
10
+ * decodes again: video size / length, audio length, loudness per second, silence. Output goes to `check-out/<name>/` (`--out DIR` writes straight into DIR, with no <name>/ inside it)
11
11
  * (`sheet.png`, `<name>.<format>`, `report.json`; a presentation with `stops` also gets `stops.png`, one picture per stop) and a short summary is printed. Exit code 0 = nothing to fix, 1 = problems.
12
12
  *
13
13
  * Needs: Node >= 22 (built-in WebSocket), Chrome / Chromium installed (or --chrome PATH / CHROME=PATH). No npm dependencies.
@@ -15,6 +15,7 @@
15
15
  *
16
16
  * Options: --draft (the export is a draft: half size, low quality, no motion blur: a much smaller file; the drawing itself is not faster) · --at LIST (pictures at moments you name: 3.5, 50%, f120, title@end → frames/*.png and at.png) ·
17
17
  * --query "a=1&b=2" (added to the page address: for a page that reads it, such as one video of a batch) ·
18
+ * --crop X,Y,W,H (with --at: each picture is that part of the canvas, in canvas pixels, enlarged up to 4x so a small figure can be judged) ·
18
19
  * --onion A:B (one picture of the movement between A and B seconds, frames overlaid: onion.png) ·
19
20
  * --strict (text overlaps and stops where the picture is still changing fail the check; by default they are only listed for review) · --out DIR · --frames N (contact sheet tiles, default 12) · --formats mp4,webm (default mp4) · --no-export ·
20
21
  * --timeout SECONDS (default 240) · --root DIR (static server root; default: the nearest folder above the page with dist/) · --chrome PATH
@@ -44,8 +45,8 @@ export function formatMusic(sources) {
44
45
  out.push(` music ${s.layer} · ${num(m.bpm)} bpm · ${num(m.beats)} beats = ${num(m.bars)} bars of ${m.meter} beats${assumed} · ${num(Math.round(m.seconds * 10) / 10)} s (+ ${num(m.tail)} s tail)`);
45
46
  for (const t of m.tracks) {
46
47
  const whole = Math.abs(t.bars - Math.round(t.bars)) < 1e-6;
47
- // a track shorter than the longest stops early (whatever its length in bars); a short BAR can only be the longest track's, and only the author's meter can say so
48
- const note = t.beats < m.beats - 1e-6 ? ` (ends early: silent from beat ${num(t.beats)})` : !whole && m.meterSet !== false ? ' (not a whole number of bars: a bar is short)' : '';
48
+ // a track shorter than the longest stops early (whatever its length in bars; decimal note lengths add up with float noise, so a gap under 1e-4 of a beat is none); a short BAR can only be the longest track's, and only the author's meter can say so
49
+ const note = t.beats < m.beats - 1e-4 ? ` (ends early: silent from beat ${num(t.beats)})` : !whole && m.meterSet !== false ? ' (not a whole number of bars: a bar is short)' : '';
49
50
  const cell = names => { const n = names.match(/\[[^\]]*\]\S*|\S+/g) ?? []; return n.length === 0 ? '–' : n.length > 4 ? `${n.slice(0, 4).join(' ')} …` : n.join(' '); };
50
51
  const shown = t.perBar.slice(0, 16).map(cell).join(' | ');
51
52
  out.push(`${pad}${t.inst.padEnd(6)} ${num(t.bars)} bar${t.bars === 1 ? '' : 's'}${note} | ${shown}${t.perBar.length > 16 ? ` | … (${t.perBar.length - 16} more)` : ''}`);
@@ -79,10 +80,10 @@ export function findChrome(env = process.env, exists = fs.existsSync, platform =
79
80
  return candidates.find(c => c && exists(c)) ?? null;
80
81
  }
81
82
 
82
- export const USAGE = 'usage: node ai/tools/check.mjs <page.html> [--out DIR] [--frames N] [--formats mp4,webm] [--no-export] [--draft] [--at 3.5,title@end] [--onion 1:3] [--query a=1&b=2] [--var name=value] [--size 1920x1080,1080x1920] [--timeout S] [--root DIR] [--chrome PATH] [--strict] [--help]\n(the header of ai/tools/check.mjs explains each option)';
83
+ export const USAGE = 'usage: node ai/tools/check.mjs <page.html> [--out DIR] [--frames N] [--formats mp4,webm] [--no-export] [--draft] [--at 3.5,title@end] [--crop X,Y,W,H] [--onion 1:3] [--query a=1&b=2] [--var name=value] [--size 1920x1080,1080x1920] [--timeout S] [--root DIR] [--chrome PATH] [--strict] [--help]\n(the header of ai/tools/check.mjs explains each option)';
83
84
 
84
85
  export function parseArgs(argv) {
85
- const o = { page: null, out: null, frames: 12, formats: ['mp4'], export: true, timeout: 240, root: null, chrome: null, strict: false, at: null, draft: false, onion: null, query: null, vars: [], sizes: [], help: false };
86
+ const o = { page: null, out: null, frames: 12, formats: ['mp4'], export: true, timeout: 240, root: null, chrome: null, strict: false, at: null, crop: null, draft: false, onion: null, query: null, vars: [], sizes: [], help: false };
86
87
  const need = (i, name) => { if (i + 1 >= argv.length) throw new Error(`${name} needs a value`); return argv[i + 1]; };
87
88
  for (let i = 0; i < argv.length; i++) {
88
89
  const a = argv[i];
@@ -95,6 +96,11 @@ export function parseArgs(argv) {
95
96
  else if (a === '--draft') o.draft = true;
96
97
  else if (a === '--query') o.query = need(i++, a).replace(/^\?/, '');
97
98
  else if (a === '--at') o.at = need(i++, a);
99
+ else if (a === '--crop') {
100
+ const m = need(i++, a).split(',').map(Number);
101
+ if (m.length !== 4 || !m.every(Number.isFinite) || m[0] < 0 || m[1] < 0 || m[2] < 1 || m[3] < 1) throw new Error(`--crop must look like 100,200,400,300 (x, y, width, height in canvas pixels, width and height at least 1), got "${argv[i]}"`);
102
+ o.crop = { x: Math.round(m[0]), y: Math.round(m[1]), width: Math.round(m[2]), height: Math.round(m[3]) };
103
+ }
98
104
  else if (a === '--var') o.vars.push(parseVar(need(i++, a)));
99
105
  else if (a === '--size') o.sizes.push(...parseSizes(need(i++, a)));
100
106
  else if (a === '--onion') {
@@ -109,6 +115,7 @@ export function parseArgs(argv) {
109
115
  else if (!o.page) o.page = a;
110
116
  else throw new Error(`unexpected argument ${a}`);
111
117
  }
118
+ if (o.crop && !o.at) throw new Error('--crop crops the pictures of --at: give --at too (for example --at 3.5 --crop 100,200,400,300)');
112
119
  return o;
113
120
  }
114
121
 
@@ -377,6 +384,7 @@ export async function runCheck(opts, log = console.log) {
377
384
  if (r.error) throw new Error(r.error.replace(/^pixi-effects: /, ''));
378
385
  atList = r.at;
379
386
  }
387
+ if (opts.crop && (opts.crop.x + opts.crop.width > info.width || opts.crop.y + opts.crop.height > info.height)) throw new Error(`--crop ${opts.crop.x},${opts.crop.y},${opts.crop.width},${opts.crop.height} goes past the canvas (${info.width}×${info.height})`);
380
388
  if (opts.onion && opts.onion[1] > info.duration + 1e-9) throw new Error(`--onion ${opts.onion[0]}:${opts.onion[1]} goes past the end: the movie is ${info.duration} s`);
381
389
 
382
390
  const rv = await cdp.eval(`movie.review(${JSON.stringify({ strict: !!opts.strict })})`);
@@ -398,8 +406,16 @@ export async function runCheck(opts, log = console.log) {
398
406
  const wanted = atList;
399
407
  const framesDir = path.join(outDir, 'frames');
400
408
  fs.mkdirSync(framesDir, { recursive: true });
409
+ for (const old of fs.readdirSync(framesDir)) if (old.endsWith('.png')) fs.rmSync(path.join(framesDir, old)); // the pictures of an earlier run: frames/ shows this run's moments only
410
+ // --crop: that part of the canvas, enlarged by a whole factor (up to 4x, the longer side to about 800 px) with no smoothing, so small figures and fine lines can be judged
411
+ const crop = opts.crop ? { ...opts.crop, scale: Math.max(1, Math.min(4, Math.floor(800 / Math.max(opts.crop.width, opts.crop.height)))) } : null;
412
+ const cropped = [];
401
413
  for (const w of wanted) {
402
- const png = await cdp.eval(`movie.snapshot(${w.frame}, { as: 'dataURL' })`);
414
+ let png = await cdp.eval(`movie.snapshot(${w.frame}, { as: 'dataURL' })`);
415
+ if (crop) {
416
+ png = await cdp.eval(`(async (url, c) => { const img = new Image(); img.src = url; await img.decode(); const cv = document.createElement('canvas'); cv.width = c.width * c.scale; cv.height = c.height * c.scale; const g = cv.getContext('2d'); g.imageSmoothingEnabled = false; g.drawImage(img, c.x, c.y, c.width, c.height, 0, 0, cv.width, cv.height); return cv.toDataURL('image/png'); })(${JSON.stringify(png)}, ${JSON.stringify(crop)})`);
417
+ cropped.push(png);
418
+ }
403
419
  fs.writeFileSync(path.join(framesDir, `${w.label}.png`), Buffer.from(png.slice(png.indexOf(',') + 1), 'base64'));
404
420
  // depth of field: the blur (px) each threeD layer has at this moment, so a focus pull can be read as numbers (0 = sharp)
405
421
  const rows = await cdp.eval(`movie.inspect(${w.frame}).then(r => r.layers.filter(l => l.depthBlur !== undefined).map(l => [l.path, l.depthBlur]))`).catch(() => []);
@@ -408,12 +424,15 @@ export async function runCheck(opts, log = console.log) {
408
424
  const lit = await cdp.eval(`movie.inspect(${w.frame}).then(r => r.layers.filter(l => l.light !== undefined).map(l => [l.path, l.light]))`).catch(() => []);
409
425
  if (lit.length) w.light = Object.fromEntries(lit);
410
426
  }
411
- const atSheet = await cdp.eval(`movie.contactSheet({ frames: ${JSON.stringify(wanted.map(w => w.frame))}, as: 'dataURL' })`);
427
+ const atSheet = crop
428
+ ? await cdp.eval(`(async (urls) => { const imgs = await Promise.all(urls.map(async u => { const i = new Image(); i.src = u; await i.decode(); return i; })); const cols = Math.min(imgs.length, 3), rows = Math.ceil(imgs.length / cols), w = imgs[0].width, h = imgs[0].height; const cv = document.createElement('canvas'); cv.width = cols * w; cv.height = rows * h; const g = cv.getContext('2d'); imgs.forEach((im, k) => g.drawImage(im, (k % cols) * w, Math.floor(k / cols) * h)); return cv.toDataURL('image/png'); })(${JSON.stringify(cropped)})`)
429
+ : await cdp.eval(`movie.contactSheet({ frames: ${JSON.stringify(wanted.map(w => w.frame))}, as: 'dataURL' })`);
412
430
  const atFile = path.join(outDir, 'at.png');
413
431
  fs.writeFileSync(atFile, Buffer.from(atSheet.slice(atSheet.indexOf(',') + 1), 'base64'));
414
432
  report.files.at = shown(atFile);
415
433
  report.files.frames = shown(framesDir);
416
434
  report.at = wanted;
435
+ if (crop) report.crop = crop;
417
436
  }
418
437
 
419
438
  // --onion A:B: one picture of the movement between A and B seconds (8 frames overlaid, the later the stronger)
@@ -105,9 +105,19 @@ export function searchDocs(root, word) {
105
105
  const heading = s.heading.toLowerCase();
106
106
  const body = s.lines.join('\n').toLowerCase();
107
107
  if (!tokens.every(t => body.includes(t))) continue;
108
- const inHeading = tokens.every(t => heading.includes(t));
108
+ const flat = (x) => x.replace(/[^a-z0-9]/g, ''); // "motionBlur" is the heading "Motion blur"
109
+ const inHeading = tokens.every(t => heading.includes(t) || (flat(t).length > 2 && flat(heading).includes(flat(t))));
109
110
  const count = tokens.reduce((n, t) => n + body.split(t).length - 1, 0);
110
- scored.push({ s, score: (inHeading ? 1000 : 0) + Math.min(count, 50) + bonus });
111
+ // the word as a table row (`| \`path\` | …`, the first cell) or as a call in code (`steps(n)`) says the section is ABOUT it, not that it only mentions it
112
+ const row = tokens.some(t => s.lines.some(l => new RegExp(`^\\|\\s*\\*{0,2}\\x60${t.replace(/[^a-z0-9]/g, '')}\\x60`).test(l.toLowerCase())));
113
+ const calls = (t) => { // the word followed by a bracket INSIDE code: a fenced block or an inline `span` (not prose that has a bracket after it)
114
+ const w = t.replace(/[^a-z0-9]/g, ''), re = new RegExp(`\\b${w}\\s*\\(`);
115
+ const fences = [...body.matchAll(/```[\s\S]*?```/g)].map(m => m[0]);
116
+ const inline = [...body.replace(/```[\s\S]*?```/g, ' ').matchAll(/`([^`\n]+)`/g)].map(m => m[1]);
117
+ return [...fences, ...inline].some(code => re.test(code));
118
+ };
119
+ const call = tokens.some(calls);
120
+ scored.push({ s, score: (inHeading ? 1000 : 0) + (row ? 1100 : 0) + (call ? 300 : 0) + Math.min(count, 50) + bonus });
111
121
  }
112
122
  }
113
123
  scored.sort((a, b) => b.score - a.score);
@@ -155,6 +165,8 @@ export function newPage(name, { cwd = process.cwd(), root = PACKAGE_ROOT, force
155
165
  .replace('<title>pixi-effects video</title>', `<title>${base.replace(/&/g, '&amp;').replace(/</g, '&lt;')}</title>`)
156
166
  // the size sits in two places (the canvas attributes and W / H) and the length in one; the poster follows DURATION
157
167
  .replace('<canvas id="stage" width="1280" height="720">', size ? `<canvas id="stage" width="${size.width}" height="${size.height}">` : '<canvas id="stage" width="1280" height="720">')
168
+ // a tall or square page would be 960 px wide and far taller than a laptop screen: its width follows a height of about 760 px
169
+ .replace('.stage { position: relative; width: min(960px, 100%); }', size ? `.stage { position: relative; width: min(${Math.min(960, Math.round(760 * size.width / size.height))}px, 100%); }` : '.stage { position: relative; width: min(960px, 100%); }')
158
170
  .replace('const W = 1280, H = 720, FPS = 30, DURATION = 6;', `const W = ${size ? size.width : 1280}, H = ${size ? size.height : 720}, FPS = 30, DURATION = ${duration ?? 6};`);
159
171
  fs.writeFileSync(file, html);
160
172
  return { file };
@@ -1,5 +1,5 @@
1
1
  import { Container } from 'pixi.js';
2
- import { S as SequenceSpec, C as CompositionShape } from './types-CavnUvd5.js';
2
+ import { S as SequenceSpec, C as CompositionShape } from './types-B6zDTU2V.js';
3
3
 
4
4
  /**
5
5
  * Resolves a routed keyframe path to a concrete tween target.
@@ -1,5 +1,5 @@
1
1
  import { Container } from 'pixi.js';
2
- import { S as SequenceSpec, C as CompositionShape } from './types-CavnUvd5.cjs';
2
+ import { S as SequenceSpec, C as CompositionShape } from './types-B6zDTU2V.cjs';
3
3
 
4
4
  /**
5
5
  * Resolves a routed keyframe path to a concrete tween target.
@@ -0,0 +1,7 @@
1
+ export { CompositionSequence } from './chunk-AZ6AINSW.js';
2
+ import './chunk-G3DX65HO.js';
3
+ import './chunk-WIIU42FH.js';
4
+ import './chunk-2JFFKHXO.js';
5
+ import './chunk-BXOCEZ4U.js';
6
+ //# sourceMappingURL=Composition-BMRCE5FH.js.map
7
+ //# sourceMappingURL=Composition-BMRCE5FH.js.map
@@ -1 +1 @@
1
- {"version":3,"sources":[],"names":[],"mappings":"","file":"Composition-NDXKQHB4.js"}
1
+ {"version":3,"sources":[],"names":[],"mappings":"","file":"Composition-BMRCE5FH.js"}
@@ -0,0 +1,16 @@
1
+ 'use strict';
2
+
3
+ var chunkY5UWFH6Y_cjs = require('./chunk-Y5UWFH6Y.cjs');
4
+ require('./chunk-KJSEQVBV.cjs');
5
+ require('./chunk-NEUPEENI.cjs');
6
+ require('./chunk-42ML5AKN.cjs');
7
+ require('./chunk-TWZNOUM7.cjs');
8
+
9
+
10
+
11
+ Object.defineProperty(exports, "CompositionSequence", {
12
+ enumerable: true,
13
+ get: function () { return chunkY5UWFH6Y_cjs.CompositionSequence; }
14
+ });
15
+ //# sourceMappingURL=Composition-XRLYR6GL.cjs.map
16
+ //# sourceMappingURL=Composition-XRLYR6GL.cjs.map
@@ -1 +1 @@
1
- {"version":3,"sources":[],"names":[],"mappings":"","file":"Composition-OLH7RC5Z.cjs"}
1
+ {"version":3,"sources":[],"names":[],"mappings":"","file":"Composition-XRLYR6GL.cjs"}
@@ -1,6 +1,6 @@
1
- import { l as Movie } from './Movie-Db4QnMIH.cjs';
1
+ import { l as Movie } from './Movie-Bh1HPdKw.cjs';
2
2
  import 'pixi.js';
3
- import './types-CavnUvd5.cjs';
3
+ import './types-B6zDTU2V.cjs';
4
4
 
5
5
  /**
6
6
  * The look of the bar. Each key sets a CSS custom property on the bar (so you can also set `--mc-accent` … in your own CSS, on the
@@ -1,6 +1,6 @@
1
- import { l as Movie } from './Movie-VYtUN4TS.js';
1
+ import { l as Movie } from './Movie-D5WFfZdZ.js';
2
2
  import 'pixi.js';
3
- import './types-CavnUvd5.js';
3
+ import './types-B6zDTU2V.js';
4
4
 
5
5
  /**
6
6
  * The look of the bar. Each key sets a CSS custom property on the bar (so you can also set `--mc-accent` … in your own CSS, on the
@@ -1,5 +1,5 @@
1
1
  import { Application } from 'pixi.js';
2
- import { Z as Stop, c as AssetSpec, k as CompositionSpec } from './types-CavnUvd5.js';
2
+ import { Z as Stop, c as AssetSpec, k as CompositionSpec } from './types-B6zDTU2V.cjs';
3
3
 
4
4
  type EaseFn = (p: number) => number;
5
5
 
@@ -232,6 +232,11 @@ interface AudioSourceReport {
232
232
  /** Loudest sample of the MIX while this source plays, dBFS (−120 = silence). */
233
233
  peakDb: number;
234
234
  sound: SoundMeasure;
235
+ /**
236
+ * How far below the loudest sample of the MIX around its own loudest moment this source is, dB (0 = it is the loudest thing there; 20 = the mix is 20 dB louder, so
237
+ * the source is hardly heard). Absent for a sound inside a remapped composition (its time is not the movie's).
238
+ */
239
+ belowMixDb?: number;
235
240
  /** For a `music` layer: its score read back (bars per track, what starts in each bar, the drum spans). */
236
241
  music?: MusicSummary;
237
242
  }
@@ -1,5 +1,5 @@
1
1
  import { Application } from 'pixi.js';
2
- import { Z as Stop, c as AssetSpec, k as CompositionSpec } from './types-CavnUvd5.cjs';
2
+ import { Z as Stop, c as AssetSpec, k as CompositionSpec } from './types-B6zDTU2V.js';
3
3
 
4
4
  type EaseFn = (p: number) => number;
5
5
 
@@ -232,6 +232,11 @@ interface AudioSourceReport {
232
232
  /** Loudest sample of the MIX while this source plays, dBFS (−120 = silence). */
233
233
  peakDb: number;
234
234
  sound: SoundMeasure;
235
+ /**
236
+ * How far below the loudest sample of the MIX around its own loudest moment this source is, dB (0 = it is the loudest thing there; 20 = the mix is 20 dB louder, so
237
+ * the source is hardly heard). Absent for a sound inside a remapped composition (its time is not the movie's).
238
+ */
239
+ belowMixDb?: number;
235
240
  /** For a `music` layer: its score read back (bars per track, what starts in each bar, the drum spans). */
236
241
  music?: MusicSummary;
237
242
  }
@@ -1,9 +1,9 @@
1
1
  'use strict';
2
2
 
3
- var chunk5IJENZOG_cjs = require('./chunk-5IJENZOG.cjs');
4
- var chunkLEECUCTG_cjs = require('./chunk-LEECUCTG.cjs');
5
- require('./chunk-IMQJVRMT.cjs');
6
- require('./chunk-GL2AQFLQ.cjs');
3
+ var chunkY5UWFH6Y_cjs = require('./chunk-Y5UWFH6Y.cjs');
4
+ var chunkKJSEQVBV_cjs = require('./chunk-KJSEQVBV.cjs');
5
+ require('./chunk-NEUPEENI.cjs');
6
+ require('./chunk-42ML5AKN.cjs');
7
7
  require('./chunk-TWZNOUM7.cjs');
8
8
 
9
9
  // src/sequences/Spans.ts
@@ -12,12 +12,12 @@ var strip = (props) => {
12
12
  const { visibleChars: _gone, ...rest } = props;
13
13
  return rest;
14
14
  };
15
- var SpansSequence = class extends chunk5IJENZOG_cjs.CompositionSequence {
15
+ var SpansSequence = class extends chunkY5UWFH6Y_cjs.CompositionSequence {
16
16
  _meta;
17
17
  _count = { value: 0, visibleChars: 0 };
18
18
  constructor(spec, parent, root) {
19
19
  super(spec, parent, root);
20
- this._meta = chunkLEECUCTG_cjs.spansMeta.get(spec);
20
+ this._meta = chunkKJSEQVBV_cjs.spansMeta.get(spec);
21
21
  }
22
22
  /** `visibleChars` is the group's own counter, not a property to write on the container. */
23
23
  displayProps() {
@@ -33,8 +33,8 @@ var SpansSequence = class extends chunk5IJENZOG_cjs.CompositionSequence {
33
33
  if (!this._meta?.typed) return;
34
34
  const scope = this.scope();
35
35
  const first = this.spec.initial?.visibleChars;
36
- this._count.visibleChars = first === void 0 ? 0 : chunkLEECUCTG_cjs.resolveNumber(first, scope);
37
- chunkLEECUCTG_cjs.bindCounterKeyframes(timeline, this._count, "visibleChars", () => this._distribute(), this.spec.keyframes ?? [], this.duration, offset + this.at, scope);
36
+ this._count.visibleChars = first === void 0 ? 0 : chunkKJSEQVBV_cjs.resolveNumber(first, scope);
37
+ chunkKJSEQVBV_cjs.bindCounterKeyframes(timeline, this._count, "visibleChars", () => this._distribute(), this.spec.keyframes ?? [], this.duration, offset + this.at, scope);
38
38
  this._distribute();
39
39
  }
40
40
  /** Every piece shows its part of the first N characters of the whole text. */
@@ -43,7 +43,7 @@ var SpansSequence = class extends chunk5IJENZOG_cjs.CompositionSequence {
43
43
  const total = Math.max(0, Math.floor(this._count.visibleChars));
44
44
  let i = 0;
45
45
  for (const child of this._children) {
46
- if (!(child instanceof chunkLEECUCTG_cjs.TextSequence)) continue;
46
+ if (!(child instanceof chunkKJSEQVBV_cjs.TextSequence)) continue;
47
47
  const p = pieces[i++];
48
48
  if (p) child.setVisibleChars(Math.min(p.length, Math.max(0, total - p.start)));
49
49
  }
@@ -51,5 +51,5 @@ var SpansSequence = class extends chunk5IJENZOG_cjs.CompositionSequence {
51
51
  };
52
52
 
53
53
  exports.SpansSequence = SpansSequence;
54
- //# sourceMappingURL=Spans-QRMN2EZO.cjs.map
55
- //# sourceMappingURL=Spans-QRMN2EZO.cjs.map
54
+ //# sourceMappingURL=Spans-4IRBSOLI.cjs.map
55
+ //# sourceMappingURL=Spans-4IRBSOLI.cjs.map
@@ -1 +1 @@
1
- {"version":3,"sources":["../src/sequences/Spans.ts"],"names":["CompositionSequence","spansMeta","resolveNumber","bindCounterKeyframes","TextSequence"],"mappings":";;;;;;;;;AAQA,IAAM,KAAA,GAAQ,CAAC,KAAA,KAAgD;AAC7D,EAAA,IAAI,CAAC,KAAA,IAAS,EAAE,cAAA,IAAkB,QAAQ,OAAO,KAAA;AACjD,EAAA,MAAM,EAAE,YAAA,EAAc,KAAA,EAAO,GAAG,MAAK,GAAI,KAAA;AACzC,EAAA,OAAO,IAAA;AACT,CAAA;AAEO,IAAM,aAAA,GAAN,cAA4BA,qCAAA,CAAoB;AAAA,EACpC,KAAA;AAAA,EACA,MAAA,GAAuB,EAAE,KAAA,EAAO,CAAA,EAAG,cAAc,CAAA,EAAE;AAAA,EAEpE,WAAA,CAAY,IAAA,EAAoB,MAAA,EAAiC,IAAA,EAAwB;AACvF,IAAA,KAAA,CAAM,IAAA,EAAM,QAAQ,IAAI,CAAA;AACxB,IAAA,IAAA,CAAK,KAAA,GAAQC,2BAAA,CAAU,GAAA,CAAI,IAAI,CAAA;AAAA,EACjC;AAAA;AAAA,EAGmB,YAAA,GAAyH;AAC1I,IAAA,MAAM,CAAA,GAAI,MAAM,YAAA,EAAa;AAC7B,IAAA,IAAI,CAAC,IAAA,CAAK,KAAA,EAAO,KAAA,EAAO,OAAO,CAAA;AAC/B,IAAA,OAAO;AAAA,MACL,OAAA,EAAS,KAAA,CAAM,CAAA,CAAE,OAA4B,CAAA;AAAA,MAC7C,SAAA,EAAW,EAAE,SAAA,EAAW,GAAA,CAAI,SAAO,EAAE,GAAG,EAAA,EAAI,GAAA,EAAK,KAAA,CAAM,EAAA,CAAG,GAAG,CAAA,EAAG,EAAA,EAAI,KAAA,CAAM,EAAA,CAAG,EAAE,CAAA,EAAG,MAAM,KAAA,CAAM,EAAA,CAAG,IAAI,CAAA,EAAE,CAAE;AAAA,KAC3G;AAAA,EACF;AAAA,EAES,YAAA,CAAa,QAAA,EAAoB,MAAA,GAAS,CAAA,EAAS;AAC1D,IAAA,KAAA,CAAM,YAAA,CAAa,UAAU,MAAM,CAAA;AACnC,IAAA,IAAI,CAAC,IAAA,CAAK,KAAA,EAAO,KAAA,EAAO;AACxB,IAAA,MAAM,KAAA,GAAQ,KAAK,KAAA,EAAM;AACzB,IAAA,MAAM,KAAA,GAAS,IAAA,CAAK,IAAA,CAAK,OAAA,EAAiD,YAAA;AAC1E,IAAA,IAAA,CAAK,OAAO,YAAA,GAAe,KAAA,KAAU,SAAY,CAAA,GAAIC,+BAAA,CAAc,OAAO,KAAK,CAAA;AAC/E,IAAAC,sCAAA,CAAqB,UAAU,IAAA,CAAK,MAAA,EAAQ,gBAAgB,MAAM,IAAA,CAAK,aAAY,EAAG,IAAA,CAAK,IAAA,CAAK,SAAA,IAAa,EAAC,EAAG,IAAA,CAAK,UAAW,MAAA,GAAS,IAAA,CAAK,IAAI,KAAK,CAAA;AACxJ,IAAA,IAAA,CAAK,WAAA,EAAY;AAAA,EACnB;AAAA;AAAA,EAGQ,WAAA,GAAoB;AAC1B,IAAA,MAAM,MAAA,GAAS,KAAK,KAAA,CAAO,MAAA;AAC3B,IAAA,MAAM,KAAA,GAAQ,KAAK,GAAA,CAAI,CAAA,EAAG,KAAK,KAAA,CAAM,IAAA,CAAK,MAAA,CAAO,YAAY,CAAC,CAAA;AAC9D,IAAA,IAAI,CAAA,GAAI,CAAA;AACR,IAAA,KAAA,MAAW,KAAA,IAAS,KAAK,SAAA,EAAW;AAClC,MAAA,IAAI,EAAE,iBAAiBC,8BAAA,CAAA,EAAe;AACtC,MAAA,MAAM,CAAA,GAAI,OAAO,CAAA,EAAG,CAAA;AACpB,MAAA,IAAI,CAAA,EAAG,KAAA,CAAM,eAAA,CAAgB,IAAA,CAAK,IAAI,CAAA,CAAE,MAAA,EAAQ,IAAA,CAAK,GAAA,CAAI,CAAA,EAAG,KAAA,GAAQ,CAAA,CAAE,KAAK,CAAC,CAAC,CAAA;AAAA,IAC/E;AAAA,EACF;AACF","file":"Spans-QRMN2EZO.cjs","sourcesContent":["// The sequence behind a text layer with `spans`: a composition of ordinary text layers (see src/text/spans.ts) that also owns the typewriter\n// (`visibleChars`) of the whole text: it counts over every character of every piece, and tells each piece how many of its own to show.\nimport { CompositionSequence } from './Composition';\nimport { TextSequence, bindCounterKeyframes, resolveNumber, type CounterState } from './Text';\nimport { spansMeta, type SpansMeta } from '../text/spans';\nimport type { CompositionShape, SequenceSpec, Props } from '../types';\nimport type { Timeline } from '../core/timelineEngine';\n\nconst strip = (props: Props | undefined): Props | undefined => {\n if (!props || !('visibleChars' in props)) return props;\n const { visibleChars: _gone, ...rest } = props as Record<string, unknown>;\n return rest as unknown as Props;\n};\n\nexport class SpansSequence extends CompositionSequence {\n private readonly _meta: SpansMeta | undefined;\n private readonly _count: CounterState = { value: 0, visibleChars: 0 };\n\n constructor(spec: SequenceSpec, parent: CompositionShape | null, root: CompositionShape) {\n super(spec, parent, root);\n this._meta = spansMeta.get(spec);\n }\n\n /** `visibleChars` is the group's own counter, not a property to write on the container. */\n protected override displayProps(): { initial: CompositionSequence['spec']['initial']; keyframes: CompositionSequence['spec']['keyframes'] } {\n const p = super.displayProps();\n if (!this._meta?.typed) return p;\n return {\n initial: strip(p.initial as Props | undefined) as never,\n keyframes: p.keyframes?.map(kf => ({ ...kf, set: strip(kf.set), to: strip(kf.to), from: strip(kf.from) })),\n };\n }\n\n override bindTimeline(timeline: Timeline, offset = 0): void {\n super.bindTimeline(timeline, offset);\n if (!this._meta?.typed) return;\n const scope = this.scope();\n const first = (this.spec.initial as Record<string, unknown> | undefined)?.visibleChars;\n this._count.visibleChars = first === undefined ? 0 : resolveNumber(first, scope); // like a text: it starts at 0 once it is animated\n bindCounterKeyframes(timeline, this._count, 'visibleChars', () => this._distribute(), this.spec.keyframes ?? [], this.duration!, offset + this.at, scope);\n this._distribute();\n }\n\n /** Every piece shows its part of the first N characters of the whole text. */\n private _distribute(): void {\n const pieces = this._meta!.pieces;\n const total = Math.max(0, Math.floor(this._count.visibleChars));\n let i = 0;\n for (const child of this._children) {\n if (!(child instanceof TextSequence)) continue;\n const p = pieces[i++];\n if (p) child.setVisibleChars(Math.min(p.length, Math.max(0, total - p.start)));\n }\n }\n}\n"]}
1
+ {"version":3,"sources":["../src/sequences/Spans.ts"],"names":["CompositionSequence","spansMeta","resolveNumber","bindCounterKeyframes","TextSequence"],"mappings":";;;;;;;;;AAQA,IAAM,KAAA,GAAQ,CAAC,KAAA,KAAgD;AAC7D,EAAA,IAAI,CAAC,KAAA,IAAS,EAAE,cAAA,IAAkB,QAAQ,OAAO,KAAA;AACjD,EAAA,MAAM,EAAE,YAAA,EAAc,KAAA,EAAO,GAAG,MAAK,GAAI,KAAA;AACzC,EAAA,OAAO,IAAA;AACT,CAAA;AAEO,IAAM,aAAA,GAAN,cAA4BA,qCAAA,CAAoB;AAAA,EACpC,KAAA;AAAA,EACA,MAAA,GAAuB,EAAE,KAAA,EAAO,CAAA,EAAG,cAAc,CAAA,EAAE;AAAA,EAEpE,WAAA,CAAY,IAAA,EAAoB,MAAA,EAAiC,IAAA,EAAwB;AACvF,IAAA,KAAA,CAAM,IAAA,EAAM,QAAQ,IAAI,CAAA;AACxB,IAAA,IAAA,CAAK,KAAA,GAAQC,2BAAA,CAAU,GAAA,CAAI,IAAI,CAAA;AAAA,EACjC;AAAA;AAAA,EAGmB,YAAA,GAAyH;AAC1I,IAAA,MAAM,CAAA,GAAI,MAAM,YAAA,EAAa;AAC7B,IAAA,IAAI,CAAC,IAAA,CAAK,KAAA,EAAO,KAAA,EAAO,OAAO,CAAA;AAC/B,IAAA,OAAO;AAAA,MACL,OAAA,EAAS,KAAA,CAAM,CAAA,CAAE,OAA4B,CAAA;AAAA,MAC7C,SAAA,EAAW,EAAE,SAAA,EAAW,GAAA,CAAI,SAAO,EAAE,GAAG,EAAA,EAAI,GAAA,EAAK,KAAA,CAAM,EAAA,CAAG,GAAG,CAAA,EAAG,EAAA,EAAI,KAAA,CAAM,EAAA,CAAG,EAAE,CAAA,EAAG,MAAM,KAAA,CAAM,EAAA,CAAG,IAAI,CAAA,EAAE,CAAE;AAAA,KAC3G;AAAA,EACF;AAAA,EAES,YAAA,CAAa,QAAA,EAAoB,MAAA,GAAS,CAAA,EAAS;AAC1D,IAAA,KAAA,CAAM,YAAA,CAAa,UAAU,MAAM,CAAA;AACnC,IAAA,IAAI,CAAC,IAAA,CAAK,KAAA,EAAO,KAAA,EAAO;AACxB,IAAA,MAAM,KAAA,GAAQ,KAAK,KAAA,EAAM;AACzB,IAAA,MAAM,KAAA,GAAS,IAAA,CAAK,IAAA,CAAK,OAAA,EAAiD,YAAA;AAC1E,IAAA,IAAA,CAAK,OAAO,YAAA,GAAe,KAAA,KAAU,SAAY,CAAA,GAAIC,+BAAA,CAAc,OAAO,KAAK,CAAA;AAC/E,IAAAC,sCAAA,CAAqB,UAAU,IAAA,CAAK,MAAA,EAAQ,gBAAgB,MAAM,IAAA,CAAK,aAAY,EAAG,IAAA,CAAK,IAAA,CAAK,SAAA,IAAa,EAAC,EAAG,IAAA,CAAK,UAAW,MAAA,GAAS,IAAA,CAAK,IAAI,KAAK,CAAA;AACxJ,IAAA,IAAA,CAAK,WAAA,EAAY;AAAA,EACnB;AAAA;AAAA,EAGQ,WAAA,GAAoB;AAC1B,IAAA,MAAM,MAAA,GAAS,KAAK,KAAA,CAAO,MAAA;AAC3B,IAAA,MAAM,KAAA,GAAQ,KAAK,GAAA,CAAI,CAAA,EAAG,KAAK,KAAA,CAAM,IAAA,CAAK,MAAA,CAAO,YAAY,CAAC,CAAA;AAC9D,IAAA,IAAI,CAAA,GAAI,CAAA;AACR,IAAA,KAAA,MAAW,KAAA,IAAS,KAAK,SAAA,EAAW;AAClC,MAAA,IAAI,EAAE,iBAAiBC,8BAAA,CAAA,EAAe;AACtC,MAAA,MAAM,CAAA,GAAI,OAAO,CAAA,EAAG,CAAA;AACpB,MAAA,IAAI,CAAA,EAAG,KAAA,CAAM,eAAA,CAAgB,IAAA,CAAK,IAAI,CAAA,CAAE,MAAA,EAAQ,IAAA,CAAK,GAAA,CAAI,CAAA,EAAG,KAAA,GAAQ,CAAA,CAAE,KAAK,CAAC,CAAC,CAAA;AAAA,IAC/E;AAAA,EACF;AACF","file":"Spans-4IRBSOLI.cjs","sourcesContent":["// The sequence behind a text layer with `spans`: a composition of ordinary text layers (see src/text/spans.ts) that also owns the typewriter\n// (`visibleChars`) of the whole text: it counts over every character of every piece, and tells each piece how many of its own to show.\nimport { CompositionSequence } from './Composition';\nimport { TextSequence, bindCounterKeyframes, resolveNumber, type CounterState } from './Text';\nimport { spansMeta, type SpansMeta } from '../text/spans';\nimport type { CompositionShape, SequenceSpec, Props } from '../types';\nimport type { Timeline } from '../core/timelineEngine';\n\nconst strip = (props: Props | undefined): Props | undefined => {\n if (!props || !('visibleChars' in props)) return props;\n const { visibleChars: _gone, ...rest } = props as Record<string, unknown>;\n return rest as unknown as Props;\n};\n\nexport class SpansSequence extends CompositionSequence {\n private readonly _meta: SpansMeta | undefined;\n private readonly _count: CounterState = { value: 0, visibleChars: 0 };\n\n constructor(spec: SequenceSpec, parent: CompositionShape | null, root: CompositionShape) {\n super(spec, parent, root);\n this._meta = spansMeta.get(spec);\n }\n\n /** `visibleChars` is the group's own counter, not a property to write on the container. */\n protected override displayProps(): { initial: CompositionSequence['spec']['initial']; keyframes: CompositionSequence['spec']['keyframes'] } {\n const p = super.displayProps();\n if (!this._meta?.typed) return p;\n return {\n initial: strip(p.initial as Props | undefined) as never,\n keyframes: p.keyframes?.map(kf => ({ ...kf, set: strip(kf.set), to: strip(kf.to), from: strip(kf.from) })),\n };\n }\n\n override bindTimeline(timeline: Timeline, offset = 0): void {\n super.bindTimeline(timeline, offset);\n if (!this._meta?.typed) return;\n const scope = this.scope();\n const first = (this.spec.initial as Record<string, unknown> | undefined)?.visibleChars;\n this._count.visibleChars = first === undefined ? 0 : resolveNumber(first, scope); // like a text: it starts at 0 once it is animated\n bindCounterKeyframes(timeline, this._count, 'visibleChars', () => this._distribute(), this.spec.keyframes ?? [], this.duration!, offset + this.at, scope);\n this._distribute();\n }\n\n /** Every piece shows its part of the first N characters of the whole text. */\n private _distribute(): void {\n const pieces = this._meta!.pieces;\n const total = Math.max(0, Math.floor(this._count.visibleChars));\n let i = 0;\n for (const child of this._children) {\n if (!(child instanceof TextSequence)) continue;\n const p = pieces[i++];\n if (p) child.setVisibleChars(Math.min(p.length, Math.max(0, total - p.start)));\n }\n }\n}\n"]}
@@ -1,7 +1,7 @@
1
- import { CompositionSequence } from './chunk-XV4L72FW.js';
2
- import { spansMeta, resolveNumber, bindCounterKeyframes, TextSequence } from './chunk-Y253JQOH.js';
3
- import './chunk-RTJD3ZSW.js';
4
- import './chunk-ITDYFBB6.js';
1
+ import { CompositionSequence } from './chunk-AZ6AINSW.js';
2
+ import { spansMeta, resolveNumber, bindCounterKeyframes, TextSequence } from './chunk-G3DX65HO.js';
3
+ import './chunk-WIIU42FH.js';
4
+ import './chunk-2JFFKHXO.js';
5
5
  import './chunk-BXOCEZ4U.js';
6
6
 
7
7
  // src/sequences/Spans.ts
@@ -49,5 +49,5 @@ var SpansSequence = class extends CompositionSequence {
49
49
  };
50
50
 
51
51
  export { SpansSequence };
52
- //# sourceMappingURL=Spans-QHHWC2PB.js.map
53
- //# sourceMappingURL=Spans-QHHWC2PB.js.map
52
+ //# sourceMappingURL=Spans-MWP2FX3T.js.map
53
+ //# sourceMappingURL=Spans-MWP2FX3T.js.map
@@ -1 +1 @@
1
- {"version":3,"sources":["../src/sequences/Spans.ts"],"names":[],"mappings":";;;;;;;AAQA,IAAM,KAAA,GAAQ,CAAC,KAAA,KAAgD;AAC7D,EAAA,IAAI,CAAC,KAAA,IAAS,EAAE,cAAA,IAAkB,QAAQ,OAAO,KAAA;AACjD,EAAA,MAAM,EAAE,YAAA,EAAc,KAAA,EAAO,GAAG,MAAK,GAAI,KAAA;AACzC,EAAA,OAAO,IAAA;AACT,CAAA;AAEO,IAAM,aAAA,GAAN,cAA4B,mBAAA,CAAoB;AAAA,EACpC,KAAA;AAAA,EACA,MAAA,GAAuB,EAAE,KAAA,EAAO,CAAA,EAAG,cAAc,CAAA,EAAE;AAAA,EAEpE,WAAA,CAAY,IAAA,EAAoB,MAAA,EAAiC,IAAA,EAAwB;AACvF,IAAA,KAAA,CAAM,IAAA,EAAM,QAAQ,IAAI,CAAA;AACxB,IAAA,IAAA,CAAK,KAAA,GAAQ,SAAA,CAAU,GAAA,CAAI,IAAI,CAAA;AAAA,EACjC;AAAA;AAAA,EAGmB,YAAA,GAAyH;AAC1I,IAAA,MAAM,CAAA,GAAI,MAAM,YAAA,EAAa;AAC7B,IAAA,IAAI,CAAC,IAAA,CAAK,KAAA,EAAO,KAAA,EAAO,OAAO,CAAA;AAC/B,IAAA,OAAO;AAAA,MACL,OAAA,EAAS,KAAA,CAAM,CAAA,CAAE,OAA4B,CAAA;AAAA,MAC7C,SAAA,EAAW,EAAE,SAAA,EAAW,GAAA,CAAI,SAAO,EAAE,GAAG,EAAA,EAAI,GAAA,EAAK,KAAA,CAAM,EAAA,CAAG,GAAG,CAAA,EAAG,EAAA,EAAI,KAAA,CAAM,EAAA,CAAG,EAAE,CAAA,EAAG,MAAM,KAAA,CAAM,EAAA,CAAG,IAAI,CAAA,EAAE,CAAE;AAAA,KAC3G;AAAA,EACF;AAAA,EAES,YAAA,CAAa,QAAA,EAAoB,MAAA,GAAS,CAAA,EAAS;AAC1D,IAAA,KAAA,CAAM,YAAA,CAAa,UAAU,MAAM,CAAA;AACnC,IAAA,IAAI,CAAC,IAAA,CAAK,KAAA,EAAO,KAAA,EAAO;AACxB,IAAA,MAAM,KAAA,GAAQ,KAAK,KAAA,EAAM;AACzB,IAAA,MAAM,KAAA,GAAS,IAAA,CAAK,IAAA,CAAK,OAAA,EAAiD,YAAA;AAC1E,IAAA,IAAA,CAAK,OAAO,YAAA,GAAe,KAAA,KAAU,SAAY,CAAA,GAAI,aAAA,CAAc,OAAO,KAAK,CAAA;AAC/E,IAAA,oBAAA,CAAqB,UAAU,IAAA,CAAK,MAAA,EAAQ,gBAAgB,MAAM,IAAA,CAAK,aAAY,EAAG,IAAA,CAAK,IAAA,CAAK,SAAA,IAAa,EAAC,EAAG,IAAA,CAAK,UAAW,MAAA,GAAS,IAAA,CAAK,IAAI,KAAK,CAAA;AACxJ,IAAA,IAAA,CAAK,WAAA,EAAY;AAAA,EACnB;AAAA;AAAA,EAGQ,WAAA,GAAoB;AAC1B,IAAA,MAAM,MAAA,GAAS,KAAK,KAAA,CAAO,MAAA;AAC3B,IAAA,MAAM,KAAA,GAAQ,KAAK,GAAA,CAAI,CAAA,EAAG,KAAK,KAAA,CAAM,IAAA,CAAK,MAAA,CAAO,YAAY,CAAC,CAAA;AAC9D,IAAA,IAAI,CAAA,GAAI,CAAA;AACR,IAAA,KAAA,MAAW,KAAA,IAAS,KAAK,SAAA,EAAW;AAClC,MAAA,IAAI,EAAE,iBAAiB,YAAA,CAAA,EAAe;AACtC,MAAA,MAAM,CAAA,GAAI,OAAO,CAAA,EAAG,CAAA;AACpB,MAAA,IAAI,CAAA,EAAG,KAAA,CAAM,eAAA,CAAgB,IAAA,CAAK,IAAI,CAAA,CAAE,MAAA,EAAQ,IAAA,CAAK,GAAA,CAAI,CAAA,EAAG,KAAA,GAAQ,CAAA,CAAE,KAAK,CAAC,CAAC,CAAA;AAAA,IAC/E;AAAA,EACF;AACF","file":"Spans-QHHWC2PB.js","sourcesContent":["// The sequence behind a text layer with `spans`: a composition of ordinary text layers (see src/text/spans.ts) that also owns the typewriter\n// (`visibleChars`) of the whole text: it counts over every character of every piece, and tells each piece how many of its own to show.\nimport { CompositionSequence } from './Composition';\nimport { TextSequence, bindCounterKeyframes, resolveNumber, type CounterState } from './Text';\nimport { spansMeta, type SpansMeta } from '../text/spans';\nimport type { CompositionShape, SequenceSpec, Props } from '../types';\nimport type { Timeline } from '../core/timelineEngine';\n\nconst strip = (props: Props | undefined): Props | undefined => {\n if (!props || !('visibleChars' in props)) return props;\n const { visibleChars: _gone, ...rest } = props as Record<string, unknown>;\n return rest as unknown as Props;\n};\n\nexport class SpansSequence extends CompositionSequence {\n private readonly _meta: SpansMeta | undefined;\n private readonly _count: CounterState = { value: 0, visibleChars: 0 };\n\n constructor(spec: SequenceSpec, parent: CompositionShape | null, root: CompositionShape) {\n super(spec, parent, root);\n this._meta = spansMeta.get(spec);\n }\n\n /** `visibleChars` is the group's own counter, not a property to write on the container. */\n protected override displayProps(): { initial: CompositionSequence['spec']['initial']; keyframes: CompositionSequence['spec']['keyframes'] } {\n const p = super.displayProps();\n if (!this._meta?.typed) return p;\n return {\n initial: strip(p.initial as Props | undefined) as never,\n keyframes: p.keyframes?.map(kf => ({ ...kf, set: strip(kf.set), to: strip(kf.to), from: strip(kf.from) })),\n };\n }\n\n override bindTimeline(timeline: Timeline, offset = 0): void {\n super.bindTimeline(timeline, offset);\n if (!this._meta?.typed) return;\n const scope = this.scope();\n const first = (this.spec.initial as Record<string, unknown> | undefined)?.visibleChars;\n this._count.visibleChars = first === undefined ? 0 : resolveNumber(first, scope); // like a text: it starts at 0 once it is animated\n bindCounterKeyframes(timeline, this._count, 'visibleChars', () => this._distribute(), this.spec.keyframes ?? [], this.duration!, offset + this.at, scope);\n this._distribute();\n }\n\n /** Every piece shows its part of the first N characters of the whole text. */\n private _distribute(): void {\n const pieces = this._meta!.pieces;\n const total = Math.max(0, Math.floor(this._count.visibleChars));\n let i = 0;\n for (const child of this._children) {\n if (!(child instanceof TextSequence)) continue;\n const p = pieces[i++];\n if (p) child.setVisibleChars(Math.min(p.length, Math.max(0, total - p.start)));\n }\n }\n}\n"]}
1
+ {"version":3,"sources":["../src/sequences/Spans.ts"],"names":[],"mappings":";;;;;;;AAQA,IAAM,KAAA,GAAQ,CAAC,KAAA,KAAgD;AAC7D,EAAA,IAAI,CAAC,KAAA,IAAS,EAAE,cAAA,IAAkB,QAAQ,OAAO,KAAA;AACjD,EAAA,MAAM,EAAE,YAAA,EAAc,KAAA,EAAO,GAAG,MAAK,GAAI,KAAA;AACzC,EAAA,OAAO,IAAA;AACT,CAAA;AAEO,IAAM,aAAA,GAAN,cAA4B,mBAAA,CAAoB;AAAA,EACpC,KAAA;AAAA,EACA,MAAA,GAAuB,EAAE,KAAA,EAAO,CAAA,EAAG,cAAc,CAAA,EAAE;AAAA,EAEpE,WAAA,CAAY,IAAA,EAAoB,MAAA,EAAiC,IAAA,EAAwB;AACvF,IAAA,KAAA,CAAM,IAAA,EAAM,QAAQ,IAAI,CAAA;AACxB,IAAA,IAAA,CAAK,KAAA,GAAQ,SAAA,CAAU,GAAA,CAAI,IAAI,CAAA;AAAA,EACjC;AAAA;AAAA,EAGmB,YAAA,GAAyH;AAC1I,IAAA,MAAM,CAAA,GAAI,MAAM,YAAA,EAAa;AAC7B,IAAA,IAAI,CAAC,IAAA,CAAK,KAAA,EAAO,KAAA,EAAO,OAAO,CAAA;AAC/B,IAAA,OAAO;AAAA,MACL,OAAA,EAAS,KAAA,CAAM,CAAA,CAAE,OAA4B,CAAA;AAAA,MAC7C,SAAA,EAAW,EAAE,SAAA,EAAW,GAAA,CAAI,SAAO,EAAE,GAAG,EAAA,EAAI,GAAA,EAAK,KAAA,CAAM,EAAA,CAAG,GAAG,CAAA,EAAG,EAAA,EAAI,KAAA,CAAM,EAAA,CAAG,EAAE,CAAA,EAAG,MAAM,KAAA,CAAM,EAAA,CAAG,IAAI,CAAA,EAAE,CAAE;AAAA,KAC3G;AAAA,EACF;AAAA,EAES,YAAA,CAAa,QAAA,EAAoB,MAAA,GAAS,CAAA,EAAS;AAC1D,IAAA,KAAA,CAAM,YAAA,CAAa,UAAU,MAAM,CAAA;AACnC,IAAA,IAAI,CAAC,IAAA,CAAK,KAAA,EAAO,KAAA,EAAO;AACxB,IAAA,MAAM,KAAA,GAAQ,KAAK,KAAA,EAAM;AACzB,IAAA,MAAM,KAAA,GAAS,IAAA,CAAK,IAAA,CAAK,OAAA,EAAiD,YAAA;AAC1E,IAAA,IAAA,CAAK,OAAO,YAAA,GAAe,KAAA,KAAU,SAAY,CAAA,GAAI,aAAA,CAAc,OAAO,KAAK,CAAA;AAC/E,IAAA,oBAAA,CAAqB,UAAU,IAAA,CAAK,MAAA,EAAQ,gBAAgB,MAAM,IAAA,CAAK,aAAY,EAAG,IAAA,CAAK,IAAA,CAAK,SAAA,IAAa,EAAC,EAAG,IAAA,CAAK,UAAW,MAAA,GAAS,IAAA,CAAK,IAAI,KAAK,CAAA;AACxJ,IAAA,IAAA,CAAK,WAAA,EAAY;AAAA,EACnB;AAAA;AAAA,EAGQ,WAAA,GAAoB;AAC1B,IAAA,MAAM,MAAA,GAAS,KAAK,KAAA,CAAO,MAAA;AAC3B,IAAA,MAAM,KAAA,GAAQ,KAAK,GAAA,CAAI,CAAA,EAAG,KAAK,KAAA,CAAM,IAAA,CAAK,MAAA,CAAO,YAAY,CAAC,CAAA;AAC9D,IAAA,IAAI,CAAA,GAAI,CAAA;AACR,IAAA,KAAA,MAAW,KAAA,IAAS,KAAK,SAAA,EAAW;AAClC,MAAA,IAAI,EAAE,iBAAiB,YAAA,CAAA,EAAe;AACtC,MAAA,MAAM,CAAA,GAAI,OAAO,CAAA,EAAG,CAAA;AACpB,MAAA,IAAI,CAAA,EAAG,KAAA,CAAM,eAAA,CAAgB,IAAA,CAAK,IAAI,CAAA,CAAE,MAAA,EAAQ,IAAA,CAAK,GAAA,CAAI,CAAA,EAAG,KAAA,GAAQ,CAAA,CAAE,KAAK,CAAC,CAAC,CAAA;AAAA,IAC/E;AAAA,EACF;AACF","file":"Spans-MWP2FX3T.js","sourcesContent":["// The sequence behind a text layer with `spans`: a composition of ordinary text layers (see src/text/spans.ts) that also owns the typewriter\n// (`visibleChars`) of the whole text: it counts over every character of every piece, and tells each piece how many of its own to show.\nimport { CompositionSequence } from './Composition';\nimport { TextSequence, bindCounterKeyframes, resolveNumber, type CounterState } from './Text';\nimport { spansMeta, type SpansMeta } from '../text/spans';\nimport type { CompositionShape, SequenceSpec, Props } from '../types';\nimport type { Timeline } from '../core/timelineEngine';\n\nconst strip = (props: Props | undefined): Props | undefined => {\n if (!props || !('visibleChars' in props)) return props;\n const { visibleChars: _gone, ...rest } = props as Record<string, unknown>;\n return rest as unknown as Props;\n};\n\nexport class SpansSequence extends CompositionSequence {\n private readonly _meta: SpansMeta | undefined;\n private readonly _count: CounterState = { value: 0, visibleChars: 0 };\n\n constructor(spec: SequenceSpec, parent: CompositionShape | null, root: CompositionShape) {\n super(spec, parent, root);\n this._meta = spansMeta.get(spec);\n }\n\n /** `visibleChars` is the group's own counter, not a property to write on the container. */\n protected override displayProps(): { initial: CompositionSequence['spec']['initial']; keyframes: CompositionSequence['spec']['keyframes'] } {\n const p = super.displayProps();\n if (!this._meta?.typed) return p;\n return {\n initial: strip(p.initial as Props | undefined) as never,\n keyframes: p.keyframes?.map(kf => ({ ...kf, set: strip(kf.set), to: strip(kf.to), from: strip(kf.from) })),\n };\n }\n\n override bindTimeline(timeline: Timeline, offset = 0): void {\n super.bindTimeline(timeline, offset);\n if (!this._meta?.typed) return;\n const scope = this.scope();\n const first = (this.spec.initial as Record<string, unknown> | undefined)?.visibleChars;\n this._count.visibleChars = first === undefined ? 0 : resolveNumber(first, scope); // like a text: it starts at 0 once it is animated\n bindCounterKeyframes(timeline, this._count, 'visibleChars', () => this._distribute(), this.spec.keyframes ?? [], this.duration!, offset + this.at, scope);\n this._distribute();\n }\n\n /** Every piece shows its part of the first N characters of the whole text. */\n private _distribute(): void {\n const pieces = this._meta!.pieces;\n const total = Math.max(0, Math.floor(this._count.visibleChars));\n let i = 0;\n for (const child of this._children) {\n if (!(child instanceof TextSequence)) continue;\n const p = pieces[i++];\n if (p) child.setVisibleChars(Math.min(p.length, Math.max(0, total - p.start)));\n }\n }\n}\n"]}
@@ -1,7 +1,7 @@
1
1
  'use strict';
2
2
 
3
- var chunkIMQJVRMT_cjs = require('./chunk-IMQJVRMT.cjs');
4
- require('./chunk-GL2AQFLQ.cjs');
3
+ var chunkNEUPEENI_cjs = require('./chunk-NEUPEENI.cjs');
4
+ require('./chunk-42ML5AKN.cjs');
5
5
  require('./chunk-TWZNOUM7.cjs');
6
6
  var pixi_js = require('pixi.js');
7
7
 
@@ -68,7 +68,7 @@ function make(name, gl, gpu) {
68
68
  var OWN_MODES = [...Object.keys(SEPARABLE), ...Object.keys(NONSEP)];
69
69
  var done = false;
70
70
  function registerBlendModes() {
71
- chunkIMQJVRMT_cjs.setBlendFilterFactory(createBlendFilter);
71
+ chunkNEUPEENI_cjs.setBlendFilterFactory(createBlendFilter);
72
72
  if (done) return;
73
73
  done = true;
74
74
  for (const [name, s] of Object.entries(SEPARABLE)) {
@@ -90,5 +90,5 @@ fn blendRGB(cb: vec3<f32>, cs: vec3<f32>) -> vec3<f32> { ${s.gpu} }`);
90
90
  exports.OWN_MODES = OWN_MODES;
91
91
  exports.createBlendFilter = createBlendFilter;
92
92
  exports.registerBlendModes = registerBlendModes;
93
- //# sourceMappingURL=blendModes-WIUCYIMC.cjs.map
94
- //# sourceMappingURL=blendModes-WIUCYIMC.cjs.map
93
+ //# sourceMappingURL=blendModes-KU7XLV7U.cjs.map
94
+ //# sourceMappingURL=blendModes-KU7XLV7U.cjs.map
@@ -1 +1 @@
1
- {"version":3,"sources":["../src/filters/blendModes.ts"],"names":["BlendModeFilter","ExtensionType","extensions","setBlendFilterFactory"],"mappings":"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","file":"blendModes-WIUCYIMC.cjs","sourcesContent":["import { BlendModeFilter, ExtensionType, extensions } from 'pixi.js';\nimport { setBlendFilterFactory, type BlendFilterLike } from '../core/blend';\n\n/**\n * The W3C compositing blend modes as our own `BlendModeFilter`s, registered on the SAME pixi instance the movie uses (this file imports\n * 'pixi.js' like the rest of the library; `import 'pixi.js/advanced-blend-modes'` would register on its own copy of `extensions` when\n * the page maps pixi.js to an esm.sh bundle, and then nothing is registered and the layer silently draws as `normal`). It also avoids\n * what pixi 8.22's own versions get wrong: the source colour is premultiplied (a half-transparent layer was blended with its darkened\n * colour and then faded again), the backdrop's alpha is ignored, soft-light is inverted on WebGPU (`select` arguments swapped),\n * linear-light and saturation are wrong. Loaded by `Movie.init` only when a layer uses one of these (see `usesAdvancedBlend`).\n */\ntype Sep = { gl: string; gpu: string }; // a separable blend: the body of B(b, s) for one channel\n\nconst SEPARABLE: Record<string, Sep> = {\n overlay: { gl: 'return b <= 0.5 ? 2.0 * b * s : 1.0 - 2.0 * (1.0 - b) * (1.0 - s);', gpu: 'return select(1.0 - 2.0 * (1.0 - b) * (1.0 - s), 2.0 * b * s, b <= 0.5);' },\n 'hard-light': { gl: 'return s <= 0.5 ? 2.0 * b * s : 1.0 - 2.0 * (1.0 - b) * (1.0 - s);', gpu: 'return select(1.0 - 2.0 * (1.0 - b) * (1.0 - s), 2.0 * b * s, s <= 0.5);' },\n 'soft-light': {\n gl: 'if (s <= 0.5) return b - (1.0 - 2.0 * s) * b * (1.0 - b); float d = b <= 0.25 ? ((16.0 * b - 12.0) * b + 4.0) * b : sqrt(b); return b + (2.0 * s - 1.0) * (d - b);',\n gpu: 'let d = select(sqrt(b), ((16.0 * b - 12.0) * b + 4.0) * b, b <= 0.25); return select(b + (2.0 * s - 1.0) * (d - b), b - (1.0 - 2.0 * s) * b * (1.0 - b), s <= 0.5);',\n },\n 'color-dodge': { gl: 'if (b <= 0.0) return 0.0; return s >= 1.0 ? 1.0 : min(1.0, b / (1.0 - s));', gpu: 'if (b <= 0.0) { return 0.0; } return select(min(1.0, b / (1.0 - s)), 1.0, s >= 1.0);' },\n 'color-burn': { gl: 'if (b >= 1.0) return 1.0; return s <= 0.0 ? 0.0 : 1.0 - min(1.0, (1.0 - b) / s);', gpu: 'if (b >= 1.0) { return 1.0; } return select(1.0 - min(1.0, (1.0 - b) / s), 0.0, s <= 0.0);' },\n darken: { gl: 'return min(b, s);', gpu: 'return min(b, s);' },\n lighten: { gl: 'return max(b, s);', gpu: 'return max(b, s);' },\n difference: { gl: 'return abs(b - s);', gpu: 'return abs(b - s);' },\n exclusion: { gl: 'return b + s - 2.0 * b * s;', gpu: 'return b + s - 2.0 * b * s;' },\n 'linear-burn': { gl: 'return max(0.0, b + s - 1.0);', gpu: 'return max(0.0, b + s - 1.0);' },\n};\n\nconst GL_NONSEP = `\nfloat lum(vec3 c) { return 0.3 * c.r + 0.59 * c.g + 0.11 * c.b; }\nvec3 clipColor(vec3 c) { float l = lum(c); float n = min(c.r, min(c.g, c.b)); float x = max(c.r, max(c.g, c.b));\n if (n < 0.0) c = l + (c - l) * l / (l - n); if (x > 1.0) c = l + (c - l) * (1.0 - l) / (x - l); return c; }\nvec3 setLum(vec3 c, float l) { return clipColor(c + (l - lum(c))); }\nfloat sat(vec3 c) { return max(c.r, max(c.g, c.b)) - min(c.r, min(c.g, c.b)); }\nvec3 setSat(vec3 c, float s) { float mx = max(c.r, max(c.g, c.b)); float mn = min(c.r, min(c.g, c.b)); return mx > mn ? (c - mn) * s / (mx - mn) : vec3(0.0); }`;\nconst GPU_NONSEP = `\nfn lum(c: vec3<f32>) -> f32 { return 0.3 * c.r + 0.59 * c.g + 0.11 * c.b; }\nfn clipColor(c0: vec3<f32>) -> vec3<f32> { var c = c0; let l = lum(c); let n = min(c.r, min(c.g, c.b)); let x = max(c.r, max(c.g, c.b));\n if (n < 0.0) { c = l + (c - l) * l / (l - n); } if (x > 1.0) { c = l + (c - l) * (1.0 - l) / (x - l); } return c; }\nfn setLum(c: vec3<f32>, l: f32) -> vec3<f32> { return clipColor(c + (l - lum(c))); }\nfn sat(c: vec3<f32>) -> f32 { return max(c.r, max(c.g, c.b)) - min(c.r, min(c.g, c.b)); }\nfn setSat(c: vec3<f32>, s: f32) -> vec3<f32> { let mx = max(c.r, max(c.g, c.b)); let mn = min(c.r, min(c.g, c.b)); if (mx > mn) { return (c - vec3<f32>(mn)) * s / (mx - mn); } return vec3<f32>(0.0); }`;\nconst NONSEP: Record<string, { gl: string; gpu: string }> = {\n hue: { gl: 'return setLum(setSat(cs, sat(cb)), lum(cb));', gpu: 'return setLum(setSat(cs, sat(cb)), lum(cb));' },\n saturation: { gl: 'return setLum(setSat(cb, sat(cs)), lum(cb));', gpu: 'return setLum(setSat(cb, sat(cs)), lum(cb));' },\n color: { gl: 'return setLum(cs, lum(cb));', gpu: 'return setLum(cs, lum(cb));' },\n luminosity: { gl: 'return setLum(cb, lum(cs));', gpu: 'return setLum(cb, lum(cs));' },\n};\n\nconst GL_MAIN = `\n vec3 cs = front.a > 0.0 ? front.rgb / front.a : vec3(0.0);\n vec3 cb = back.a > 0.0 ? back.rgb / back.a : vec3(0.0);\n vec3 mixed = front.rgb * (1.0 - back.a) + front.a * back.a * blendRGB(cb, cs) + (1.0 - front.a) * back.rgb;\n finalColor = vec4(mixed, blendedAlpha) * uBlend;`;\nconst GPU_MAIN = `\n let cs = select(vec3<f32>(0.0), front.rgb / front.a, front.a > 0.0);\n let cb = select(vec3<f32>(0.0), back.rgb / back.a, back.a > 0.0);\n let mixed = front.rgb * (1.0 - back.a) + front.a * back.a * blendRGB(cb, cs) + (1.0 - front.a) * back.rgb;\n out = vec4<f32>(mixed, blendedAlpha) * blendUniforms.uBlend;`;\n\nconst CLASSES = new Map<string, new () => BlendModeFilter>();\n\n/** A fresh blend filter for a layer that has filters of its own (it goes last in the chain and reads the backdrop). */\nexport function createBlendFilter(name: string): BlendFilterLike | null {\n const C = CLASSES.get(name);\n return C ? (new C() as unknown as BlendFilterLike) : null;\n}\n\nfunction make(name: string, gl: string, gpu: string): void {\n const C = class extends BlendModeFilter {\n static extension = { name, type: ExtensionType.BlendMode };\n constructor() { super({ gl: { functions: gl, main: GL_MAIN }, gpu: { functions: gpu, main: GPU_MAIN } }); }\n };\n CLASSES.set(name, C);\n extensions.add(C as never);\n}\n\nexport const OWN_MODES: readonly string[] = [...Object.keys(SEPARABLE), ...Object.keys(NONSEP)];\n\nlet done = false;\n/** Register the blends (once per page), and give `core/blend.ts` the maker for the filter chain case. */\nexport function registerBlendModes(): void {\n setBlendFilterFactory(createBlendFilter); // every call: the registration below is once per page, the maker is cheap to hand over again\n if (done) return;\n done = true;\n for (const [name, s] of Object.entries(SEPARABLE)) {\n make(name,\n `float B(float b, float s) { ${s.gl} }\\nvec3 blendRGB(vec3 cb, vec3 cs) { return vec3(B(cb.r, cs.r), B(cb.g, cs.g), B(cb.b, cs.b)); }`,\n `fn B(b: f32, s: f32) -> f32 { ${s.gpu} }\\nfn blendRGB(cb: vec3<f32>, cs: vec3<f32>) -> vec3<f32> { return vec3<f32>(B(cb.r, cs.r), B(cb.g, cs.g), B(cb.b, cs.b)); }`);\n }\n for (const [name, s] of Object.entries(NONSEP)) {\n make(name, `${GL_NONSEP}\\nvec3 blendRGB(vec3 cb, vec3 cs) { ${s.gl} }`, `${GPU_NONSEP}\\nfn blendRGB(cb: vec3<f32>, cs: vec3<f32>) -> vec3<f32> { ${s.gpu} }`);\n }\n}\n"]}
1
+ {"version":3,"sources":["../src/filters/blendModes.ts"],"names":["BlendModeFilter","ExtensionType","extensions","setBlendFilterFactory"],"mappings":"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","file":"blendModes-KU7XLV7U.cjs","sourcesContent":["import { BlendModeFilter, ExtensionType, extensions } from 'pixi.js';\nimport { setBlendFilterFactory, type BlendFilterLike } from '../core/blend';\n\n/**\n * The W3C compositing blend modes as our own `BlendModeFilter`s, registered on the SAME pixi instance the movie uses (this file imports\n * 'pixi.js' like the rest of the library; `import 'pixi.js/advanced-blend-modes'` would register on its own copy of `extensions` when\n * the page maps pixi.js to an esm.sh bundle, and then nothing is registered and the layer silently draws as `normal`). It also avoids\n * what pixi 8.22's own versions get wrong: the source colour is premultiplied (a half-transparent layer was blended with its darkened\n * colour and then faded again), the backdrop's alpha is ignored, soft-light is inverted on WebGPU (`select` arguments swapped),\n * linear-light and saturation are wrong. Loaded by `Movie.init` only when a layer uses one of these (see `usesAdvancedBlend`).\n */\ntype Sep = { gl: string; gpu: string }; // a separable blend: the body of B(b, s) for one channel\n\nconst SEPARABLE: Record<string, Sep> = {\n overlay: { gl: 'return b <= 0.5 ? 2.0 * b * s : 1.0 - 2.0 * (1.0 - b) * (1.0 - s);', gpu: 'return select(1.0 - 2.0 * (1.0 - b) * (1.0 - s), 2.0 * b * s, b <= 0.5);' },\n 'hard-light': { gl: 'return s <= 0.5 ? 2.0 * b * s : 1.0 - 2.0 * (1.0 - b) * (1.0 - s);', gpu: 'return select(1.0 - 2.0 * (1.0 - b) * (1.0 - s), 2.0 * b * s, s <= 0.5);' },\n 'soft-light': {\n gl: 'if (s <= 0.5) return b - (1.0 - 2.0 * s) * b * (1.0 - b); float d = b <= 0.25 ? ((16.0 * b - 12.0) * b + 4.0) * b : sqrt(b); return b + (2.0 * s - 1.0) * (d - b);',\n gpu: 'let d = select(sqrt(b), ((16.0 * b - 12.0) * b + 4.0) * b, b <= 0.25); return select(b + (2.0 * s - 1.0) * (d - b), b - (1.0 - 2.0 * s) * b * (1.0 - b), s <= 0.5);',\n },\n 'color-dodge': { gl: 'if (b <= 0.0) return 0.0; return s >= 1.0 ? 1.0 : min(1.0, b / (1.0 - s));', gpu: 'if (b <= 0.0) { return 0.0; } return select(min(1.0, b / (1.0 - s)), 1.0, s >= 1.0);' },\n 'color-burn': { gl: 'if (b >= 1.0) return 1.0; return s <= 0.0 ? 0.0 : 1.0 - min(1.0, (1.0 - b) / s);', gpu: 'if (b >= 1.0) { return 1.0; } return select(1.0 - min(1.0, (1.0 - b) / s), 0.0, s <= 0.0);' },\n darken: { gl: 'return min(b, s);', gpu: 'return min(b, s);' },\n lighten: { gl: 'return max(b, s);', gpu: 'return max(b, s);' },\n difference: { gl: 'return abs(b - s);', gpu: 'return abs(b - s);' },\n exclusion: { gl: 'return b + s - 2.0 * b * s;', gpu: 'return b + s - 2.0 * b * s;' },\n 'linear-burn': { gl: 'return max(0.0, b + s - 1.0);', gpu: 'return max(0.0, b + s - 1.0);' },\n};\n\nconst GL_NONSEP = `\nfloat lum(vec3 c) { return 0.3 * c.r + 0.59 * c.g + 0.11 * c.b; }\nvec3 clipColor(vec3 c) { float l = lum(c); float n = min(c.r, min(c.g, c.b)); float x = max(c.r, max(c.g, c.b));\n if (n < 0.0) c = l + (c - l) * l / (l - n); if (x > 1.0) c = l + (c - l) * (1.0 - l) / (x - l); return c; }\nvec3 setLum(vec3 c, float l) { return clipColor(c + (l - lum(c))); }\nfloat sat(vec3 c) { return max(c.r, max(c.g, c.b)) - min(c.r, min(c.g, c.b)); }\nvec3 setSat(vec3 c, float s) { float mx = max(c.r, max(c.g, c.b)); float mn = min(c.r, min(c.g, c.b)); return mx > mn ? (c - mn) * s / (mx - mn) : vec3(0.0); }`;\nconst GPU_NONSEP = `\nfn lum(c: vec3<f32>) -> f32 { return 0.3 * c.r + 0.59 * c.g + 0.11 * c.b; }\nfn clipColor(c0: vec3<f32>) -> vec3<f32> { var c = c0; let l = lum(c); let n = min(c.r, min(c.g, c.b)); let x = max(c.r, max(c.g, c.b));\n if (n < 0.0) { c = l + (c - l) * l / (l - n); } if (x > 1.0) { c = l + (c - l) * (1.0 - l) / (x - l); } return c; }\nfn setLum(c: vec3<f32>, l: f32) -> vec3<f32> { return clipColor(c + (l - lum(c))); }\nfn sat(c: vec3<f32>) -> f32 { return max(c.r, max(c.g, c.b)) - min(c.r, min(c.g, c.b)); }\nfn setSat(c: vec3<f32>, s: f32) -> vec3<f32> { let mx = max(c.r, max(c.g, c.b)); let mn = min(c.r, min(c.g, c.b)); if (mx > mn) { return (c - vec3<f32>(mn)) * s / (mx - mn); } return vec3<f32>(0.0); }`;\nconst NONSEP: Record<string, { gl: string; gpu: string }> = {\n hue: { gl: 'return setLum(setSat(cs, sat(cb)), lum(cb));', gpu: 'return setLum(setSat(cs, sat(cb)), lum(cb));' },\n saturation: { gl: 'return setLum(setSat(cb, sat(cs)), lum(cb));', gpu: 'return setLum(setSat(cb, sat(cs)), lum(cb));' },\n color: { gl: 'return setLum(cs, lum(cb));', gpu: 'return setLum(cs, lum(cb));' },\n luminosity: { gl: 'return setLum(cb, lum(cs));', gpu: 'return setLum(cb, lum(cs));' },\n};\n\nconst GL_MAIN = `\n vec3 cs = front.a > 0.0 ? front.rgb / front.a : vec3(0.0);\n vec3 cb = back.a > 0.0 ? back.rgb / back.a : vec3(0.0);\n vec3 mixed = front.rgb * (1.0 - back.a) + front.a * back.a * blendRGB(cb, cs) + (1.0 - front.a) * back.rgb;\n finalColor = vec4(mixed, blendedAlpha) * uBlend;`;\nconst GPU_MAIN = `\n let cs = select(vec3<f32>(0.0), front.rgb / front.a, front.a > 0.0);\n let cb = select(vec3<f32>(0.0), back.rgb / back.a, back.a > 0.0);\n let mixed = front.rgb * (1.0 - back.a) + front.a * back.a * blendRGB(cb, cs) + (1.0 - front.a) * back.rgb;\n out = vec4<f32>(mixed, blendedAlpha) * blendUniforms.uBlend;`;\n\nconst CLASSES = new Map<string, new () => BlendModeFilter>();\n\n/** A fresh blend filter for a layer that has filters of its own (it goes last in the chain and reads the backdrop). */\nexport function createBlendFilter(name: string): BlendFilterLike | null {\n const C = CLASSES.get(name);\n return C ? (new C() as unknown as BlendFilterLike) : null;\n}\n\nfunction make(name: string, gl: string, gpu: string): void {\n const C = class extends BlendModeFilter {\n static extension = { name, type: ExtensionType.BlendMode };\n constructor() { super({ gl: { functions: gl, main: GL_MAIN }, gpu: { functions: gpu, main: GPU_MAIN } }); }\n };\n CLASSES.set(name, C);\n extensions.add(C as never);\n}\n\nexport const OWN_MODES: readonly string[] = [...Object.keys(SEPARABLE), ...Object.keys(NONSEP)];\n\nlet done = false;\n/** Register the blends (once per page), and give `core/blend.ts` the maker for the filter chain case. */\nexport function registerBlendModes(): void {\n setBlendFilterFactory(createBlendFilter); // every call: the registration below is once per page, the maker is cheap to hand over again\n if (done) return;\n done = true;\n for (const [name, s] of Object.entries(SEPARABLE)) {\n make(name,\n `float B(float b, float s) { ${s.gl} }\\nvec3 blendRGB(vec3 cb, vec3 cs) { return vec3(B(cb.r, cs.r), B(cb.g, cs.g), B(cb.b, cs.b)); }`,\n `fn B(b: f32, s: f32) -> f32 { ${s.gpu} }\\nfn blendRGB(cb: vec3<f32>, cs: vec3<f32>) -> vec3<f32> { return vec3<f32>(B(cb.r, cs.r), B(cb.g, cs.g), B(cb.b, cs.b)); }`);\n }\n for (const [name, s] of Object.entries(NONSEP)) {\n make(name, `${GL_NONSEP}\\nvec3 blendRGB(vec3 cb, vec3 cs) { ${s.gl} }`, `${GPU_NONSEP}\\nfn blendRGB(cb: vec3<f32>, cs: vec3<f32>) -> vec3<f32> { ${s.gpu} }`);\n }\n}\n"]}
@@ -1,5 +1,5 @@
1
- import { setBlendFilterFactory } from './chunk-RTJD3ZSW.js';
2
- import './chunk-ITDYFBB6.js';
1
+ import { setBlendFilterFactory } from './chunk-WIIU42FH.js';
2
+ import './chunk-2JFFKHXO.js';
3
3
  import './chunk-BXOCEZ4U.js';
4
4
  import { BlendModeFilter, ExtensionType, extensions } from 'pixi.js';
5
5
 
@@ -86,5 +86,5 @@ fn blendRGB(cb: vec3<f32>, cs: vec3<f32>) -> vec3<f32> { ${s.gpu} }`);
86
86
  }
87
87
 
88
88
  export { OWN_MODES, createBlendFilter, registerBlendModes };
89
- //# sourceMappingURL=blendModes-PP5NPBXY.js.map
90
- //# sourceMappingURL=blendModes-PP5NPBXY.js.map
89
+ //# sourceMappingURL=blendModes-OOI3T6BE.js.map
90
+ //# sourceMappingURL=blendModes-OOI3T6BE.js.map