pixi-effects 0.35.0 → 0.37.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.
- package/CHANGELOG.md +35 -0
- package/README.md +5 -5
- package/ai/chat-template.html +3 -3
- package/ai/tools/check.mjs +38 -9
- package/ai/tools/lookup.mjs +15 -2
- package/dist/{Base-pNq6sVGl.d.cts → Base-BlA6SeqJ.d.cts} +1 -1
- package/dist/{Base-CXsMdmJB.d.ts → Base-YqM0Ljvp.d.ts} +1 -1
- package/dist/Composition-5KZETM4J.js +7 -0
- package/dist/{Composition-NDXKQHB4.js.map → Composition-5KZETM4J.js.map} +1 -1
- package/dist/Composition-MHDI7UNZ.cjs +16 -0
- package/dist/{Composition-OLH7RC5Z.cjs.map → Composition-MHDI7UNZ.cjs.map} +1 -1
- package/dist/Controller.d.cts +2 -2
- package/dist/Controller.d.ts +2 -2
- package/dist/{Movie-VYtUN4TS.d.ts → Movie-CJN_gvLB.d.cts} +26 -2
- package/dist/{Movie-Db4QnMIH.d.cts → Movie-CfHaGmUP.d.ts} +26 -2
- package/dist/{Spans-QHHWC2PB.js → Spans-FSSO2HGX.js} +6 -6
- package/dist/{Spans-QHHWC2PB.js.map → Spans-FSSO2HGX.js.map} +1 -1
- package/dist/{Spans-QRMN2EZO.cjs → Spans-WVJH77BW.cjs} +11 -11
- package/dist/{Spans-QRMN2EZO.cjs.map → Spans-WVJH77BW.cjs.map} +1 -1
- package/dist/{blendModes-WIUCYIMC.cjs → blendModes-FD3THLNC.cjs} +5 -5
- package/dist/{blendModes-WIUCYIMC.cjs.map → blendModes-FD3THLNC.cjs.map} +1 -1
- package/dist/{blendModes-PP5NPBXY.js → blendModes-FMYO6SQN.js} +4 -4
- package/dist/{blendModes-PP5NPBXY.js.map → blendModes-FMYO6SQN.js.map} +1 -1
- package/dist/{chunk-ITDYFBB6.js → chunk-32MMNDDN.js} +7 -6
- package/dist/chunk-32MMNDDN.js.map +1 -0
- package/dist/{chunk-GL2AQFLQ.cjs → chunk-3SAS3ZE5.cjs} +7 -6
- package/dist/chunk-3SAS3ZE5.cjs.map +1 -0
- package/dist/{chunk-7TTCPHAV.cjs → chunk-E26M4NTL.cjs} +235 -49
- package/dist/chunk-E26M4NTL.cjs.map +1 -0
- package/dist/{chunk-XV4L72FW.js → chunk-IPQNT65V.js} +6 -6
- package/dist/chunk-IPQNT65V.js.map +1 -0
- package/dist/{chunk-IMQJVRMT.cjs → chunk-JN7JHNZR.cjs} +5 -5
- package/dist/{chunk-IMQJVRMT.cjs.map → chunk-JN7JHNZR.cjs.map} +1 -1
- package/dist/{chunk-2GNK6DPV.js → chunk-KNDEKWQO.js} +203 -18
- package/dist/chunk-KNDEKWQO.js.map +1 -0
- package/dist/{chunk-5IJENZOG.cjs → chunk-MJMOPWMK.cjs} +92 -92
- package/dist/chunk-MJMOPWMK.cjs.map +1 -0
- package/dist/{chunk-LEECUCTG.cjs → chunk-QHIOOK5S.cjs} +436 -29
- package/dist/chunk-QHIOOK5S.cjs.map +1 -0
- package/dist/{chunk-Y253JQOH.js → chunk-SBR75G73.js} +416 -11
- package/dist/chunk-SBR75G73.js.map +1 -0
- package/dist/{chunk-RTJD3ZSW.js → chunk-VNP7QCJE.js} +4 -4
- package/dist/{chunk-RTJD3ZSW.js.map → chunk-VNP7QCJE.js.map} +1 -1
- package/dist/index.cjs +41 -33
- package/dist/index.d.cts +47 -7
- package/dist/index.d.ts +47 -7
- package/dist/index.js +5 -5
- package/dist/loader.css +1 -1
- package/dist/present.cjs +5 -5
- package/dist/present.d.cts +2 -2
- package/dist/present.d.ts +2 -2
- package/dist/present.js +5 -5
- package/dist/three.cjs +6 -6
- package/dist/three.d.cts +2 -2
- package/dist/three.d.ts +2 -2
- package/dist/three.js +2 -2
- package/dist/{types-CavnUvd5.d.cts → types-DvnXXm33.d.cts} +67 -3
- package/dist/{types-CavnUvd5.d.ts → types-DvnXXm33.d.ts} +67 -3
- package/docs/api.md +5 -4
- package/docs/dsl.md +42 -5
- package/llms-full.txt +305 -22
- package/package.json +1 -1
- package/skills/pixi-effects/SKILL.md +5 -4
- package/skills/pixi-effects/reference/cheatsheet.md +9 -6
- package/skills/pixi-effects/reference/pitfalls.md +7 -0
- package/skills/pixi-effects/reference/recipes.md +237 -3
- package/skills/pixi-effects/template.html +6 -6
- package/dist/Composition-NDXKQHB4.js +0 -7
- package/dist/Composition-OLH7RC5Z.cjs +0 -16
- package/dist/chunk-2GNK6DPV.js.map +0 -1
- package/dist/chunk-5IJENZOG.cjs.map +0 -1
- package/dist/chunk-7TTCPHAV.cjs.map +0 -1
- package/dist/chunk-GL2AQFLQ.cjs.map +0 -1
- package/dist/chunk-ITDYFBB6.js.map +0 -1
- package/dist/chunk-LEECUCTG.cjs.map +0 -1
- package/dist/chunk-XV4L72FW.js.map +0 -1
- package/dist/chunk-Y253JQOH.js.map +0 -1
package/CHANGELOG.md
CHANGED
|
@@ -1,5 +1,40 @@
|
|
|
1
1
|
# Changelog
|
|
2
2
|
|
|
3
|
+
## 0.37.0
|
|
4
|
+
|
|
5
|
+
**The `canvas` layer: draw it yourself, and what eight trial pieces taught**
|
|
6
|
+
|
|
7
|
+
- **New: `{ type: 'canvas', draw: (ctx, t, info) => { … } }`.** A picture drawn by your own function of the time into a canvas of the layer's own (cleared before every call, nothing survives into the next), shown like an image at its place in the stack: `initial`, `keyframes`, `filters`, `mask`, `blendMode`, `parent`, `threeD`, transitions and export work on it. Keys: `draw` (required), `setup` (once, may be async; its result is `info.state`, the place to compute a whole simulation), `width` / `height` (px or expression, at most 8192), `resolution` (0.25 to 4), `params` (numbers that keyframes animate as `'params.name'`), `seed` (seeds `info.random()`, which starts again at every call), `smoothing: false` (hard pixels, also when the layer is scaled up: pixel art). `info` is `{ frame, duration, progress, width, height, resolution, frameRate, params, state, random, start, time, canvas }`.
|
|
8
|
+
- **The contract is a function of the time, and `check` holds you to it:** a build-time warning for `Math.random`, `Date.now`, `performance.now` and `new Date()` in `draw`, and `check` (and `movie.review()`, so the Playground) draws every canvas layer twice at four moments and reports a layer whose two pictures differ, naming the moments (`movie.canvasProblems()`). The check draws in two orders (an integrator that moves by the time since the last call is caught, which two draws in a row cannot show) and in a scratch canvas (reading the pixels of the real one made Chrome move it from the GPU to the CPU in the middle of the check, which anti-aliases a few pixels differently), covers inline canvas masks, and `check` lists it under its own `canvas` heading, not as a layout issue. A missing or throwing `draw` / `setup` is said once with the layer's name and the layer is a magenta checkerboard (the next frames are tried again).
|
|
9
|
+
- It cannot see or paint on what is below it, it is not audio, and a spec that holds one holds functions (not pure JSON): an escape hatch like `three` and the `custom` filter. Recipes `canvas-trail`, `canvas-rings`, `canvas-pixel-sprite`, `canvas-life`; a guide section with a live demo; pitfall 115; `help canvas` finds the layer first (a heading that is the word now ranks first).
|
|
10
|
+
- **What eight fresh authors needed (a trial of eight different pieces made with the layer: a botanical plate, a fifths clock, a data race, a brush painting, an elastic exposure, a pixel-art scene, kinetic type, a flow field), and what changed because of it:**
|
|
11
|
+
- **`info.time`** (the movie's seconds) and **`info.start`** (where the layer starts on the movie's clock, nested compositions counted; inside a time-remapped composition it is that composition's own clock): several canvas layers and the sound share one clock without adding the layer's `at` by hand, which broke quietly when someone moved the layer (6 of 8 pieces did it by hand).
|
|
12
|
+
- **`info.canvas(w, h)`**: an offscreen canvas for sprites, masks and scratch pictures, with a 2D context made for reading (CPU). Found by tracing a `check` report that blamed `Math.random` for a layer that used none: a canvas made in `setup` with `document.createElement` and drawn with `destination-in` over 96 anti-aliased strokes was drawn differently the first time (up to 128 of 255, 40 418 channel values on a 1280×720 layer) and the same every time after.
|
|
13
|
+
- **`check` tells the truth about canvas layers**: every moment is drawn once and thrown away before the two orders are compared (a first-use difference of the browser is not held against the layer, and a cache filled on the first call is fine); a layer that differs is reported with how many channel values differ and by how much; when every layer passes, `check` prints a `canvas` line (`2 canvas layer(s) drew the same picture twice at 4 moments (two orders): deterministic · slowest draw 3.2 ms on the CPU`; silence made 7 of 8 authors unsure it had run); `movie.canvasReport()` and `review().canvas` carry the rows. A draw that throws at a checked moment is no longer "deterministic" (its checkerboard was the same twice): the row says it could not draw there. The draw time is measured after a one-pixel read, because a canvas keeps its calls until something reads it (a draw that took 259 ms in the check was the flow-field piece's 3600 lines).
|
|
14
|
+
- **New exports for `draw`:** `samplePath(d)` (SVG path data walked by length: `.length`, `.at(u)` → `{ x, y, angle }`, `.trim(a, b)` → flat point pieces; for a line that draws on, text along a curve, a petal that falls along a route) and `noise2(x, y, seed)` (smooth 2-D noise). `ease`, `noise`, `random` and `rand` were already exported and are now named in the docs: 6 of 8 authors rewrote easing by hand because nothing said it could be imported.
|
|
15
|
+
- **Docs:** shared data belongs in module scope (the state `setup` returns is private to its layer), a cache of a pure function is fine, sprites and masks come from `info.canvas`, `ctx.filter` / composite operations / `drawImage` of another canvas / `measureText` all work, a web font is loaded with `await document.fonts.load(…)` in `setup`, **`check` does not see text drawn in a canvas**, the first line of SKILL.md and AGENTS.md no longer says "you never write per-frame code" without the one exception; pitfalls 116–118; recipes `canvas-text-mask` (words as a window onto moving bands: mask first, then `source-atop`), `canvas-text-on-path` and `canvas-bars` (bars that swap places) in a section of their own.
|
|
16
|
+
- **Not built, on purpose:** values several layers read from one animated source (`composition.params`), reading another layer's picture, text on a path as a layer, pixel-art helpers (framebuffer, dither, palette, bitmap font), `paramAt(name, t)` (a keyframed param read at another time), a square wave for `music`. Each was asked for by one or two pieces.
|
|
17
|
+
- Gallery: five pieces made with the layer in the trial (a botanical plate, a bar-chart race, an elastic long exposure, a pixel cut-scene, a poem in kinetic type): 64 pieces.
|
|
18
|
+
- Spec and plan: `docs/superpowers/specs/2026-10-14-canvas-layer-design.md`. Plugins, registries and maintainer-only fields were considered and dropped: a layer that can draw anything makes them unnecessary.
|
|
19
|
+
|
|
20
|
+
## 0.36.0
|
|
21
|
+
|
|
22
|
+
**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)**
|
|
23
|
+
|
|
24
|
+
- **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.
|
|
25
|
+
- **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`).
|
|
26
|
+
- **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).
|
|
27
|
+
- **Guide demos load this repository's build** (`../../dist/…`), like the gallery pages, so a demo shows the code of the same commit.
|
|
28
|
+
- **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`).
|
|
29
|
+
- **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.
|
|
30
|
+
- **`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).
|
|
31
|
+
- **`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).
|
|
32
|
+
- **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.
|
|
33
|
+
- **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.
|
|
34
|
+
- 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.
|
|
35
|
+
- 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).
|
|
36
|
+
- 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`).
|
|
37
|
+
|
|
3
38
|
## 0.35.0
|
|
4
39
|
|
|
5
40
|
**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.
|
|
3
|
+
> **Status**: experimental — current release `0.37.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.
|
|
53
|
-
"pixi-effects/controller": "https://cdn.jsdelivr.net/npm/pixi-effects@0.
|
|
52
|
+
"pixi-effects": "https://cdn.jsdelivr.net/npm/pixi-effects@0.37.0/dist/index.js",
|
|
53
|
+
"pixi-effects/controller": "https://cdn.jsdelivr.net/npm/pixi-effects@0.37.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.
|
|
64
|
+
Load the `dist/` files **as they are** (jsDelivr's `/npm/…/dist/…`, or unpkg's `https://unpkg.com/pixi-effects@0.37.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.
|
|
80
|
+
"pixi-effects/three": "https://cdn.jsdelivr.net/npm/pixi-effects@0.37.0/dist/three.js"
|
|
81
81
|
}
|
|
82
82
|
}
|
|
83
83
|
</script>
|
package/ai/chat-template.html
CHANGED
|
@@ -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.
|
|
63
|
-
"pixi-effects/controller": "https://cdn.jsdelivr.net/npm/pixi-effects@0.
|
|
64
|
-
"pixi-effects/present": "https://cdn.jsdelivr.net/npm/pixi-effects@0.
|
|
62
|
+
"pixi-effects": "https://cdn.jsdelivr.net/npm/pixi-effects@0.37.0/dist/index.js",
|
|
63
|
+
"pixi-effects/controller": "https://cdn.jsdelivr.net/npm/pixi-effects@0.37.0/dist/Controller.js",
|
|
64
|
+
"pixi-effects/present": "https://cdn.jsdelivr.net/npm/pixi-effects@0.37.0/dist/present.js"
|
|
65
65
|
}
|
|
66
66
|
}
|
|
67
67
|
</script>
|
package/ai/tools/check.mjs
CHANGED
|
@@ -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-
|
|
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,11 +384,14 @@ 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 })})`);
|
|
383
|
-
|
|
384
|
-
|
|
391
|
+
const layoutProblems = rv.problems.filter(g => g.kind !== 'canvas'), canvasProblems = rv.problems.filter(g => g.kind === 'canvas');
|
|
392
|
+
report.inspect = { checkedFrames: rv.frames, issues: layoutProblems, review: rv.review, canvas: canvasProblems.map(g => g.message), canvasLayers: rv.canvas ?? [] };
|
|
393
|
+
if (layoutProblems.length) report.problems.push(`${layoutProblems.length} kind(s) of layout issue from movie.inspect`);
|
|
394
|
+
if (canvasProblems.length) report.problems.push(`${canvasProblems.length} canvas layer(s) that do not draw the same picture for the same time (movie.canvasProblems)`);
|
|
385
395
|
// fonts: a web font whose file did not load is a problem (the library lists a layer with no available font for review)
|
|
386
396
|
report.fonts = rv.fonts;
|
|
387
397
|
if (rv.fonts.failed.length) report.problems.push(`${rv.fonts.failed.length} web font(s) failed to load: ${rv.fonts.failed.join(', ')} (the layers that use them are drawn in a fallback)`);
|
|
@@ -398,8 +408,16 @@ export async function runCheck(opts, log = console.log) {
|
|
|
398
408
|
const wanted = atList;
|
|
399
409
|
const framesDir = path.join(outDir, 'frames');
|
|
400
410
|
fs.mkdirSync(framesDir, { recursive: true });
|
|
411
|
+
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
|
|
412
|
+
// --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
|
|
413
|
+
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;
|
|
414
|
+
const cropped = [];
|
|
401
415
|
for (const w of wanted) {
|
|
402
|
-
|
|
416
|
+
let png = await cdp.eval(`movie.snapshot(${w.frame}, { as: 'dataURL' })`);
|
|
417
|
+
if (crop) {
|
|
418
|
+
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)})`);
|
|
419
|
+
cropped.push(png);
|
|
420
|
+
}
|
|
403
421
|
fs.writeFileSync(path.join(framesDir, `${w.label}.png`), Buffer.from(png.slice(png.indexOf(',') + 1), 'base64'));
|
|
404
422
|
// 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
423
|
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 +426,15 @@ export async function runCheck(opts, log = console.log) {
|
|
|
408
426
|
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
427
|
if (lit.length) w.light = Object.fromEntries(lit);
|
|
410
428
|
}
|
|
411
|
-
const atSheet =
|
|
429
|
+
const atSheet = crop
|
|
430
|
+
? 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)})`)
|
|
431
|
+
: await cdp.eval(`movie.contactSheet({ frames: ${JSON.stringify(wanted.map(w => w.frame))}, as: 'dataURL' })`);
|
|
412
432
|
const atFile = path.join(outDir, 'at.png');
|
|
413
433
|
fs.writeFileSync(atFile, Buffer.from(atSheet.slice(atSheet.indexOf(',') + 1), 'base64'));
|
|
414
434
|
report.files.at = shown(atFile);
|
|
415
435
|
report.files.frames = shown(framesDir);
|
|
416
436
|
report.at = wanted;
|
|
437
|
+
if (crop) report.crop = crop;
|
|
417
438
|
}
|
|
418
439
|
|
|
419
440
|
// --onion A:B: one picture of the movement between A and B seconds (8 frames overlaid, the later the stronger)
|
|
@@ -561,6 +582,14 @@ function finish(report, outDir, log) {
|
|
|
561
582
|
L.push(` layout ${g.length ? g.length + ' kind(s) of issue over ' + report.inspect.checkedFrames + ' frames:' : 'no cut-off / off-canvas text over ' + report.inspect.checkedFrames + ' frames (movie.inspect)'}`);
|
|
562
583
|
for (const i of g.slice(0, 8)) L.push(line(i));
|
|
563
584
|
if (g.length > 8) L.push(` … ${g.length - 8} more (report.json)`);
|
|
585
|
+
const cv = report.inspect.canvas ?? [];
|
|
586
|
+
const rows = report.inspect.canvasLayers ?? [];
|
|
587
|
+
if (cv.length) { L.push(` canvas ${cv.length} canvas layer(s) that do not draw the same picture for the same time:`); for (const m of cv.slice(0, 4)) L.push(` - ${m}`); }
|
|
588
|
+
if (rows.length) { // silence must not be the only sign that the check ran
|
|
589
|
+
const good = rows.filter(r => r.ok), slow = [...rows].sort((a, b) => b.drawMs - a.drawMs)[0];
|
|
590
|
+
const ms = x => x < 10 ? x.toFixed(1) : String(Math.round(x));
|
|
591
|
+
if (good.length) L.push(` canvas ${cv.length ? 'the other ' : ''}${good.length} canvas layer(s) drew the same picture twice at ${good[0].moments.length} moments (two orders): deterministic · slowest draw ${ms(slow.drawMs)} ms on the CPU (${slow.name})`);
|
|
592
|
+
}
|
|
564
593
|
const rv = report.inspect.review;
|
|
565
594
|
if (rv.length) {
|
|
566
595
|
L.push(` review ${rv.length} to look at — text overlaps are often intentional (ghost / glow copies, per-letter boxes, a wipe), and a font that is not available is drawn in a fallback; check them on the contact sheet, or use --strict to fail on them:`);
|
package/ai/tools/lookup.mjs
CHANGED
|
@@ -105,9 +105,20 @@ 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
|
|
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
|
-
|
|
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
|
+
const exact = tokens.length === 1 && flat(heading) === flat(tokens[0]); // the heading IS the word
|
|
121
|
+
scored.push({ s, score: (exact ? 1500 : 0) + (inHeading ? 1000 : 0) + (row ? 1100 : 0) + (call ? 300 : 0) + Math.min(count, 50) + bonus });
|
|
111
122
|
}
|
|
112
123
|
}
|
|
113
124
|
scored.sort((a, b) => b.score - a.score);
|
|
@@ -155,6 +166,8 @@ export function newPage(name, { cwd = process.cwd(), root = PACKAGE_ROOT, force
|
|
|
155
166
|
.replace('<title>pixi-effects video</title>', `<title>${base.replace(/&/g, '&').replace(/</g, '<')}</title>`)
|
|
156
167
|
// the size sits in two places (the canvas attributes and W / H) and the length in one; the poster follows DURATION
|
|
157
168
|
.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">')
|
|
169
|
+
// 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
|
|
170
|
+
.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
171
|
.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
172
|
fs.writeFileSync(file, html);
|
|
160
173
|
return { file };
|
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
import { Container } from 'pixi.js';
|
|
2
|
-
import { S as SequenceSpec, C as CompositionShape } from './types-
|
|
2
|
+
import { S as SequenceSpec, C as CompositionShape } from './types-DvnXXm33.cjs';
|
|
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-
|
|
2
|
+
import { S as SequenceSpec, C as CompositionShape } from './types-DvnXXm33.js';
|
|
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-IPQNT65V.js';
|
|
2
|
+
import './chunk-SBR75G73.js';
|
|
3
|
+
import './chunk-VNP7QCJE.js';
|
|
4
|
+
import './chunk-32MMNDDN.js';
|
|
5
|
+
import './chunk-BXOCEZ4U.js';
|
|
6
|
+
//# sourceMappingURL=Composition-5KZETM4J.js.map
|
|
7
|
+
//# sourceMappingURL=Composition-5KZETM4J.js.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"sources":[],"names":[],"mappings":"","file":"Composition-
|
|
1
|
+
{"version":3,"sources":[],"names":[],"mappings":"","file":"Composition-5KZETM4J.js"}
|
|
@@ -0,0 +1,16 @@
|
|
|
1
|
+
'use strict';
|
|
2
|
+
|
|
3
|
+
var chunkMJMOPWMK_cjs = require('./chunk-MJMOPWMK.cjs');
|
|
4
|
+
require('./chunk-QHIOOK5S.cjs');
|
|
5
|
+
require('./chunk-JN7JHNZR.cjs');
|
|
6
|
+
require('./chunk-3SAS3ZE5.cjs');
|
|
7
|
+
require('./chunk-TWZNOUM7.cjs');
|
|
8
|
+
|
|
9
|
+
|
|
10
|
+
|
|
11
|
+
Object.defineProperty(exports, "CompositionSequence", {
|
|
12
|
+
enumerable: true,
|
|
13
|
+
get: function () { return chunkMJMOPWMK_cjs.CompositionSequence; }
|
|
14
|
+
});
|
|
15
|
+
//# sourceMappingURL=Composition-MHDI7UNZ.cjs.map
|
|
16
|
+
//# sourceMappingURL=Composition-MHDI7UNZ.cjs.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"sources":[],"names":[],"mappings":"","file":"Composition-
|
|
1
|
+
{"version":3,"sources":[],"names":[],"mappings":"","file":"Composition-MHDI7UNZ.cjs"}
|
package/dist/Controller.d.cts
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
|
-
import { l as Movie } from './Movie-
|
|
1
|
+
import { l as Movie } from './Movie-CJN_gvLB.cjs';
|
|
2
2
|
import 'pixi.js';
|
|
3
|
-
import './types-
|
|
3
|
+
import './types-DvnXXm33.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
|
package/dist/Controller.d.ts
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
|
-
import { l as Movie } from './Movie-
|
|
1
|
+
import { l as Movie } from './Movie-CfHaGmUP.js';
|
|
2
2
|
import 'pixi.js';
|
|
3
|
-
import './types-
|
|
3
|
+
import './types-DvnXXm33.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 {
|
|
2
|
+
import { $ as Stop, c as AssetSpec, m as CompositionSpec } from './types-DvnXXm33.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
|
}
|
|
@@ -292,12 +297,25 @@ interface FontReport {
|
|
|
292
297
|
failedUnused: string[];
|
|
293
298
|
}
|
|
294
299
|
|
|
300
|
+
/** One canvas layer as the check saw it. */
|
|
301
|
+
interface CanvasRow {
|
|
302
|
+
name: string;
|
|
303
|
+
/** The moments (seconds of the layer's own time) it was drawn at, in two orders. */
|
|
304
|
+
moments: number[];
|
|
305
|
+
/** The usual time of one draw on the CPU canvas of the check (the browser's GPU may be faster or slower). */
|
|
306
|
+
drawMs: number;
|
|
307
|
+
ok: boolean;
|
|
308
|
+
/** Why not, in a sentence (empty when ok). */
|
|
309
|
+
problem: string;
|
|
310
|
+
}
|
|
311
|
+
|
|
295
312
|
/** One kind of issue, with how many frames it was seen in. Issues that differ only by numbers are one kind. */
|
|
296
313
|
interface IssueGroup {
|
|
297
314
|
message: string;
|
|
298
315
|
count: number;
|
|
299
316
|
firstFrame: number;
|
|
300
|
-
lastFrame: number;
|
|
317
|
+
lastFrame: number; /** `'canvas'`: a canvas layer that does not draw the same picture for the same time (not about the layout; `check` lists it under its own heading). */
|
|
318
|
+
kind?: 'canvas';
|
|
301
319
|
}
|
|
302
320
|
interface ReviewOptions {
|
|
303
321
|
/** Moments to resolve and report: `3.5`, `50%`, `f120`, `title@end` (comma-separated). */
|
|
@@ -312,6 +330,8 @@ interface ReviewReport {
|
|
|
312
330
|
problems: IssueGroup[];
|
|
313
331
|
/** Text overlaps and fonts that are not available: often intentional, look at them on the contact sheet. */
|
|
314
332
|
review: IssueGroup[];
|
|
333
|
+
/** Every canvas layer as the determinism check saw it (a failing one is also in `problems` with `kind: 'canvas'`). */
|
|
334
|
+
canvas: CanvasRow[];
|
|
315
335
|
fonts: FontReport;
|
|
316
336
|
/** The sound as numbers (`movie.inspectAudio()`), or null for a movie with no audio. */
|
|
317
337
|
audio: AudioReport | null;
|
|
@@ -812,6 +832,10 @@ declare class Movie {
|
|
|
812
832
|
* the canvas, cut by an edge, empty, or overlapping other text. Seeks there and stays there.
|
|
813
833
|
*/
|
|
814
834
|
inspect(frame?: number, opts?: InspectOptions): Promise<InspectReport>;
|
|
835
|
+
/** The canvas layers whose same frame is not the same picture (what `check` reports as a problem): one sentence each. */
|
|
836
|
+
canvasProblems(): string[];
|
|
837
|
+
/** Every canvas layer as the determinism check saw it: the moments drawn, the usual time of a draw in ms, and whether the same time gave the same picture (`check` prints it even when all pass). */
|
|
838
|
+
canvasReport(): CanvasRow[];
|
|
815
839
|
/**
|
|
816
840
|
* Every layer as a row with absolute start / end / keyframe times (seconds), plus the transition windows. Runs of
|
|
817
841
|
* similar layers (`pop-1` … `pop-12`) are one row with a `parts` span each. Read it, or draw it with `timelineChart()`.
|
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
import { Application } from 'pixi.js';
|
|
2
|
-
import {
|
|
2
|
+
import { $ as Stop, c as AssetSpec, m as CompositionSpec } from './types-DvnXXm33.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
|
}
|
|
@@ -292,12 +297,25 @@ interface FontReport {
|
|
|
292
297
|
failedUnused: string[];
|
|
293
298
|
}
|
|
294
299
|
|
|
300
|
+
/** One canvas layer as the check saw it. */
|
|
301
|
+
interface CanvasRow {
|
|
302
|
+
name: string;
|
|
303
|
+
/** The moments (seconds of the layer's own time) it was drawn at, in two orders. */
|
|
304
|
+
moments: number[];
|
|
305
|
+
/** The usual time of one draw on the CPU canvas of the check (the browser's GPU may be faster or slower). */
|
|
306
|
+
drawMs: number;
|
|
307
|
+
ok: boolean;
|
|
308
|
+
/** Why not, in a sentence (empty when ok). */
|
|
309
|
+
problem: string;
|
|
310
|
+
}
|
|
311
|
+
|
|
295
312
|
/** One kind of issue, with how many frames it was seen in. Issues that differ only by numbers are one kind. */
|
|
296
313
|
interface IssueGroup {
|
|
297
314
|
message: string;
|
|
298
315
|
count: number;
|
|
299
316
|
firstFrame: number;
|
|
300
|
-
lastFrame: number;
|
|
317
|
+
lastFrame: number; /** `'canvas'`: a canvas layer that does not draw the same picture for the same time (not about the layout; `check` lists it under its own heading). */
|
|
318
|
+
kind?: 'canvas';
|
|
301
319
|
}
|
|
302
320
|
interface ReviewOptions {
|
|
303
321
|
/** Moments to resolve and report: `3.5`, `50%`, `f120`, `title@end` (comma-separated). */
|
|
@@ -312,6 +330,8 @@ interface ReviewReport {
|
|
|
312
330
|
problems: IssueGroup[];
|
|
313
331
|
/** Text overlaps and fonts that are not available: often intentional, look at them on the contact sheet. */
|
|
314
332
|
review: IssueGroup[];
|
|
333
|
+
/** Every canvas layer as the determinism check saw it (a failing one is also in `problems` with `kind: 'canvas'`). */
|
|
334
|
+
canvas: CanvasRow[];
|
|
315
335
|
fonts: FontReport;
|
|
316
336
|
/** The sound as numbers (`movie.inspectAudio()`), or null for a movie with no audio. */
|
|
317
337
|
audio: AudioReport | null;
|
|
@@ -812,6 +832,10 @@ declare class Movie {
|
|
|
812
832
|
* the canvas, cut by an edge, empty, or overlapping other text. Seeks there and stays there.
|
|
813
833
|
*/
|
|
814
834
|
inspect(frame?: number, opts?: InspectOptions): Promise<InspectReport>;
|
|
835
|
+
/** The canvas layers whose same frame is not the same picture (what `check` reports as a problem): one sentence each. */
|
|
836
|
+
canvasProblems(): string[];
|
|
837
|
+
/** Every canvas layer as the determinism check saw it: the moments drawn, the usual time of a draw in ms, and whether the same time gave the same picture (`check` prints it even when all pass). */
|
|
838
|
+
canvasReport(): CanvasRow[];
|
|
815
839
|
/**
|
|
816
840
|
* Every layer as a row with absolute start / end / keyframe times (seconds), plus the transition windows. Runs of
|
|
817
841
|
* similar layers (`pop-1` … `pop-12`) are one row with a `parts` span each. Read it, or draw it with `timelineChart()`.
|
|
@@ -1,7 +1,7 @@
|
|
|
1
|
-
import { CompositionSequence } from './chunk-
|
|
2
|
-
import { spansMeta, resolveNumber, bindCounterKeyframes, TextSequence } from './chunk-
|
|
3
|
-
import './chunk-
|
|
4
|
-
import './chunk-
|
|
1
|
+
import { CompositionSequence } from './chunk-IPQNT65V.js';
|
|
2
|
+
import { spansMeta, resolveNumber, bindCounterKeyframes, TextSequence } from './chunk-SBR75G73.js';
|
|
3
|
+
import './chunk-VNP7QCJE.js';
|
|
4
|
+
import './chunk-32MMNDDN.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-
|
|
53
|
-
//# sourceMappingURL=Spans-
|
|
52
|
+
//# sourceMappingURL=Spans-FSSO2HGX.js.map
|
|
53
|
+
//# sourceMappingURL=Spans-FSSO2HGX.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-
|
|
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-FSSO2HGX.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,9 +1,9 @@
|
|
|
1
1
|
'use strict';
|
|
2
2
|
|
|
3
|
-
var
|
|
4
|
-
var
|
|
5
|
-
require('./chunk-
|
|
6
|
-
require('./chunk-
|
|
3
|
+
var chunkMJMOPWMK_cjs = require('./chunk-MJMOPWMK.cjs');
|
|
4
|
+
var chunkQHIOOK5S_cjs = require('./chunk-QHIOOK5S.cjs');
|
|
5
|
+
require('./chunk-JN7JHNZR.cjs');
|
|
6
|
+
require('./chunk-3SAS3ZE5.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
|
|
15
|
+
var SpansSequence = class extends chunkMJMOPWMK_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 =
|
|
20
|
+
this._meta = chunkQHIOOK5S_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 :
|
|
37
|
-
|
|
36
|
+
this._count.visibleChars = first === void 0 ? 0 : chunkQHIOOK5S_cjs.resolveNumber(first, scope);
|
|
37
|
+
chunkQHIOOK5S_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
|
|
46
|
+
if (!(child instanceof chunkQHIOOK5S_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-
|
|
55
|
-
//# sourceMappingURL=Spans-
|
|
54
|
+
//# sourceMappingURL=Spans-WVJH77BW.cjs.map
|
|
55
|
+
//# sourceMappingURL=Spans-WVJH77BW.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-
|
|
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-WVJH77BW.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"]}
|