pixi-effects 0.16.2 → 0.17.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (52) hide show
  1. package/CHANGELOG.md +39 -0
  2. package/README.md +6 -6
  3. package/ai/CHAT.md +4 -0
  4. package/ai/SKILL.md +2 -1
  5. package/ai/chat-template.html +3 -3
  6. package/ai/reference/cheatsheet.md +4 -2
  7. package/ai/reference/pitfalls.md +3 -0
  8. package/ai/reference/recipes.md +67 -0
  9. package/ai/template.html +5 -5
  10. package/ai/tools/check.mjs +162 -18
  11. package/ai/tools/render.mjs +21 -7
  12. package/dist/Composition-HYAOYPAX.js +5 -0
  13. package/dist/{Composition-VJWY2NDL.js.map → Composition-HYAOYPAX.js.map} +1 -1
  14. package/dist/Composition-LMVEASHF.cjs +14 -0
  15. package/dist/{Composition-3WWNVRR5.cjs.map → Composition-LMVEASHF.cjs.map} +1 -1
  16. package/dist/Controller.cjs +9 -3
  17. package/dist/Controller.cjs.map +1 -1
  18. package/dist/Controller.d.cts +1 -1
  19. package/dist/Controller.d.ts +1 -1
  20. package/dist/Controller.js +9 -3
  21. package/dist/Controller.js.map +1 -1
  22. package/dist/{Movie-oA0SUAyb.d.cts → Movie-CoxF2Z7t.d.cts} +82 -2
  23. package/dist/{Movie-DilBC348.d.ts → Movie-DHJRrUfI.d.ts} +82 -2
  24. package/dist/Presenter.d.cts +1 -1
  25. package/dist/Presenter.d.ts +1 -1
  26. package/dist/{chunk-2ROVCPSL.cjs → chunk-ETAIVOHP.cjs} +32 -27
  27. package/dist/chunk-ETAIVOHP.cjs.map +1 -0
  28. package/dist/{chunk-FJA5VUOF.cjs → chunk-LBJRXU7N.cjs} +15 -3
  29. package/dist/chunk-LBJRXU7N.cjs.map +1 -0
  30. package/dist/{chunk-ZNKQH356.js → chunk-ORTYBA42.js} +15 -4
  31. package/dist/chunk-ORTYBA42.js.map +1 -0
  32. package/dist/{chunk-XTIHKODH.js → chunk-Q3IVBYT7.js} +10 -5
  33. package/dist/chunk-Q3IVBYT7.js.map +1 -0
  34. package/dist/index.cjs +351 -41
  35. package/dist/index.cjs.map +1 -1
  36. package/dist/index.d.cts +1 -1
  37. package/dist/index.d.ts +1 -1
  38. package/dist/index.js +329 -19
  39. package/dist/index.js.map +1 -1
  40. package/dist/loader.css +1 -1
  41. package/dist/three.cjs +5 -5
  42. package/dist/three.js +1 -1
  43. package/docs/dsl.md +2 -1
  44. package/llms-full.txt +78 -4
  45. package/llms.txt +1 -1
  46. package/package.json +6 -3
  47. package/dist/Composition-3WWNVRR5.cjs +0 -14
  48. package/dist/Composition-VJWY2NDL.js +0 -5
  49. package/dist/chunk-2ROVCPSL.cjs.map +0 -1
  50. package/dist/chunk-FJA5VUOF.cjs.map +0 -1
  51. package/dist/chunk-XTIHKODH.js.map +0 -1
  52. package/dist/chunk-ZNKQH356.js.map +0 -1
@@ -13,6 +13,8 @@
13
13
  *
14
14
  * Options: -o, --out FILE (default ./<page name>.<format>; the container follows the extension: mp4 webm mov mkv) ·
15
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) ·
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
+ * --draft (for looking: half size, low quality, no motion blur; a --quality you give still wins) ·
16
18
  * --motion-blur SAMPLES (2-64: each frame is drawn that many times over the shutter and averaged; overrides the page's own motionBlur) ·
17
19
  * --shutter FRACTION (with --motion-blur: how long the shutter is open, 0-1 of a frame, default 0.5) ·
18
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
@@ -41,7 +43,7 @@ export function defaultOutput(page, format) {
41
43
  }
42
44
 
43
45
  export function parseRenderArgs(argv) {
44
- const o = { 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 };
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 };
45
47
  const need = (i, name) => { if (i + 1 >= argv.length) throw new Error(`${name} needs a value`); return argv[i + 1]; };
46
48
  for (let i = 0; i < argv.length; i++) {
47
49
  const a = argv[i];
@@ -50,6 +52,7 @@ export function parseRenderArgs(argv) {
50
52
  o.format = need(i++, a).toLowerCase();
51
53
  if (!FORMATS.includes(o.format)) throw new Error(`--format must be mp4, webm, mov, mkv or pdf (got "${o.format}")`);
52
54
  } else if (a === '--quality') {
55
+ o.qualityGiven = true;
53
56
  o.quality = need(i++, a);
54
57
  if (!QUALITIES.includes(o.quality)) throw new Error(`--quality must be very-low, low, medium, high or very-high (got "${o.quality}")`);
55
58
  } else if (a === '--video-codec') o.videoCodec = need(i++, a);
@@ -60,7 +63,16 @@ export function parseRenderArgs(argv) {
60
63
  } else if (a === '--shutter') {
61
64
  o.shutter = Number(need(i++, a));
62
65
  if (!(o.shutter > 0 && o.shutter <= 1)) throw new Error(`--shutter must be above 0 and at most 1 (0.5 is a 180° shutter), got "${argv[i]}"`);
63
- } else if (a === '--query') o.query = need(i++, a).replace(/^\?/, '');
66
+ } else if (a === '--range') {
67
+ const m = need(i++, a).match(/^(\d+(?:\.\d+)?)?:(\d+(?:\.\d+)?)?$/);
68
+ if (!m || (m[1] === undefined && m[2] === undefined)) throw new Error(`--range must look like 2:5 (seconds; 2: runs to the end, :5 starts at the beginning), got "${argv[i]}"`);
69
+ o.range = [m[1] === undefined ? null : Number(m[1]), m[2] === undefined ? null : Number(m[2])];
70
+ } else if (a === '--scene') o.scene = need(i++, a);
71
+ else if (a === '--scale') {
72
+ o.scale = Number(need(i++, a));
73
+ if (!(o.scale > 0 && o.scale <= 1)) throw new Error(`--scale must be above 0 and at most 1 (0.5 is half size), got "${argv[i]}"`);
74
+ } else if (a === '--draft') o.draft = true;
75
+ else if (a === '--query') o.query = need(i++, a).replace(/^\?/, '');
64
76
  else if (a === '--all-stops') o.allStops = true;
65
77
  else if (a === '--fail-on-warn') o.failOnWarn = true;
66
78
  else if (a === '--quiet') o.quiet = true;
@@ -74,6 +86,8 @@ export function parseRenderArgs(argv) {
74
86
  if (o.shutter !== null && o.motionBlur === null) throw new Error('--shutter goes with --motion-blur SAMPLES');
75
87
  if (o.allStops && o.format !== 'pdf' && !(o.out && formatFromPath(o.out) === 'pdf')) throw new Error('--all-stops goes with a PDF (-o deck.pdf)');
76
88
  o.format ??= (o.out ? formatFromPath(o.out) : null) ?? 'mp4';
89
+ if (o.range && o.scene !== null) throw new Error('--range and --scene cannot be used together: a scene is a range');
90
+ 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)');
77
91
  return o;
78
92
  }
79
93
 
@@ -81,16 +95,16 @@ const UPLOAD = '/__pixi_effects_out';
81
95
  const kb = n => (n >= 1048576 ? `${(n / 1048576).toFixed(2)} MB` : `${Math.max(1, Math.round(n / 1024))} KB`);
82
96
 
83
97
  /** The page's side: render, and PUT the Blob to our server (so a long video never goes through base64). */
84
- const renderScript = (format, quality, videoCodec, audioCodec, motionBlur, shutter, allStops) => `(() => {
98
+ const renderScript = (format, quality, videoCodec, audioCodec, motionBlur, shutter, allStops, part = {}) => `(() => {
85
99
  window.__r = { state: 'running', progress: 0 };
86
100
  movie.on('progress', e => { window.__r.progress = e.progress; });
87
101
  (async () => {
88
102
  try {
89
- const video = { bitrate: ${JSON.stringify(quality)}${videoCodec ? `, codec: ${JSON.stringify(videoCodec)}` : ''} };
103
+ const video = { ${[part.draft && !part.qualityGiven ? null : `bitrate: ${JSON.stringify(quality)}`, videoCodec ? `codec: ${JSON.stringify(videoCodec)}` : null].filter(Boolean).join(', ')} };
90
104
  const audio = { bitrate: ${JSON.stringify(quality)}${audioCodec ? `, codec: ${JSON.stringify(audioCodec)}` : ''} };
91
105
  const blob = ${JSON.stringify(format)} === 'pdf'
92
106
  ? await movie.exportPDF({ which: ${allStops ? "'stops'" : "'pages'"}, title: document.title })
93
- : await movie.render({ format: ${JSON.stringify(format)}, video, audio${motionBlur ? `, motionBlur: { samples: ${motionBlur}${shutter ? `, shutter: ${shutter}` : ''} }` : ''} });
107
+ : 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}` : ''} }` : ''} });
94
108
  const res = await fetch(${JSON.stringify(UPLOAD)}, { method: 'PUT', body: blob });
95
109
  if (!res.ok) throw new Error('could not hand the file to the command (' + res.status + ')');
96
110
  window.__r = { state: 'done', bytes: blob.size, type: blob.type };
@@ -156,7 +170,7 @@ export async function runRender(opts, log = console.log, progress = () => {}) {
156
170
  }
157
171
  Object.assign(result, { duration: info.duration, frames: info.totalFrames, width: info.width, height: info.height, frameRate: info.frameRate });
158
172
 
159
- await cdp.eval(renderScript(opts.format, opts.quality, opts.videoCodec, opts.audioCodec, opts.motionBlur, opts.shutter, opts.allStops));
173
+ 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 }));
160
174
  let state;
161
175
  for (;;) {
162
176
  left();
@@ -190,7 +204,7 @@ if (process.argv[1] && import.meta.url === pathToFileURL(process.argv[1]).href)
190
204
  if (typeof WebSocket === 'undefined') throw new Error('Node >= 22 is needed (built-in WebSocket)');
191
205
  const opts = parseRenderArgs(process.argv.slice(2));
192
206
  if (!opts.page) {
193
- console.error('usage: node ai/tools/render.mjs <page.html> [-o out.mp4] [--format mp4|webm|mov|mkv|pdf] [--quality very-low|low|medium|high|very-high] [--query a=1] [--fail-on-warn] [--quiet] [--timeout S] [--root DIR] [--chrome PATH]');
207
+ console.error('usage: node ai/tools/render.mjs <page.html> [-o out.mp4] [--format mp4|webm|mov|mkv|pdf] [--range A:B | --scene NAME] [--scale S] [--draft] [--quality very-low|low|medium|high|very-high] [--query a=1] [--fail-on-warn] [--quiet] [--timeout S] [--root DIR] [--chrome PATH]');
194
208
  process.exit(2);
195
209
  }
196
210
  let last = -1;
@@ -0,0 +1,5 @@
1
+ export { CompositionSequence } from './chunk-Q3IVBYT7.js';
2
+ import './chunk-ORTYBA42.js';
3
+ import './chunk-DKGG7WD4.js';
4
+ //# sourceMappingURL=Composition-HYAOYPAX.js.map
5
+ //# sourceMappingURL=Composition-HYAOYPAX.js.map
@@ -1 +1 @@
1
- {"version":3,"sources":[],"names":[],"mappings":"","file":"Composition-VJWY2NDL.js"}
1
+ {"version":3,"sources":[],"names":[],"mappings":"","file":"Composition-HYAOYPAX.js"}
@@ -0,0 +1,14 @@
1
+ 'use strict';
2
+
3
+ var chunkETAIVOHP_cjs = require('./chunk-ETAIVOHP.cjs');
4
+ require('./chunk-LBJRXU7N.cjs');
5
+ require('./chunk-UQBGW5DO.cjs');
6
+
7
+
8
+
9
+ Object.defineProperty(exports, "CompositionSequence", {
10
+ enumerable: true,
11
+ get: function () { return chunkETAIVOHP_cjs.CompositionSequence; }
12
+ });
13
+ //# sourceMappingURL=Composition-LMVEASHF.cjs.map
14
+ //# sourceMappingURL=Composition-LMVEASHF.cjs.map
@@ -1 +1 @@
1
- {"version":3,"sources":[],"names":[],"mappings":"","file":"Composition-3WWNVRR5.cjs"}
1
+ {"version":3,"sources":[],"names":[],"mappings":"","file":"Composition-LMVEASHF.cjs"}
@@ -919,6 +919,11 @@ var Controller = class _Controller {
919
919
  async handleExport() {
920
920
  if (this.isExporting) return;
921
921
  this.isExporting = true;
922
+ this.setVisible(true);
923
+ if (this.hideTimer) {
924
+ clearTimeout(this.hideTimer);
925
+ this.hideTimer = null;
926
+ }
922
927
  const wasPlaying = this.movie.isPlaying;
923
928
  if (wasPlaying) {
924
929
  this.movie.pause();
@@ -951,6 +956,7 @@ var Controller = class _Controller {
951
956
  } finally {
952
957
  this.hideExportOverlay();
953
958
  this.isExporting = false;
959
+ this.kickIdleTimer();
954
960
  if (wasPlaying) {
955
961
  void this.startPlaying();
956
962
  this.refreshPlayIcon();
@@ -1014,7 +1020,7 @@ var Controller = class _Controller {
1014
1020
  const wrap = this.wrapper;
1015
1021
  this.onWrapPointerMove = () => this.kickIdleTimer();
1016
1022
  this.onWrapMouseLeave = () => {
1017
- if (!this.settingsOpen && !this.isScrubbing && !this.isVolumeScrubbing) this.setVisible(false);
1023
+ if (!this.settingsOpen && !this.isScrubbing && !this.isVolumeScrubbing && !this.isExporting) this.setVisible(false);
1018
1024
  };
1019
1025
  wrap.addEventListener("pointermove", this.onWrapPointerMove);
1020
1026
  wrap.addEventListener("mouseleave", this.onWrapMouseLeave);
@@ -1032,9 +1038,9 @@ var Controller = class _Controller {
1032
1038
  clearTimeout(this.hideTimer);
1033
1039
  this.hideTimer = null;
1034
1040
  }
1035
- if (this.settingsOpen) return;
1041
+ if (this.settingsOpen || this.isExporting) return;
1036
1042
  this.hideTimer = setTimeout(() => {
1037
- if (!this.isScrubbing && !this.isVolumeScrubbing && !this.settingsOpen) this.setVisible(false);
1043
+ if (!this.isScrubbing && !this.isVolumeScrubbing && !this.settingsOpen && !this.isExporting) this.setVisible(false);
1038
1044
  }, _Controller.HIDE_DELAY_MS);
1039
1045
  }
1040
1046
  setVisible(v) {