pixi-effects 0.26.2 → 0.27.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 +28 -0
- package/README.md +8 -10
- package/ai/CHAT.md +1 -1
- package/ai/chat-template.html +4 -6
- package/ai/tools/render.mjs +16 -11
- package/dist/{Base-CpdDs4HT.d.ts → Base-CVezIEdo.d.ts} +2 -4
- package/dist/{Base-TbBhHPrG.d.cts → Base-DzC3i919.d.cts} +2 -4
- package/dist/Composition-4TYDRRRY.js +7 -0
- package/dist/{Composition-H4QA24MV.js.map → Composition-4TYDRRRY.js.map} +1 -1
- package/dist/Composition-QCOWQSDB.cjs +16 -0
- package/dist/{Composition-HM4YT5L7.cjs.map → Composition-QCOWQSDB.cjs.map} +1 -1
- package/dist/Controller.d.cts +2 -3
- package/dist/Controller.d.ts +2 -3
- package/dist/{Movie-i05Oh9mJ.d.cts → Movie-CQ0Nz62a.d.cts} +12 -6
- package/dist/{Movie-DVeDTdnc.d.ts → Movie-M9dKm9dI.d.ts} +12 -6
- package/dist/Presenter.d.cts +2 -3
- package/dist/Presenter.d.ts +2 -3
- package/dist/{blendModes-YV43K6E7.cjs → blendModes-HDOP6R7T.cjs} +5 -5
- package/dist/{blendModes-YV43K6E7.cjs.map → blendModes-HDOP6R7T.cjs.map} +1 -1
- package/dist/{blendModes-G3NOAU3T.js → blendModes-PAVTW3BO.js} +4 -4
- package/dist/{blendModes-G3NOAU3T.js.map → blendModes-PAVTW3BO.js.map} +1 -1
- package/dist/{chunk-TLYTYBWX.js → chunk-45QKLD6M.js} +9 -11
- package/dist/chunk-45QKLD6M.js.map +1 -0
- package/dist/{chunk-GBUM7X2S.js → chunk-CKTS6G6I.js} +7 -11
- package/dist/chunk-CKTS6G6I.js.map +1 -0
- package/dist/{chunk-555URQHQ.cjs → chunk-NDR2VQFV.cjs} +91 -95
- package/dist/chunk-NDR2VQFV.cjs.map +1 -0
- package/dist/{chunk-3DLYAPK7.cjs → chunk-P4GY3Z4G.cjs} +5 -5
- package/dist/{chunk-3DLYAPK7.cjs.map → chunk-P4GY3Z4G.cjs.map} +1 -1
- package/dist/{chunk-WPXDRSGN.cjs → chunk-RQMI7EJT.cjs} +1170 -280
- package/dist/chunk-RQMI7EJT.cjs.map +1 -0
- package/dist/{chunk-X3HB4DSR.js → chunk-TGJACVLD.js} +1150 -263
- package/dist/chunk-TGJACVLD.js.map +1 -0
- package/dist/{chunk-AXZUKNXF.js → chunk-TYWFKCPY.js} +4 -4
- package/dist/{chunk-AXZUKNXF.js.map → chunk-TYWFKCPY.js.map} +1 -1
- package/dist/{chunk-ECA2WNRX.cjs → chunk-ZMAD3KJU.cjs} +9 -11
- package/dist/chunk-ZMAD3KJU.cjs.map +1 -0
- package/dist/index.cjs +189 -116
- package/dist/index.cjs.map +1 -1
- package/dist/index.d.cts +16 -14
- package/dist/index.d.ts +16 -14
- package/dist/index.js +136 -45
- package/dist/index.js.map +1 -1
- package/dist/loader.css +1 -1
- package/dist/three.cjs +6 -6
- package/dist/three.d.cts +2 -3
- package/dist/three.d.ts +2 -3
- package/dist/three.js +2 -2
- package/dist/{types-DSGwN5w1.d.ts → types-DUVefWiB.d.cts} +128 -3
- package/dist/{types-DSGwN5w1.d.cts → types-DUVefWiB.d.ts} +128 -3
- package/docs/api.md +16 -6
- package/docs/dsl.md +14 -14
- package/llms-full.txt +38 -28
- package/llms.txt +1 -1
- package/package.json +3 -4
- package/skills/pixi-effects/SKILL.md +1 -1
- package/skills/pixi-effects/reference/cheatsheet.md +3 -3
- package/skills/pixi-effects/reference/pitfalls.md +3 -3
- package/skills/pixi-effects/reference/recipes.md +1 -1
- package/skills/pixi-effects/template.html +6 -8
- package/dist/Composition-H4QA24MV.js +0 -7
- package/dist/Composition-HM4YT5L7.cjs +0 -16
- package/dist/chunk-555URQHQ.cjs.map +0 -1
- package/dist/chunk-ECA2WNRX.cjs.map +0 -1
- package/dist/chunk-GBUM7X2S.js.map +0 -1
- package/dist/chunk-TLYTYBWX.js.map +0 -1
- package/dist/chunk-WPXDRSGN.cjs.map +0 -1
- package/dist/chunk-X3HB4DSR.js.map +0 -1
package/CHANGELOG.md
CHANGED
|
@@ -1,5 +1,33 @@
|
|
|
1
1
|
# Changelog
|
|
2
2
|
|
|
3
|
+
## 0.27.0
|
|
4
|
+
|
|
5
|
+
**Changed (breaking)**
|
|
6
|
+
|
|
7
|
+
- **The library no longer uses GSAP.** The timeline is the library's own (`src/core/pure/`): a value at a time is computed from the script and the time alone, never from the way the playhead came to it, so scrubbing, looping, reverse play and export all give the same picture at the same time. `gsap` leaves the peer dependencies (`npm install pixi-effects pixi.js`), and every page loses the `gsap` and `gsap/PixiPlugin` lines of its import map (a page that still has them works; nothing imports them). Measured against the GSAP build on 92 pages by frame hash: 77 identical, 9 within 3/255, and the rest explained (four pages use `expo`, whose formula differs between GSAP 3.12 and 3.15; one page is not reproducible even with GSAP). The timeline itself is 1.3 to 11 times faster per frame and 25 KB (gzip) lighter for a page; a whole frame is dominated by drawing and changes by 1 to 15 %.
|
|
8
|
+
- **`expo` is GSAP 3.15's curve** (`2^(10(p-1))·p + p^6(1-p)`, which lands exactly on 1): up to 0.78 % of the range away from 3.12's, so a page made with 3.12 moves a little.
|
|
9
|
+
- **Where GSAP answered by the way the playhead came, the answer is now the script's**: overlapping tweens on one property (the one that started last wins), two items with the same start, a `set` where a tween ends (GSAP could lose it), a `from` after another segment (it ends where that segment ended).
|
|
10
|
+
- **`blur`, `blurX`, `blurY`, `blurPadding`, `colorize`, `colorizeAmount`, `colorMatrixFilter`, `lineColor`, `lineAlpha` are no longer keys** (they were GSAP PixiPlugin shorthands for a filter on the layer): the lint says so with the likely name; use a named filter and animate `filters.<name>.<prop>`. `scale`, `anchor`, `pivot`, `skew` (degrees), `rotation` (degrees), `position`, `tilePosition`, `tileScale`, `tint` and `autoAlpha` stay.
|
|
11
|
+
- `ease` names are the familiar ones (`power1…4`, `sine`, `expo`, `circ`, `back`, `elastic`, `bounce`, `steps(n)`, `spring…`, `cubic-bezier(…)`); a name nobody knows still warns and runs as `power1.out`. `delay` in a keyframe's vars moves its start; `onStart`, `onComplete`, `onRepeat` and `onReverseComplete` are said once and ignored.
|
|
12
|
+
- `movie.timeline` is the library's `Timeline` (`time(t)`, `duration()`, `segments()`), not a GSAP timeline.
|
|
13
|
+
|
|
14
|
+
**Added**
|
|
15
|
+
|
|
16
|
+
- `ease(name)` is exported: the function behind an ease name.
|
|
17
|
+
- Colour strings that Pixi can read (names, `hsl()`, `oklch()`) interpolate in `'rgb'` colour space as rgba.
|
|
18
|
+
|
|
19
|
+
## 0.26.3
|
|
20
|
+
|
|
21
|
+
**Added**
|
|
22
|
+
|
|
23
|
+
- **Sound-only export.** `render({ format: 'wav' })` (16-bit PCM) and `render({ format: 'ogg' })` (Opus) write the movie's mixed sound without drawing a frame, with `range` cutting it; `pixi-effects render page.html -o mix.wav` does it from a script. A movie with no sound says so instead of writing an empty file.
|
|
24
|
+
- **Video codec and bitrate options.** `render({ video: { codec, bitrate, hardware, keyFrameInterval } })`: `codec` is `avc`, `hevc`, `vp9`, `av1` or `vp8` (checked against the container and against what this browser can encode: the usual one falls back to the next that works with a warning, a codec you name is used or the error names the one that would work, and `h264` gets a did-you-mean); `bitrate` is a quality name or bits a second (`8_000_000`, `'8M'`, `'800k'`); `hardware` picks the encoder; `keyFrameInterval` (seconds, default 2). The render command gains `--video-bitrate`.
|
|
25
|
+
- **Relative keyframe values.** `to`, `from` and `set` accept `'+=36'` / `'-=36'`, measured from where the layer stands, with an expression on the right (`'-=GW*0.1'`): `from: { x: '-=36' }` is the usual slide-in, `to: { y: '+=40' }` a nudge, and keyframes one after another stack in any order of seeking. Until now `'+=36'` was evaluated as an expression and became **0 with no warning**; in `initial`, a style or the start of a `from` + `to` it now warns and is read as the number. (GSAP supported relative values all along; the keyframe pipeline did not let them through.)
|
|
26
|
+
|
|
27
|
+
**Changed**
|
|
28
|
+
|
|
29
|
+
- **Video: the pauses of reverse play and of a loop are read ahead.** A looping layer reads the first frames of the file (and a pass behind them) before the end comes, so the wrap shows no pause (a 30 s loop of two heavy layers: 0 pauses, it had 3). Reverse play reads the next window in the background while the held frames last, and each window now just fits what is held: one heavy layer played backwards shows 29.3 distinct frames a second with 1 pause in 6 s (24.8 and 6 pauses without the read-ahead, 14.1 and 31 before 0.26.2); four heavy layers, each reading its own window, reach 18 a second. Frames are now let go by where the playhead is (the ones it has passed first), not by age. Measured with the on-screen frame-number method of 0.26.2, on an M1 Pro.
|
|
30
|
+
|
|
3
31
|
## 0.26.2
|
|
4
32
|
|
|
5
33
|
**Fixed**
|
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.27.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/)** · 50 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
|
|
|
@@ -12,7 +12,7 @@ Give any AI (ChatGPT, Claude, Codex, Cursor, Claude Code …) one sentence:
|
|
|
12
12
|
|
|
13
13
|
That is all it needs. **To register it as a skill** in your agent (Claude Code, Codex, Cursor, Gemini CLI, Copilot, OpenCode, …): `npx skills add yjmtmtk/pixi-effects` (update with `npx skills update`). In Claude Code you can also run `/plugin install pixi-effects --marketplace yjmtmtk/pixi-effects`. **If you are the AI** that was asked to do this, do not read the whole repository: if you can run commands (a shell, Node 22, Chrome), read [`AGENTS.md`](./AGENTS.md) and [`skills/pixi-effects/SKILL.md`](./skills/pixi-effects/SKILL.md); if you can only write text (a browser chat), fetch https://raw.githubusercontent.com/yjmtmtk/pixi-effects/main/ai/CHAT.md and follow it. You will write the video as plain data in one HTML file, check it (the check tool writes a contact sheet to look at), and hand it over; the person opens the file and downloads the MP4 with the button in the player bar.
|
|
14
14
|
|
|
15
|
-
Declarative composition and video rendering for the web. After Effects-style timelines on top of [PixiJS v8](https://pixijs.com/) and [GSAP](https://gsap.com/), with
|
|
15
|
+
Declarative composition and video rendering for the web. After Effects-style timelines on top of [PixiJS v8](https://pixijs.com/), with a small timeline of its own and strict TypeScript types. The ease names and the way `to` / `from` / `set` read are [GSAP](https://gsap.com/)'s, with thanks. Render to MP4 / WebM / MOV via [mediabunny](https://mediabunny.dev/).
|
|
16
16
|
|
|
17
17
|
- **Declarative DSL** — describe your composition as a tree of typed sequences (text, image, video, audio, shapes, nested compositions). No imperative tween code.
|
|
18
18
|
- **Sound effects without files** — `{ type: 'audio', sfx: 'swoosh', at: 2 }`: 11 synthesised presets (click, pop, swoosh, riser, hit, chime …) with `pitch` / `brightness` / `seed`, or your own `voices`; deterministic and in the exported file at the same moments as in the browser; `movie.inspectAudio()` checks them as numbers.
|
|
@@ -29,12 +29,12 @@ Declarative composition and video rendering for the web. After Effects-style tim
|
|
|
29
29
|
- **Built-in player UI — or your own** — a drop-in HTML5-`<video>`-style overlay controller (play, scrub, mute, volume, fullscreen, export-to-file) whose colours are one option (`theme: { accent: '#ff4d6d' }`); or build a player yourself on the movie's `<video>`-named events (`play pause ended seeking seeked volumechange error`).
|
|
30
30
|
- **MP4 / WebM / MOV export** — pick container and quality from the controller, call `movie.render()` from code, or render headless from the command line (`pixi-effects-render`).
|
|
31
31
|
- **A timeline you can read and scrub** — `movie.timelineChart()` draws every layer as a bar on a time axis; `pixi-effects-view` opens the page with that timeline under it, zoomable, with a playhead you can drag.
|
|
32
|
-
- **Tiny dependency surface** — only `mediabunny` (runtime) plus PixiJS
|
|
32
|
+
- **Tiny dependency surface** — only `mediabunny` (runtime) plus PixiJS (a peer dep). three.js is an *optional* peer, needed only if you import `pixi-effects/three`.
|
|
33
33
|
|
|
34
34
|
## Install
|
|
35
35
|
|
|
36
36
|
```bash
|
|
37
|
-
npm install pixi-effects pixi.js
|
|
37
|
+
npm install pixi-effects pixi.js
|
|
38
38
|
npm install three # optional — only if you import `pixi-effects/three`
|
|
39
39
|
npm install pixi-filters # optional — only if a layer uses a named filter other than blur / noise / alpha / colorMatrix
|
|
40
40
|
```
|
|
@@ -48,11 +48,9 @@ Drop the imports into an [importmap](https://developer.mozilla.org/docs/Web/HTML
|
|
|
48
48
|
{
|
|
49
49
|
"imports": {
|
|
50
50
|
"pixi.js": "https://esm.sh/pixi.js@8.22.0?bundle-deps",
|
|
51
|
-
"gsap": "https://esm.sh/gsap@3.12.5",
|
|
52
|
-
"gsap/PixiPlugin": "https://esm.sh/gsap@3.12.5/PixiPlugin",
|
|
53
51
|
"mediabunny": "https://esm.sh/mediabunny",
|
|
54
|
-
"pixi-effects": "https://cdn.jsdelivr.net/npm/pixi-effects@0.
|
|
55
|
-
"pixi-effects/controller": "https://cdn.jsdelivr.net/npm/pixi-effects@0.
|
|
52
|
+
"pixi-effects": "https://cdn.jsdelivr.net/npm/pixi-effects@0.27.0/dist/index.js",
|
|
53
|
+
"pixi-effects/controller": "https://cdn.jsdelivr.net/npm/pixi-effects@0.27.0/dist/Controller.js"
|
|
56
54
|
}
|
|
57
55
|
}
|
|
58
56
|
</script>
|
|
@@ -63,7 +61,7 @@ Drop the imports into an [importmap](https://developer.mozilla.org/docs/Web/HTML
|
|
|
63
61
|
</script>
|
|
64
62
|
```
|
|
65
63
|
|
|
66
|
-
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.27.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.
|
|
67
65
|
|
|
68
66
|
> **Using three.js?** Add two more entries (`three` and `pixi-effects/three`) to this importmap — see [Adding three.js](#adding-threejs-optional) below.
|
|
69
67
|
>
|
|
@@ -79,7 +77,7 @@ Add two more entries to the importmap above: three.js itself, and the `pixi-effe
|
|
|
79
77
|
"imports": {
|
|
80
78
|
"...": "(everything from the importmap above)",
|
|
81
79
|
"three": "https://esm.sh/three@0.178.0",
|
|
82
|
-
"pixi-effects/three": "https://cdn.jsdelivr.net/npm/pixi-effects@0.
|
|
80
|
+
"pixi-effects/three": "https://cdn.jsdelivr.net/npm/pixi-effects@0.27.0/dist/three.js"
|
|
83
81
|
}
|
|
84
82
|
}
|
|
85
83
|
</script>
|
package/ai/CHAT.md
CHANGED
|
@@ -53,7 +53,7 @@ That is a complete, working video (the block between the two markers). Change th
|
|
|
53
53
|
- **Order.** Layers are drawn in array order: later = on top. Put the background rect first.
|
|
54
54
|
- **Units.** Pixels from the top-left; angles in **degrees**; colours `'#rrggbb'`. Any number may be an expression string: `'GW/2'`, `'GH - 80'`, `'min(GW, GH) * 0.4'`.
|
|
55
55
|
- **Anchors.** `rect`, `circle`, `ellipse` are centred on `x, y`. **Text and image are top-left**: add `anchorX: 0.5, anchorY: 0.5` to centre them. A bar that grows from its base needs `anchorY: 1`.
|
|
56
|
-
- **Animate with keyframes.** `{ at, duration, ease, to: {...} }` animates to a value, `from: {...}` from one, `from` + `to` both (a delayed fade-in needs no `initial.alpha`), `set: {...}` jumps. Eases are
|
|
56
|
+
- **Animate with keyframes.** `{ at, duration, ease, to: {...} }` animates to a value, `from: {...}` from one, `from` + `to` both (a delayed fade-in needs no `initial.alpha`), `set: {...}` jumps. Eases are the familiar names: `'power2.out'`, `'back.out(1.7)'`, `'none'`.
|
|
57
57
|
- **Draw a line on:** `trimEnd: 0` in `initial`, then a keyframe `to: { trimEnd: 1 }` (every shape except `arc`).
|
|
58
58
|
- **Shapes:** `shape: 'rect' | 'circle' | 'ellipse' | 'arc' | 'line' | 'polygon' | 'path'`. `rect`: `width height cornerRadius`; `circle`: `radius`; `line`: `from: [x, y], to: [x, y]` (canvas coordinates, no `x, y`); `path`: `d` (SVG path data). Style: `fillColor strokeColor strokeWidth fillAlpha`.
|
|
59
59
|
- **Text:** `style` is a PixiJS text style (`fontSize fontFamily fontWeight fill letterSpacing align wordWrap wordWrapWidth lineHeight`). Use fonts that exist everywhere (`Georgia, serif`, `Arial, sans-serif`, `ui-monospace, monospace`); this page loads no web fonts. Estimate a text's width at about 0.6 × `fontSize` per character, keep important text 5 % away from the edges, and keep the bottom 60 px free (the player bar covers it in the preview, not in the file).
|
package/ai/chat-template.html
CHANGED
|
@@ -54,16 +54,14 @@
|
|
|
54
54
|
window.__add = add;
|
|
55
55
|
</script>
|
|
56
56
|
|
|
57
|
-
|
|
57
|
+
<script type="importmap">
|
|
58
58
|
{
|
|
59
59
|
"imports": {
|
|
60
60
|
"pixi.js": "https://cdn.jsdelivr.net/npm/pixi.js@8.22.0/dist/pixi.min.mjs",
|
|
61
|
-
"gsap": "https://cdn.jsdelivr.net/npm/gsap@3.12.5/+esm",
|
|
62
|
-
"gsap/PixiPlugin": "https://cdn.jsdelivr.net/npm/gsap@3.12.5/PixiPlugin/+esm",
|
|
63
61
|
"mediabunny": "https://cdn.jsdelivr.net/npm/mediabunny@1.61.3/+esm",
|
|
64
|
-
"pixi-effects": "https://cdn.jsdelivr.net/npm/pixi-effects@0.
|
|
65
|
-
"pixi-effects/controller": "https://cdn.jsdelivr.net/npm/pixi-effects@0.
|
|
66
|
-
"pixi-effects/presenter": "https://cdn.jsdelivr.net/npm/pixi-effects@0.
|
|
62
|
+
"pixi-effects": "https://cdn.jsdelivr.net/npm/pixi-effects@0.27.0/dist/index.js",
|
|
63
|
+
"pixi-effects/controller": "https://cdn.jsdelivr.net/npm/pixi-effects@0.27.0/dist/Controller.js",
|
|
64
|
+
"pixi-effects/presenter": "https://cdn.jsdelivr.net/npm/pixi-effects@0.27.0/dist/Presenter.js"
|
|
67
65
|
}
|
|
68
66
|
}
|
|
69
67
|
</script>
|
package/ai/tools/render.mjs
CHANGED
|
@@ -12,12 +12,12 @@
|
|
|
12
12
|
* Exit code: 0 = the file was written, 1 = the page or the render failed (no file), 2 = bad usage.
|
|
13
13
|
*
|
|
14
14
|
* Options: -o, --out FILE (default ./<page name>.<format>; the container follows the extension: mp4 webm mov mkv) ·
|
|
15
|
-
* --format mp4|webm|mov|mkv|pdf (a .pdf is the deck as pages: one picture per page of a movie with `stops`; --all-stops makes a page of every stop) · --quality very-low|low|medium|high|very-high (video and audio bitrate, default high) ·
|
|
15
|
+
* --format mp4|webm|mov|mkv|wav|ogg|pdf (wav / ogg: only the movie's sound, no picture; a .pdf is the deck as pages: one picture per page of a movie with `stops`; --all-stops makes a page of every stop) · --quality very-low|low|medium|high|very-high (video and audio bitrate, default high) ·
|
|
16
16
|
* --range A:B (only seconds A to B; 2: runs to the end, :5 starts at the beginning) · --scene NAME (the span of a top-level layer: a movie's scene) · --scale S (output size, 0 < S <= 1: smaller picture, smaller file) ·
|
|
17
17
|
* --draft (for looking: half size, low quality, no motion blur; a --quality you give still wins) ·
|
|
18
18
|
* --motion-blur SAMPLES (2-64: each frame is drawn that many times over the shutter and averaged; overrides the page's own motionBlur) ·
|
|
19
19
|
* --shutter FRACTION (with --motion-blur: how long the shutter is open, 0-1 of a frame, default 0.5) ·
|
|
20
|
-
* --video-codec C · --audio-codec C · --query "a=1&b=2" (added to the page URL) · --fail-on-warn (exit 1 when the page
|
|
20
|
+
* --video-codec C (avc hevc vp9 av1 vp8) · --video-bitrate B (a quality or bits a second: 8M, 800k) · --audio-codec C · --query "a=1&b=2" (added to the page URL) · --fail-on-warn (exit 1 when the page
|
|
21
21
|
* logged a warning; the file is still written) · --quiet (no progress line) · --timeout SECONDS (default 900) ·
|
|
22
22
|
* --root DIR (static server root; default: the nearest folder above the page with dist/) · --chrome PATH
|
|
23
23
|
*/
|
|
@@ -27,14 +27,15 @@ import path from 'node:path';
|
|
|
27
27
|
import { pathToFileURL } from 'node:url';
|
|
28
28
|
import { findChrome, findRoot, serve, launchChrome, shown, sleep } from './check.mjs';
|
|
29
29
|
|
|
30
|
-
const FORMATS = ['mp4', 'webm', 'mov', 'mkv', 'pdf'];
|
|
30
|
+
const FORMATS = ['mp4', 'webm', 'mov', 'mkv', 'wav', 'ogg', 'pdf'];
|
|
31
|
+
const SOUND_ONLY = ['wav', 'ogg'];
|
|
31
32
|
const QUALITIES = ['very-low', 'low', 'medium', 'high', 'very-high'];
|
|
32
33
|
|
|
33
34
|
/** 'clip.WEBM' → 'webm'; no extension → null; any other extension is an error. */
|
|
34
35
|
export function formatFromPath(file) {
|
|
35
36
|
const ext = path.extname(file).slice(1).toLowerCase();
|
|
36
37
|
if (!ext) return null;
|
|
37
|
-
if (!FORMATS.includes(ext)) throw new Error(`cannot tell the container from ".${ext}": use an output ending in mp4, webm, mov, mkv or pdf (or --format)`);
|
|
38
|
+
if (!FORMATS.includes(ext)) throw new Error(`cannot tell the container from ".${ext}": use an output ending in mp4, webm, mov, mkv, wav, ogg or pdf (or --format)`);
|
|
38
39
|
return ext;
|
|
39
40
|
}
|
|
40
41
|
|
|
@@ -43,18 +44,21 @@ export function defaultOutput(page, format) {
|
|
|
43
44
|
}
|
|
44
45
|
|
|
45
46
|
export function parseRenderArgs(argv) {
|
|
46
|
-
const o = { range: null, scene: null, scale: null, draft: false, qualityGiven: false, page: null, out: null, format: null, quality: 'high', videoCodec: null, audioCodec: null, allStops: false, motionBlur: null, shutter: null, query: null, failOnWarn: false, quiet: false, timeout: 900, root: null, chrome: null };
|
|
47
|
+
const o = { range: null, scene: null, scale: null, draft: false, qualityGiven: false, page: null, out: null, format: null, quality: 'high', videoCodec: null, videoBitrate: null, audioCodec: null, allStops: false, motionBlur: null, shutter: null, query: null, failOnWarn: false, quiet: false, timeout: 900, root: null, chrome: null };
|
|
47
48
|
const need = (i, name) => { if (i + 1 >= argv.length) throw new Error(`${name} needs a value`); return argv[i + 1]; };
|
|
48
49
|
for (let i = 0; i < argv.length; i++) {
|
|
49
50
|
const a = argv[i];
|
|
50
51
|
if (a === '-o' || a === '--out') o.out = need(i++, a);
|
|
51
52
|
else if (a === '--format') {
|
|
52
53
|
o.format = need(i++, a).toLowerCase();
|
|
53
|
-
if (!FORMATS.includes(o.format)) throw new Error(`--format must be mp4, webm, mov, mkv or pdf (got "${o.format}")`);
|
|
54
|
+
if (!FORMATS.includes(o.format)) throw new Error(`--format must be mp4, webm, mov, mkv, wav, ogg or pdf (got "${o.format}")`);
|
|
54
55
|
} else if (a === '--quality') {
|
|
55
56
|
o.qualityGiven = true;
|
|
56
57
|
o.quality = need(i++, a);
|
|
57
58
|
if (!QUALITIES.includes(o.quality)) throw new Error(`--quality must be very-low, low, medium, high or very-high (got "${o.quality}")`);
|
|
59
|
+
} else if (a === '--video-bitrate') {
|
|
60
|
+
o.videoBitrate = need(i++, a);
|
|
61
|
+
if (!/^(very-low|low|medium|high|very-high|\d+(\.\d+)?\s*[kKmM]?)$/.test(o.videoBitrate)) throw new Error(`--video-bitrate must be a quality (very-low, low, medium, high, very-high) or bits a second (8000000, 8M, 800k), got "${o.videoBitrate}"`);
|
|
58
62
|
} else if (a === '--video-codec') o.videoCodec = need(i++, a);
|
|
59
63
|
else if (a === '--audio-codec') o.audioCodec = need(i++, a);
|
|
60
64
|
else if (a === '--motion-blur') {
|
|
@@ -88,6 +92,7 @@ export function parseRenderArgs(argv) {
|
|
|
88
92
|
if (o.allStops && o.format !== 'pdf' && !(o.out && formatFromPath(o.out) === 'pdf')) throw new Error('--all-stops goes with a PDF (-o deck.pdf)');
|
|
89
93
|
o.format ??= (o.out ? formatFromPath(o.out) : null) ?? 'mp4';
|
|
90
94
|
if (o.range && o.scene !== null) throw new Error('--range and --scene cannot be used together: a scene is a range');
|
|
95
|
+
if (SOUND_ONLY.includes(o.format) && (o.scale !== null || o.draft || o.motionBlur !== null || o.videoCodec || o.videoBitrate)) throw new Error(`--scale, --draft, --motion-blur, --video-codec and --video-bitrate are for a picture: ${o.format} writes only the sound`);
|
|
91
96
|
if (o.format === 'pdf' && (o.range || o.scene !== null || o.scale !== null || o.draft)) throw new Error('--range, --scene, --scale and --draft are not for a PDF (a PDF is the pages of a deck)');
|
|
92
97
|
return o;
|
|
93
98
|
}
|
|
@@ -96,16 +101,16 @@ const UPLOAD = '/__pixi_effects_out';
|
|
|
96
101
|
const kb = n => (n >= 1048576 ? `${(n / 1048576).toFixed(2)} MB` : `${Math.max(1, Math.round(n / 1024))} KB`);
|
|
97
102
|
|
|
98
103
|
/** The page's side: render, and PUT the Blob to our server (so a long video never goes through base64). */
|
|
99
|
-
const renderScript = (format, quality, videoCodec, audioCodec, motionBlur, shutter, allStops, part = {}) => `(() => {
|
|
104
|
+
const renderScript = (format, quality, videoCodec, audioCodec, motionBlur, shutter, allStops, part = {}, videoBitrate = null) => `(() => {
|
|
100
105
|
window.__r = { state: 'running', progress: 0 };
|
|
101
106
|
movie.on('progress', e => { window.__r.progress = e.progress; });
|
|
102
107
|
(async () => {
|
|
103
108
|
try {
|
|
104
|
-
const video = { ${[part.draft && !part.qualityGiven ? null : `bitrate: ${JSON.stringify(quality)}`, videoCodec ? `codec: ${JSON.stringify(videoCodec)}` : null].filter(Boolean).join(', ')} };
|
|
109
|
+
const video = { ${[videoBitrate ? `bitrate: ${JSON.stringify(/^\d+(\.\d+)?$/.test(videoBitrate) ? Number(videoBitrate) : videoBitrate)}` : part.draft && !part.qualityGiven ? null : `bitrate: ${JSON.stringify(quality)}`, videoCodec ? `codec: ${JSON.stringify(videoCodec)}` : null].filter(Boolean).join(', ')} };
|
|
105
110
|
const audio = { bitrate: ${JSON.stringify(quality)}${audioCodec ? `, codec: ${JSON.stringify(audioCodec)}` : ''} };
|
|
106
111
|
const blob = ${JSON.stringify(format)} === 'pdf'
|
|
107
112
|
? await movie.exportPDF({ which: ${allStops ? "'stops'" : "'pages'"}, title: document.title })
|
|
108
|
-
: await movie.render({ format: ${JSON.stringify(format)}, video, audio${part.range ? `, range: [${part.range[0] ?? 0}, ${part.range[1] ?? 'movie.duration'}]` : ''}${part.scene ? `, range: ${JSON.stringify(part.scene)}` : ''}${part.scale ? `, scale: ${part.scale}` : ''}${part.draft ? ', draft: true' : ''}${motionBlur ? `, motionBlur: { samples: ${motionBlur}${shutter ? `, shutter: ${shutter}` : ''} }` : ''} });
|
|
113
|
+
: await movie.render({ format: ${JSON.stringify(format)}, ${SOUND_ONLY.includes(format) ? '' : 'video, '}audio${part.range ? `, range: [${part.range[0] ?? 0}, ${part.range[1] ?? 'movie.duration'}]` : ''}${part.scene ? `, range: ${JSON.stringify(part.scene)}` : ''}${part.scale ? `, scale: ${part.scale}` : ''}${part.draft ? ', draft: true' : ''}${motionBlur ? `, motionBlur: { samples: ${motionBlur}${shutter ? `, shutter: ${shutter}` : ''} }` : ''} });
|
|
109
114
|
const res = await fetch(${JSON.stringify(UPLOAD)}, { method: 'PUT', body: blob });
|
|
110
115
|
if (!res.ok) throw new Error('could not hand the file to the command (' + res.status + ')');
|
|
111
116
|
window.__r = { state: 'done', bytes: blob.size, type: blob.type };
|
|
@@ -171,7 +176,7 @@ export async function runRender(opts, log = console.log, progress = () => {}) {
|
|
|
171
176
|
}
|
|
172
177
|
Object.assign(result, { duration: info.duration, frames: info.totalFrames, width: info.width, height: info.height, frameRate: info.frameRate });
|
|
173
178
|
|
|
174
|
-
await cdp.eval(renderScript(opts.format, opts.quality, opts.videoCodec, opts.audioCodec, opts.motionBlur, opts.shutter, opts.allStops, { range: opts.range, scene: opts.scene, scale: opts.scale, draft: opts.draft, qualityGiven: opts.qualityGiven }));
|
|
179
|
+
await cdp.eval(renderScript(opts.format, opts.quality, opts.videoCodec, opts.audioCodec, opts.motionBlur, opts.shutter, opts.allStops, { range: opts.range, scene: opts.scene, scale: opts.scale, draft: opts.draft, qualityGiven: opts.qualityGiven }, opts.videoBitrate));
|
|
175
180
|
let state;
|
|
176
181
|
for (;;) {
|
|
177
182
|
left();
|
|
@@ -221,7 +226,7 @@ if (process.argv[1] && import.meta.url === pathToFileURL(fs.realpathSync(process
|
|
|
221
226
|
console.error(`pixi-effects render failed: ${r.problems.join('; ') || 'no file written'}`);
|
|
222
227
|
process.exit(1);
|
|
223
228
|
}
|
|
224
|
-
console.log(`${shown(r.file)} ${kb(r.bytes)} · ${r.duration} s · ${r.width}×${r.height} @ ${r.frameRate} fps · ${opts.format} · rendered in ${r.seconds} s`);
|
|
229
|
+
console.log(`${shown(r.file)} ${kb(r.bytes)} · ${r.duration} s · ${SOUND_ONLY.includes(opts.format) ? 'sound only' : `${r.width}×${r.height} @ ${r.frameRate} fps`} · ${opts.format} · rendered in ${r.seconds} s`);
|
|
225
230
|
process.exit(opts.failOnWarn && r.logs.length ? 1 : 0);
|
|
226
231
|
} catch (e) {
|
|
227
232
|
console.error(`pixi-effects render failed: ${e.message}`);
|
|
@@ -1,9 +1,8 @@
|
|
|
1
1
|
import { Filter, Container, RenderTexture, Matrix } from 'pixi.js';
|
|
2
|
-
import { S as SequenceSpec, C as CompositionShape, a6 as AudioDescriptor } from './types-
|
|
3
|
-
import { gsap } from 'gsap';
|
|
2
|
+
import { S as SequenceSpec, C as CompositionShape, a6 as Timeline, a7 as AudioDescriptor } from './types-DUVefWiB.js';
|
|
4
3
|
|
|
5
4
|
/**
|
|
6
|
-
* Resolves a routed keyframe path to a concrete
|
|
5
|
+
* Resolves a routed keyframe path to a concrete tween target.
|
|
7
6
|
* `path` is everything after the registered prefix + dot (for
|
|
8
7
|
* `three.cube.rotation.y` under prefix `three`, path = `cube.rotation.y`).
|
|
9
8
|
* Return null to skip the key (the router is expected to have warned).
|
|
@@ -45,7 +44,6 @@ interface SpaceHost {
|
|
|
45
44
|
}): void;
|
|
46
45
|
}
|
|
47
46
|
|
|
48
|
-
type Timeline = ReturnType<typeof gsap.timeline>;
|
|
49
47
|
declare abstract class Sequence {
|
|
50
48
|
spec: SequenceSpec;
|
|
51
49
|
parent: CompositionShape | null;
|
|
@@ -1,9 +1,8 @@
|
|
|
1
1
|
import { Filter, Container, RenderTexture, Matrix } from 'pixi.js';
|
|
2
|
-
import { S as SequenceSpec, C as CompositionShape, a6 as AudioDescriptor } from './types-
|
|
3
|
-
import { gsap } from 'gsap';
|
|
2
|
+
import { S as SequenceSpec, C as CompositionShape, a6 as Timeline, a7 as AudioDescriptor } from './types-DUVefWiB.cjs';
|
|
4
3
|
|
|
5
4
|
/**
|
|
6
|
-
* Resolves a routed keyframe path to a concrete
|
|
5
|
+
* Resolves a routed keyframe path to a concrete tween target.
|
|
7
6
|
* `path` is everything after the registered prefix + dot (for
|
|
8
7
|
* `three.cube.rotation.y` under prefix `three`, path = `cube.rotation.y`).
|
|
9
8
|
* Return null to skip the key (the router is expected to have warned).
|
|
@@ -45,7 +44,6 @@ interface SpaceHost {
|
|
|
45
44
|
}): void;
|
|
46
45
|
}
|
|
47
46
|
|
|
48
|
-
type Timeline = ReturnType<typeof gsap.timeline>;
|
|
49
47
|
declare abstract class Sequence {
|
|
50
48
|
spec: SequenceSpec;
|
|
51
49
|
parent: CompositionShape | null;
|
|
@@ -0,0 +1,7 @@
|
|
|
1
|
+
export { CompositionSequence } from './chunk-CKTS6G6I.js';
|
|
2
|
+
import './chunk-TGJACVLD.js';
|
|
3
|
+
import './chunk-TYWFKCPY.js';
|
|
4
|
+
import './chunk-45QKLD6M.js';
|
|
5
|
+
import './chunk-BXOCEZ4U.js';
|
|
6
|
+
//# sourceMappingURL=Composition-4TYDRRRY.js.map
|
|
7
|
+
//# sourceMappingURL=Composition-4TYDRRRY.js.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"sources":[],"names":[],"mappings":"","file":"Composition-
|
|
1
|
+
{"version":3,"sources":[],"names":[],"mappings":"","file":"Composition-4TYDRRRY.js"}
|
|
@@ -0,0 +1,16 @@
|
|
|
1
|
+
'use strict';
|
|
2
|
+
|
|
3
|
+
var chunkNDR2VQFV_cjs = require('./chunk-NDR2VQFV.cjs');
|
|
4
|
+
require('./chunk-RQMI7EJT.cjs');
|
|
5
|
+
require('./chunk-P4GY3Z4G.cjs');
|
|
6
|
+
require('./chunk-ZMAD3KJU.cjs');
|
|
7
|
+
require('./chunk-TWZNOUM7.cjs');
|
|
8
|
+
|
|
9
|
+
|
|
10
|
+
|
|
11
|
+
Object.defineProperty(exports, "CompositionSequence", {
|
|
12
|
+
enumerable: true,
|
|
13
|
+
get: function () { return chunkNDR2VQFV_cjs.CompositionSequence; }
|
|
14
|
+
});
|
|
15
|
+
//# sourceMappingURL=Composition-QCOWQSDB.cjs.map
|
|
16
|
+
//# sourceMappingURL=Composition-QCOWQSDB.cjs.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"sources":[],"names":[],"mappings":"","file":"Composition-
|
|
1
|
+
{"version":3,"sources":[],"names":[],"mappings":"","file":"Composition-QCOWQSDB.cjs"}
|
package/dist/Controller.d.cts
CHANGED
|
@@ -1,7 +1,6 @@
|
|
|
1
|
-
import { j as Movie } from './Movie-
|
|
1
|
+
import { j as Movie } from './Movie-CQ0Nz62a.cjs';
|
|
2
2
|
import 'pixi.js';
|
|
3
|
-
import '
|
|
4
|
-
import './types-DSGwN5w1.cjs';
|
|
3
|
+
import './types-DUVefWiB.cjs';
|
|
5
4
|
|
|
6
5
|
/** The bar's default stylesheet. Its colours and sizes are CSS custom properties (`--mc-accent`, `--mc-fg`, `--mc-track`, `--mc-bar-bg`, `--mc-track-height`, `--mc-font`): set them in your page CSS, or with the `theme` option. */
|
|
7
6
|
declare const CONTROLLER_CSS = "\n.movie-controller-wrap { position: relative; display: inline-block; line-height: 0; }\n.movie-controller {\n position: absolute;\n pointer-events: none;\n font-family: var(--mc-font, -apple-system, BlinkMacSystemFont, \"Segoe UI\", Roboto, sans-serif);\n display: flex;\n flex-direction: column;\n justify-content: flex-end;\n opacity: 1;\n transition: opacity 200ms ease;\n}\n.movie-controller[data-state=\"hidden\"] { opacity: 0; }\n.movie-controller > * { pointer-events: auto; }\n\n.mc-progress {\n position: relative;\n width: 100%;\n height: 16px;\n cursor: pointer;\n display: flex;\n align-items: center;\n outline: none;\n}\n.mc-progress:focus-visible {\n outline: 2px solid var(--mc-accent, #007AFF);\n outline-offset: 2px;\n}\n.mc-progress::before {\n content: \"\"; position: absolute; left: 12px; right: 12px;\n height: var(--mc-track-height, 3px); background: var(--mc-track, rgba(255,255,255,0.25));\n transition: height 120ms ease;\n}\n.mc-progress:hover::before, .mc-progress.mc-scrubbing::before { height: calc(var(--mc-track-height, 3px) + 2px); }\n.mc-stops { position: absolute; left: 12px; right: 12px; top: 0; bottom: 0; pointer-events: none; }\n.mc-stop-tick { position: absolute; top: 50%; width: 7px; height: 7px; transform: translate(-50%, -50%); border-radius: 50%; background: var(--mc-fg, #fff); box-shadow: 0 0 0 1.5px rgba(0,0,0,0.5); }\n.mc-stop-page { width: 11px; height: 11px; box-shadow: 0 0 0 2.5px var(--mc-accent, #007AFF), 0 0 0 4px rgba(0,0,0,0.35); }\n.mc-progress-fill {\n position: absolute; left: 12px; top: 50%;\n height: var(--mc-track-height, 3px);\n width: calc((100% - 24px) * var(--mc-fill, 0));\n background: var(--mc-accent, #007AFF);\n transform: translateY(-50%);\n transition: height 120ms ease;\n pointer-events: none;\n}\n.mc-progress:hover .mc-progress-fill,\n.mc-progress.mc-scrubbing .mc-progress-fill { height: calc(var(--mc-track-height, 3px) + 2px); }\n.mc-progress-thumb {\n position: absolute; top: 50%;\n left: calc(12px + (100% - 24px) * var(--mc-fill, 0));\n width: 12px; height: 12px; border-radius: 50%;\n background: var(--mc-accent, #007AFF);\n transform: translate(-50%, -50%) scale(0);\n transition: transform 120ms ease;\n pointer-events: none;\n}\n.mc-progress:hover .mc-progress-thumb,\n.mc-progress.mc-scrubbing .mc-progress-thumb { transform: translate(-50%, -50%) scale(1); }\n\n.mc-bar {\n position: relative;\n display: flex;\n align-items: center;\n gap: 12px;\n padding: 6px 12px 8px 12px;\n background: var(--mc-bar-bg, linear-gradient(to top, rgba(0,0,0,0.75) 0%, rgba(0,0,0,0) 100%));\n color: var(--mc-fg, #fff);\n line-height: 1;\n}\n.mc-btn {\n background: none; border: 0; padding: 0; margin: 0;\n width: 28px; height: 28px;\n display: inline-flex; align-items: center; justify-content: center;\n color: var(--mc-fg, #fff); opacity: 0.85; cursor: pointer;\n transition: opacity 120ms ease;\n}\n.mc-btn:hover { opacity: 1; }\n.mc-btn:disabled { opacity: 0.4; cursor: not-allowed; }\n.mc-time {\n font-size: 12px; font-family: Menlo, Monaco, monospace;\n color: var(--mc-fg, #fff); opacity: 0.85;\n min-width: 90px;\n}\n.mc-spacer { flex: 1; }\n\n.mc-volume { display: flex; align-items: center; }\n.mc-vol-slider {\n position: relative;\n width: 0;\n height: 28px;\n display: flex;\n align-items: center;\n cursor: pointer;\n margin-left: 0;\n outline: none;\n transition: width 200ms ease, margin-left 200ms ease;\n overflow: hidden;\n}\n.mc-volume:hover .mc-vol-slider,\n.mc-volume:focus-within .mc-vol-slider,\n.mc-vol-slider.mc-scrubbing {\n width: 70px;\n margin-left: 6px;\n}\n.mc-vol-slider:focus-visible { outline: 2px solid var(--mc-accent, #007AFF); outline-offset: 2px; }\n.mc-vol-slider::before {\n content: \"\"; position: absolute; left: 4px; right: 4px;\n height: 3px; background: var(--mc-track, rgba(255,255,255,0.25)); border-radius: 2px;\n}\n.mc-vol-fill {\n position: absolute; left: 4px; top: 50%;\n height: 3px;\n width: calc((100% - 8px) * var(--mc-volume, 1));\n background: var(--mc-fg, #fff);\n transform: translateY(-50%);\n border-radius: 2px;\n pointer-events: none;\n}\n.mc-vol-thumb {\n position: absolute; top: 50%;\n left: calc(4px + (100% - 8px) * var(--mc-volume, 1));\n width: 10px; height: 10px; border-radius: 50%;\n background: var(--mc-fg, #fff);\n transform: translate(-50%, -50%);\n pointer-events: none;\n}\n\n[data-mc-wrap]:fullscreen {\n width: 100vw; height: 100vh;\n display: flex; align-items: center; justify-content: center;\n background: #000;\n}\n[data-mc-wrap]:fullscreen > canvas {\n width: 100% !important;\n height: 100% !important;\n max-width: none !important;\n max-height: none !important;\n object-fit: contain;\n border-radius: 0;\n}\n\n.mc-settings-popover {\n position: absolute;\n bottom: calc(100% + 8px);\n right: 12px;\n background: rgba(20, 20, 20, 0.96);\n border-radius: 8px;\n padding: 10px 12px;\n min-width: 180px;\n color: #fff;\n font-size: 12px;\n box-shadow: 0 4px 16px rgba(0,0,0,0.4);\n display: flex;\n flex-direction: column;\n gap: 8px;\n}\n.mc-settings-popover[data-open=\"false\"] { display: none; }\n.mc-settings-row {\n display: flex;\n align-items: center;\n justify-content: space-between;\n gap: 12px;\n}\n.mc-settings-row > span { color: rgba(255,255,255,0.85); }\n.mc-settings-row > select {\n font: inherit;\n background: rgba(255,255,255,0.1);\n color: #fff;\n border: 1px solid rgba(255,255,255,0.18);\n border-radius: 4px;\n padding: 2px 6px;\n cursor: pointer;\n}\n.mc-export-confirm {\n margin-top: 4px;\n width: 100%;\n background: var(--mc-accent, #007AFF);\n color: #fff;\n border: 0;\n border-radius: 4px;\n padding: 6px 10px;\n font: inherit;\n font-weight: 600;\n cursor: pointer;\n transition: background 120ms ease;\n}\n.mc-export-confirm:hover { background: #0066d6; }\n.mc-export-confirm:disabled { background: rgba(255,255,255,0.18); cursor: not-allowed; }\n\n.mc-export-overlay {\n position: absolute; inset: 0;\n background: rgba(0,0,0,0.7);\n display: flex; justify-content: center; align-items: center;\n z-index: 2;\n}\n.mc-export-panel {\n background: rgba(20,20,20,0.96); color: #fff;\n border-radius: 8px; padding: 16px 20px;\n text-align: center; min-width: 200px; max-width: 80%;\n font-family: -apple-system, BlinkMacSystemFont, \"Segoe UI\", Roboto, sans-serif;\n}\n.mc-export-title { font-size: 13px; margin-bottom: 10px; opacity: 0.9; }\n.mc-export-track {\n width: 100%; height: 6px;\n background: rgba(255,255,255,0.15); border-radius: 3px;\n margin-bottom: 8px; overflow: hidden;\n}\n.mc-export-fill {\n height: 100%; width: 0%;\n background: linear-gradient(90deg, var(--mc-accent, #007AFF), var(--mc-accent, #0056CC));\n transition: width 0.3s ease; border-radius: 3px;\n}\n.mc-export-text {\n color: rgba(255,255,255,0.7); font-size: 11px;\n font-family: Menlo, Monaco, monospace;\n}\n";
|
package/dist/Controller.d.ts
CHANGED
|
@@ -1,7 +1,6 @@
|
|
|
1
|
-
import { j as Movie } from './Movie-
|
|
1
|
+
import { j as Movie } from './Movie-M9dKm9dI.js';
|
|
2
2
|
import 'pixi.js';
|
|
3
|
-
import '
|
|
4
|
-
import './types-DSGwN5w1.js';
|
|
3
|
+
import './types-DUVefWiB.js';
|
|
5
4
|
|
|
6
5
|
/** The bar's default stylesheet. Its colours and sizes are CSS custom properties (`--mc-accent`, `--mc-fg`, `--mc-track`, `--mc-bar-bg`, `--mc-track-height`, `--mc-font`): set them in your page CSS, or with the `theme` option. */
|
|
7
6
|
declare const CONTROLLER_CSS = "\n.movie-controller-wrap { position: relative; display: inline-block; line-height: 0; }\n.movie-controller {\n position: absolute;\n pointer-events: none;\n font-family: var(--mc-font, -apple-system, BlinkMacSystemFont, \"Segoe UI\", Roboto, sans-serif);\n display: flex;\n flex-direction: column;\n justify-content: flex-end;\n opacity: 1;\n transition: opacity 200ms ease;\n}\n.movie-controller[data-state=\"hidden\"] { opacity: 0; }\n.movie-controller > * { pointer-events: auto; }\n\n.mc-progress {\n position: relative;\n width: 100%;\n height: 16px;\n cursor: pointer;\n display: flex;\n align-items: center;\n outline: none;\n}\n.mc-progress:focus-visible {\n outline: 2px solid var(--mc-accent, #007AFF);\n outline-offset: 2px;\n}\n.mc-progress::before {\n content: \"\"; position: absolute; left: 12px; right: 12px;\n height: var(--mc-track-height, 3px); background: var(--mc-track, rgba(255,255,255,0.25));\n transition: height 120ms ease;\n}\n.mc-progress:hover::before, .mc-progress.mc-scrubbing::before { height: calc(var(--mc-track-height, 3px) + 2px); }\n.mc-stops { position: absolute; left: 12px; right: 12px; top: 0; bottom: 0; pointer-events: none; }\n.mc-stop-tick { position: absolute; top: 50%; width: 7px; height: 7px; transform: translate(-50%, -50%); border-radius: 50%; background: var(--mc-fg, #fff); box-shadow: 0 0 0 1.5px rgba(0,0,0,0.5); }\n.mc-stop-page { width: 11px; height: 11px; box-shadow: 0 0 0 2.5px var(--mc-accent, #007AFF), 0 0 0 4px rgba(0,0,0,0.35); }\n.mc-progress-fill {\n position: absolute; left: 12px; top: 50%;\n height: var(--mc-track-height, 3px);\n width: calc((100% - 24px) * var(--mc-fill, 0));\n background: var(--mc-accent, #007AFF);\n transform: translateY(-50%);\n transition: height 120ms ease;\n pointer-events: none;\n}\n.mc-progress:hover .mc-progress-fill,\n.mc-progress.mc-scrubbing .mc-progress-fill { height: calc(var(--mc-track-height, 3px) + 2px); }\n.mc-progress-thumb {\n position: absolute; top: 50%;\n left: calc(12px + (100% - 24px) * var(--mc-fill, 0));\n width: 12px; height: 12px; border-radius: 50%;\n background: var(--mc-accent, #007AFF);\n transform: translate(-50%, -50%) scale(0);\n transition: transform 120ms ease;\n pointer-events: none;\n}\n.mc-progress:hover .mc-progress-thumb,\n.mc-progress.mc-scrubbing .mc-progress-thumb { transform: translate(-50%, -50%) scale(1); }\n\n.mc-bar {\n position: relative;\n display: flex;\n align-items: center;\n gap: 12px;\n padding: 6px 12px 8px 12px;\n background: var(--mc-bar-bg, linear-gradient(to top, rgba(0,0,0,0.75) 0%, rgba(0,0,0,0) 100%));\n color: var(--mc-fg, #fff);\n line-height: 1;\n}\n.mc-btn {\n background: none; border: 0; padding: 0; margin: 0;\n width: 28px; height: 28px;\n display: inline-flex; align-items: center; justify-content: center;\n color: var(--mc-fg, #fff); opacity: 0.85; cursor: pointer;\n transition: opacity 120ms ease;\n}\n.mc-btn:hover { opacity: 1; }\n.mc-btn:disabled { opacity: 0.4; cursor: not-allowed; }\n.mc-time {\n font-size: 12px; font-family: Menlo, Monaco, monospace;\n color: var(--mc-fg, #fff); opacity: 0.85;\n min-width: 90px;\n}\n.mc-spacer { flex: 1; }\n\n.mc-volume { display: flex; align-items: center; }\n.mc-vol-slider {\n position: relative;\n width: 0;\n height: 28px;\n display: flex;\n align-items: center;\n cursor: pointer;\n margin-left: 0;\n outline: none;\n transition: width 200ms ease, margin-left 200ms ease;\n overflow: hidden;\n}\n.mc-volume:hover .mc-vol-slider,\n.mc-volume:focus-within .mc-vol-slider,\n.mc-vol-slider.mc-scrubbing {\n width: 70px;\n margin-left: 6px;\n}\n.mc-vol-slider:focus-visible { outline: 2px solid var(--mc-accent, #007AFF); outline-offset: 2px; }\n.mc-vol-slider::before {\n content: \"\"; position: absolute; left: 4px; right: 4px;\n height: 3px; background: var(--mc-track, rgba(255,255,255,0.25)); border-radius: 2px;\n}\n.mc-vol-fill {\n position: absolute; left: 4px; top: 50%;\n height: 3px;\n width: calc((100% - 8px) * var(--mc-volume, 1));\n background: var(--mc-fg, #fff);\n transform: translateY(-50%);\n border-radius: 2px;\n pointer-events: none;\n}\n.mc-vol-thumb {\n position: absolute; top: 50%;\n left: calc(4px + (100% - 8px) * var(--mc-volume, 1));\n width: 10px; height: 10px; border-radius: 50%;\n background: var(--mc-fg, #fff);\n transform: translate(-50%, -50%);\n pointer-events: none;\n}\n\n[data-mc-wrap]:fullscreen {\n width: 100vw; height: 100vh;\n display: flex; align-items: center; justify-content: center;\n background: #000;\n}\n[data-mc-wrap]:fullscreen > canvas {\n width: 100% !important;\n height: 100% !important;\n max-width: none !important;\n max-height: none !important;\n object-fit: contain;\n border-radius: 0;\n}\n\n.mc-settings-popover {\n position: absolute;\n bottom: calc(100% + 8px);\n right: 12px;\n background: rgba(20, 20, 20, 0.96);\n border-radius: 8px;\n padding: 10px 12px;\n min-width: 180px;\n color: #fff;\n font-size: 12px;\n box-shadow: 0 4px 16px rgba(0,0,0,0.4);\n display: flex;\n flex-direction: column;\n gap: 8px;\n}\n.mc-settings-popover[data-open=\"false\"] { display: none; }\n.mc-settings-row {\n display: flex;\n align-items: center;\n justify-content: space-between;\n gap: 12px;\n}\n.mc-settings-row > span { color: rgba(255,255,255,0.85); }\n.mc-settings-row > select {\n font: inherit;\n background: rgba(255,255,255,0.1);\n color: #fff;\n border: 1px solid rgba(255,255,255,0.18);\n border-radius: 4px;\n padding: 2px 6px;\n cursor: pointer;\n}\n.mc-export-confirm {\n margin-top: 4px;\n width: 100%;\n background: var(--mc-accent, #007AFF);\n color: #fff;\n border: 0;\n border-radius: 4px;\n padding: 6px 10px;\n font: inherit;\n font-weight: 600;\n cursor: pointer;\n transition: background 120ms ease;\n}\n.mc-export-confirm:hover { background: #0066d6; }\n.mc-export-confirm:disabled { background: rgba(255,255,255,0.18); cursor: not-allowed; }\n\n.mc-export-overlay {\n position: absolute; inset: 0;\n background: rgba(0,0,0,0.7);\n display: flex; justify-content: center; align-items: center;\n z-index: 2;\n}\n.mc-export-panel {\n background: rgba(20,20,20,0.96); color: #fff;\n border-radius: 8px; padding: 16px 20px;\n text-align: center; min-width: 200px; max-width: 80%;\n font-family: -apple-system, BlinkMacSystemFont, \"Segoe UI\", Roboto, sans-serif;\n}\n.mc-export-title { font-size: 13px; margin-bottom: 10px; opacity: 0.9; }\n.mc-export-track {\n width: 100%; height: 6px;\n background: rgba(255,255,255,0.15); border-radius: 3px;\n margin-bottom: 8px; overflow: hidden;\n}\n.mc-export-fill {\n height: 100%; width: 0%;\n background: linear-gradient(90deg, var(--mc-accent, #007AFF), var(--mc-accent, #0056CC));\n transition: width 0.3s ease; border-radius: 3px;\n}\n.mc-export-text {\n color: rgba(255,255,255,0.7); font-size: 11px;\n font-family: Menlo, Monaco, monospace;\n}\n";
|
|
@@ -1,6 +1,5 @@
|
|
|
1
1
|
import { Application } from 'pixi.js';
|
|
2
|
-
import {
|
|
3
|
-
import { _ as Stop, e as AssetSpec, c as CompositionSpec } from './types-DSGwN5w1.cjs';
|
|
2
|
+
import { a6 as Timeline, _ as Stop, e as AssetSpec, c as CompositionSpec } from './types-DUVefWiB.cjs';
|
|
4
3
|
|
|
5
4
|
interface AudioInspectOptions {
|
|
6
5
|
/** Width of each loudness window in seconds. Default 0.1. */
|
|
@@ -289,14 +288,21 @@ interface MovieOptions {
|
|
|
289
288
|
interface RenderOptions {
|
|
290
289
|
/** Motion blur for this export (see `MovieOptions.motionBlur`); overrides the movie's own setting, `false` turns it off. */
|
|
291
290
|
motionBlur?: MotionBlurSpec;
|
|
292
|
-
|
|
291
|
+
/** `mp4`, `mov`, `webm`, `mkv`: the picture with its sound. `wav`, `ogg`: only the sound of the movie (an error when it has none). */
|
|
292
|
+
format?: 'mp4' | 'mov' | 'webm' | 'mkv' | 'wav' | 'ogg';
|
|
293
293
|
video?: {
|
|
294
|
+
/** `'avc'` (H.264), `'hevc'`, `'vp9'`, `'av1'`, `'vp8'`: default the usual one for the format, the next that works if this browser cannot encode it; a name you give is never swapped. */
|
|
294
295
|
codec?: string;
|
|
295
|
-
|
|
296
|
+
/** A quality name, or bits a second: `8_000_000` or `'8M'`, `'800k'`. */
|
|
297
|
+
bitrate?: 'very-low' | 'low' | 'medium' | 'high' | 'very-high' | number | string;
|
|
298
|
+
/** Where to encode: `'prefer-hardware'`, `'prefer-software'` or `'no-preference'` (default). */
|
|
299
|
+
hardware?: 'no-preference' | 'prefer-hardware' | 'prefer-software';
|
|
300
|
+
/** Seconds between keyframes (default 2): shorter seeks faster and makes a bigger file. */
|
|
301
|
+
keyFrameInterval?: number;
|
|
296
302
|
};
|
|
297
303
|
audio?: {
|
|
298
304
|
codec?: string;
|
|
299
|
-
bitrate?: 'very-low' | 'low' | 'medium' | 'high' | 'very-high';
|
|
305
|
+
bitrate?: 'very-low' | 'low' | 'medium' | 'high' | 'very-high' | number | string;
|
|
300
306
|
};
|
|
301
307
|
/**
|
|
302
308
|
* Only part of the movie: `[from, to]` in seconds, or the name of a top-level layer (its start to its end). The file starts at 0
|
|
@@ -403,7 +409,7 @@ declare class Movie {
|
|
|
403
409
|
private _events;
|
|
404
410
|
private _initState;
|
|
405
411
|
app: Application | null;
|
|
406
|
-
timeline:
|
|
412
|
+
timeline: Timeline | null;
|
|
407
413
|
audioBuffer: AudioBuffer | null;
|
|
408
414
|
audioSource: AudioBufferSourceNode | null;
|
|
409
415
|
gainNode: GainNode | null;
|
|
@@ -1,6 +1,5 @@
|
|
|
1
1
|
import { Application } from 'pixi.js';
|
|
2
|
-
import {
|
|
3
|
-
import { _ as Stop, e as AssetSpec, c as CompositionSpec } from './types-DSGwN5w1.js';
|
|
2
|
+
import { a6 as Timeline, _ as Stop, e as AssetSpec, c as CompositionSpec } from './types-DUVefWiB.js';
|
|
4
3
|
|
|
5
4
|
interface AudioInspectOptions {
|
|
6
5
|
/** Width of each loudness window in seconds. Default 0.1. */
|
|
@@ -289,14 +288,21 @@ interface MovieOptions {
|
|
|
289
288
|
interface RenderOptions {
|
|
290
289
|
/** Motion blur for this export (see `MovieOptions.motionBlur`); overrides the movie's own setting, `false` turns it off. */
|
|
291
290
|
motionBlur?: MotionBlurSpec;
|
|
292
|
-
|
|
291
|
+
/** `mp4`, `mov`, `webm`, `mkv`: the picture with its sound. `wav`, `ogg`: only the sound of the movie (an error when it has none). */
|
|
292
|
+
format?: 'mp4' | 'mov' | 'webm' | 'mkv' | 'wav' | 'ogg';
|
|
293
293
|
video?: {
|
|
294
|
+
/** `'avc'` (H.264), `'hevc'`, `'vp9'`, `'av1'`, `'vp8'`: default the usual one for the format, the next that works if this browser cannot encode it; a name you give is never swapped. */
|
|
294
295
|
codec?: string;
|
|
295
|
-
|
|
296
|
+
/** A quality name, or bits a second: `8_000_000` or `'8M'`, `'800k'`. */
|
|
297
|
+
bitrate?: 'very-low' | 'low' | 'medium' | 'high' | 'very-high' | number | string;
|
|
298
|
+
/** Where to encode: `'prefer-hardware'`, `'prefer-software'` or `'no-preference'` (default). */
|
|
299
|
+
hardware?: 'no-preference' | 'prefer-hardware' | 'prefer-software';
|
|
300
|
+
/** Seconds between keyframes (default 2): shorter seeks faster and makes a bigger file. */
|
|
301
|
+
keyFrameInterval?: number;
|
|
296
302
|
};
|
|
297
303
|
audio?: {
|
|
298
304
|
codec?: string;
|
|
299
|
-
bitrate?: 'very-low' | 'low' | 'medium' | 'high' | 'very-high';
|
|
305
|
+
bitrate?: 'very-low' | 'low' | 'medium' | 'high' | 'very-high' | number | string;
|
|
300
306
|
};
|
|
301
307
|
/**
|
|
302
308
|
* Only part of the movie: `[from, to]` in seconds, or the name of a top-level layer (its start to its end). The file starts at 0
|
|
@@ -403,7 +409,7 @@ declare class Movie {
|
|
|
403
409
|
private _events;
|
|
404
410
|
private _initState;
|
|
405
411
|
app: Application | null;
|
|
406
|
-
timeline:
|
|
412
|
+
timeline: Timeline | null;
|
|
407
413
|
audioBuffer: AudioBuffer | null;
|
|
408
414
|
audioSource: AudioBufferSourceNode | null;
|
|
409
415
|
gainNode: GainNode | null;
|
package/dist/Presenter.d.cts
CHANGED
|
@@ -1,7 +1,6 @@
|
|
|
1
|
-
import { j as Movie } from './Movie-
|
|
1
|
+
import { j as Movie } from './Movie-CQ0Nz62a.cjs';
|
|
2
2
|
import 'pixi.js';
|
|
3
|
-
import '
|
|
4
|
-
import './types-DSGwN5w1.cjs';
|
|
3
|
+
import './types-DUVefWiB.cjs';
|
|
5
4
|
|
|
6
5
|
interface PresenterOptions {
|
|
7
6
|
canvas: HTMLCanvasElement;
|
package/dist/Presenter.d.ts
CHANGED
|
@@ -1,7 +1,6 @@
|
|
|
1
|
-
import { j as Movie } from './Movie-
|
|
1
|
+
import { j as Movie } from './Movie-M9dKm9dI.js';
|
|
2
2
|
import 'pixi.js';
|
|
3
|
-
import '
|
|
4
|
-
import './types-DSGwN5w1.js';
|
|
3
|
+
import './types-DUVefWiB.js';
|
|
5
4
|
|
|
6
5
|
interface PresenterOptions {
|
|
7
6
|
canvas: HTMLCanvasElement;
|
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
'use strict';
|
|
2
2
|
|
|
3
|
-
var
|
|
4
|
-
require('./chunk-
|
|
3
|
+
var chunkP4GY3Z4G_cjs = require('./chunk-P4GY3Z4G.cjs');
|
|
4
|
+
require('./chunk-ZMAD3KJU.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
|
-
|
|
71
|
+
chunkP4GY3Z4G_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-
|
|
94
|
-
//# sourceMappingURL=blendModes-
|
|
93
|
+
//# sourceMappingURL=blendModes-HDOP6R7T.cjs.map
|
|
94
|
+
//# sourceMappingURL=blendModes-HDOP6R7T.cjs.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"sources":["../src/filters/blendModes.ts"],"names":["BlendModeFilter","ExtensionType","extensions","setBlendFilterFactory"],"mappings":"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","file":"blendModes-
|
|
1
|
+
{"version":3,"sources":["../src/filters/blendModes.ts"],"names":["BlendModeFilter","ExtensionType","extensions","setBlendFilterFactory"],"mappings":"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","file":"blendModes-HDOP6R7T.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-
|
|
2
|
-
import './chunk-
|
|
1
|
+
import { setBlendFilterFactory } from './chunk-TYWFKCPY.js';
|
|
2
|
+
import './chunk-45QKLD6M.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-
|
|
90
|
-
//# sourceMappingURL=blendModes-
|
|
89
|
+
//# sourceMappingURL=blendModes-PAVTW3BO.js.map
|
|
90
|
+
//# sourceMappingURL=blendModes-PAVTW3BO.js.map
|