pixi-effects 0.6.0 → 0.8.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 +29 -0
  2. package/README.md +18 -11
  3. package/ai/SKILL.md +1 -1
  4. package/ai/reference/cheatsheet.md +9 -1
  5. package/ai/template.html +4 -3
  6. package/ai/tools/check.mjs +14 -5
  7. package/ai/tools/render.mjs +202 -0
  8. package/ai/tools/view.mjs +269 -0
  9. package/dist/{Base-4vNLl4tH.d.cts → Base-CSIulyIq.d.cts} +1 -1
  10. package/dist/{Base-CD4AoT5-.d.ts → Base-DVgwyIUj.d.ts} +1 -1
  11. package/dist/Composition-CM62XJKT.cjs +14 -0
  12. package/dist/{Composition-Y5VZSDDP.cjs.map → Composition-CM62XJKT.cjs.map} +1 -1
  13. package/dist/Composition-RJDSQYTV.js +5 -0
  14. package/dist/{Composition-7SO5WYHU.js.map → Composition-RJDSQYTV.js.map} +1 -1
  15. package/dist/Controller.cjs +122 -20
  16. package/dist/Controller.cjs.map +1 -1
  17. package/dist/Controller.d.cts +45 -3
  18. package/dist/Controller.d.ts +45 -3
  19. package/dist/Controller.js +122 -21
  20. package/dist/Controller.js.map +1 -1
  21. package/dist/{Movie-Ch2SUI1h.d.ts → Movie-CQOhUmko.d.cts} +75 -2
  22. package/dist/{Movie-CADDBfpl.d.cts → Movie-HF3pSGjQ.d.ts} +75 -2
  23. package/dist/{chunk-KQM2ZZMK.cjs → chunk-AGMNSJJH.cjs} +175 -29
  24. package/dist/chunk-AGMNSJJH.cjs.map +1 -0
  25. package/dist/{chunk-7IJHJXDI.js → chunk-FNKSXBKB.js} +136 -10
  26. package/dist/chunk-FNKSXBKB.js.map +1 -0
  27. package/dist/{chunk-B2FTJLNB.js → chunk-O4AJYUPK.js} +3 -3
  28. package/dist/{chunk-B2FTJLNB.js.map → chunk-O4AJYUPK.js.map} +1 -1
  29. package/dist/{chunk-4AGJF5RK.cjs → chunk-PDBDDGG5.cjs} +21 -21
  30. package/dist/{chunk-4AGJF5RK.cjs.map → chunk-PDBDDGG5.cjs.map} +1 -1
  31. package/dist/index.cjs +255 -25
  32. package/dist/index.cjs.map +1 -1
  33. package/dist/index.d.cts +4 -4
  34. package/dist/index.d.ts +4 -4
  35. package/dist/index.js +248 -18
  36. package/dist/index.js.map +1 -1
  37. package/dist/three.cjs +6 -5
  38. package/dist/three.cjs.map +1 -1
  39. package/dist/three.d.cts +2 -2
  40. package/dist/three.d.ts +2 -2
  41. package/dist/three.js +2 -1
  42. package/dist/three.js.map +1 -1
  43. package/dist/{types-DrW9QeKU.d.ts → types-oJijbn-K.d.cts} +14 -1
  44. package/dist/{types-DrW9QeKU.d.cts → types-oJijbn-K.d.ts} +14 -1
  45. package/docs/api.md +57 -0
  46. package/docs/dsl.md +60 -1
  47. package/llms-full.txt +127 -3
  48. package/package.json +7 -4
  49. package/dist/Composition-7SO5WYHU.js +0 -4
  50. package/dist/Composition-Y5VZSDDP.cjs +0 -13
  51. package/dist/chunk-7IJHJXDI.js.map +0 -1
  52. package/dist/chunk-KQM2ZZMK.cjs.map +0 -1
@@ -1,3 +1,5 @@
1
+ import { warnUnknownOptions } from './chunk-DKGG7WD4.js';
2
+
1
3
  // src/Controller.ts
2
4
  var ICONS = {
3
5
  play: '<svg viewBox="0 0 16 16" width="16" height="16" fill="currentColor"><path d="M3 2 L13 8 L3 14 Z"/></svg>',
@@ -9,12 +11,12 @@ var ICONS = {
9
11
  fullscreenExit: '<svg viewBox="0 0 16 16" width="16" height="16" fill="none" stroke="currentColor" stroke-width="1.4" stroke-linecap="round" stroke-linejoin="round"><path d="M6 2 V6 H2 M10 2 V6 H14 M6 14 V10 H2 M10 14 V10 H14"/></svg>'
10
12
  };
11
13
  var STYLE_ATTR = "data-movie-controller";
12
- var STYLE_CSS = `
14
+ var CONTROLLER_CSS = `
13
15
  .movie-controller-wrap { position: relative; display: inline-block; line-height: 0; }
14
16
  .movie-controller {
15
17
  position: absolute;
16
18
  pointer-events: none;
17
- font-family: -apple-system, BlinkMacSystemFont, "Segoe UI", Roboto, sans-serif;
19
+ font-family: var(--mc-font, -apple-system, BlinkMacSystemFont, "Segoe UI", Roboto, sans-serif);
18
20
  display: flex;
19
21
  flex-direction: column;
20
22
  justify-content: flex-end;
@@ -34,31 +36,31 @@ var STYLE_CSS = `
34
36
  outline: none;
35
37
  }
36
38
  .mc-progress:focus-visible {
37
- outline: 2px solid #007AFF;
39
+ outline: 2px solid var(--mc-accent, #007AFF);
38
40
  outline-offset: 2px;
39
41
  }
40
42
  .mc-progress::before {
41
43
  content: ""; position: absolute; left: 12px; right: 12px;
42
- height: 3px; background: rgba(255,255,255,0.25);
44
+ height: var(--mc-track-height, 3px); background: var(--mc-track, rgba(255,255,255,0.25));
43
45
  transition: height 120ms ease;
44
46
  }
45
- .mc-progress:hover::before, .mc-progress.mc-scrubbing::before { height: 5px; }
47
+ .mc-progress:hover::before, .mc-progress.mc-scrubbing::before { height: calc(var(--mc-track-height, 3px) + 2px); }
46
48
  .mc-progress-fill {
47
49
  position: absolute; left: 12px; top: 50%;
48
- height: 3px;
50
+ height: var(--mc-track-height, 3px);
49
51
  width: calc((100% - 24px) * var(--mc-fill, 0));
50
- background: #007AFF;
52
+ background: var(--mc-accent, #007AFF);
51
53
  transform: translateY(-50%);
52
54
  transition: height 120ms ease;
53
55
  pointer-events: none;
54
56
  }
55
57
  .mc-progress:hover .mc-progress-fill,
56
- .mc-progress.mc-scrubbing .mc-progress-fill { height: 5px; }
58
+ .mc-progress.mc-scrubbing .mc-progress-fill { height: calc(var(--mc-track-height, 3px) + 2px); }
57
59
  .mc-progress-thumb {
58
60
  position: absolute; top: 50%;
59
61
  left: calc(12px + (100% - 24px) * var(--mc-fill, 0));
60
62
  width: 12px; height: 12px; border-radius: 50%;
61
- background: #007AFF;
63
+ background: var(--mc-accent, #007AFF);
62
64
  transform: translate(-50%, -50%) scale(0);
63
65
  transition: transform 120ms ease;
64
66
  pointer-events: none;
@@ -72,22 +74,22 @@ var STYLE_CSS = `
72
74
  align-items: center;
73
75
  gap: 12px;
74
76
  padding: 6px 12px 8px 12px;
75
- background: linear-gradient(to top, rgba(0,0,0,0.75) 0%, rgba(0,0,0,0) 100%);
76
- color: #fff;
77
+ background: var(--mc-bar-bg, linear-gradient(to top, rgba(0,0,0,0.75) 0%, rgba(0,0,0,0) 100%));
78
+ color: var(--mc-fg, #fff);
77
79
  line-height: 1;
78
80
  }
79
81
  .mc-btn {
80
82
  background: none; border: 0; padding: 0; margin: 0;
81
83
  width: 28px; height: 28px;
82
84
  display: inline-flex; align-items: center; justify-content: center;
83
- color: #fff; opacity: 0.85; cursor: pointer;
85
+ color: var(--mc-fg, #fff); opacity: 0.85; cursor: pointer;
84
86
  transition: opacity 120ms ease;
85
87
  }
86
88
  .mc-btn:hover { opacity: 1; }
87
89
  .mc-btn:disabled { opacity: 0.4; cursor: not-allowed; }
88
90
  .mc-time {
89
91
  font-size: 12px; font-family: Menlo, Monaco, monospace;
90
- color: #fff; opacity: 0.85;
92
+ color: var(--mc-fg, #fff); opacity: 0.85;
91
93
  min-width: 90px;
92
94
  }
93
95
  .mc-spacer { flex: 1; }
@@ -111,16 +113,16 @@ var STYLE_CSS = `
111
113
  width: 70px;
112
114
  margin-left: 6px;
113
115
  }
114
- .mc-vol-slider:focus-visible { outline: 2px solid #007AFF; outline-offset: 2px; }
116
+ .mc-vol-slider:focus-visible { outline: 2px solid var(--mc-accent, #007AFF); outline-offset: 2px; }
115
117
  .mc-vol-slider::before {
116
118
  content: ""; position: absolute; left: 4px; right: 4px;
117
- height: 3px; background: rgba(255,255,255,0.25); border-radius: 2px;
119
+ height: 3px; background: var(--mc-track, rgba(255,255,255,0.25)); border-radius: 2px;
118
120
  }
119
121
  .mc-vol-fill {
120
122
  position: absolute; left: 4px; top: 50%;
121
123
  height: 3px;
122
124
  width: calc((100% - 8px) * var(--mc-volume, 1));
123
- background: #fff;
125
+ background: var(--mc-fg, #fff);
124
126
  transform: translateY(-50%);
125
127
  border-radius: 2px;
126
128
  pointer-events: none;
@@ -129,7 +131,7 @@ var STYLE_CSS = `
129
131
  position: absolute; top: 50%;
130
132
  left: calc(4px + (100% - 8px) * var(--mc-volume, 1));
131
133
  width: 10px; height: 10px; border-radius: 50%;
132
- background: #fff;
134
+ background: var(--mc-fg, #fff);
133
135
  transform: translate(-50%, -50%);
134
136
  pointer-events: none;
135
137
  }
@@ -183,7 +185,7 @@ var STYLE_CSS = `
183
185
  .mc-export-confirm {
184
186
  margin-top: 4px;
185
187
  width: 100%;
186
- background: #007AFF;
188
+ background: var(--mc-accent, #007AFF);
187
189
  color: #fff;
188
190
  border: 0;
189
191
  border-radius: 4px;
@@ -216,7 +218,7 @@ var STYLE_CSS = `
216
218
  }
217
219
  .mc-export-fill {
218
220
  height: 100%; width: 0%;
219
- background: linear-gradient(90deg, #007AFF, #0056CC);
221
+ background: linear-gradient(90deg, var(--mc-accent, #007AFF), var(--mc-accent, #0056CC));
220
222
  transition: width 0.3s ease; border-radius: 3px;
221
223
  }
222
224
  .mc-export-text {
@@ -229,7 +231,7 @@ function installStyles() {
229
231
  if (styleRefCount === 0) {
230
232
  const el = document.createElement("style");
231
233
  el.setAttribute(STYLE_ATTR, "");
232
- el.textContent = STYLE_CSS;
234
+ el.textContent = CONTROLLER_CSS;
233
235
  document.head.appendChild(el);
234
236
  }
235
237
  styleRefCount++;
@@ -271,12 +273,25 @@ function extensionForMimeType(mime) {
271
273
  if (t.includes("matroska") || t.includes("mkv")) return "mkv";
272
274
  return "mp4";
273
275
  }
276
+ var THEME_PROPERTIES = {
277
+ accent: "--mc-accent",
278
+ foreground: "--mc-fg",
279
+ track: "--mc-track",
280
+ barBackground: "--mc-bar-bg",
281
+ trackHeight: "--mc-track-height",
282
+ font: "--mc-font"
283
+ };
274
284
  var Controller = class _Controller {
275
285
  movie;
276
286
  options;
277
287
  root;
278
288
  wrapper;
279
289
  wrappedHere = false;
290
+ /** The canvas's own inline width / height, as the page wrote them (we override them while the wrapper is fitted). */
291
+ canvasInlineWidth = "";
292
+ canvasInlineHeight = "";
293
+ fitWrapHandler = null;
294
+ fitObserver = null;
280
295
  destroyed = false;
281
296
  progressEl;
282
297
  progressFillEl;
@@ -338,6 +353,8 @@ var Controller = class _Controller {
338
353
  this.root.className = this.options.className;
339
354
  this.wrapper.appendChild(this.root);
340
355
  this.buildBar();
356
+ this.setTheme(options.theme ?? {});
357
+ this.bindFitWrap();
341
358
  this.syncRootToCanvas();
342
359
  if (typeof ResizeObserver !== "undefined") {
343
360
  this.resizeObserver = new ResizeObserver(() => this.syncRootToCanvas());
@@ -416,6 +433,17 @@ var Controller = class _Controller {
416
433
  if (this.settingsFormatSelect) this.settingsFormatSelect.value = this.exportFormat;
417
434
  if (this.settingsQualitySelect) this.settingsQualitySelect.value = this.exportQuality;
418
435
  }
436
+ /** Change the look of the bar (see {@link ControllerTheme}): only the keys you give change, `null` restores the default. */
437
+ setTheme(theme) {
438
+ warnUnknownOptions("Controller theme", theme, Object.keys(THEME_PROPERTIES));
439
+ for (const key of Object.keys(THEME_PROPERTIES)) {
440
+ if (!(key in theme)) continue;
441
+ const value = theme[key];
442
+ const prop = THEME_PROPERTIES[key];
443
+ if (value === null || value === void 0 || value === "") this.root.style.removeProperty(prop);
444
+ else this.root.style.setProperty(prop, typeof value === "number" ? `${value}px` : String(value));
445
+ }
446
+ }
419
447
  ensurePositioningContext(canvas) {
420
448
  const parent = canvas.parentElement;
421
449
  if (!parent) {
@@ -435,8 +463,56 @@ var Controller = class _Controller {
435
463
  parent.insertBefore(wrap, canvas);
436
464
  wrap.appendChild(canvas);
437
465
  this.wrappedHere = true;
466
+ this.canvasInlineWidth = canvas.style.width;
467
+ this.canvasInlineHeight = canvas.style.height;
438
468
  return wrap;
439
469
  }
470
+ /**
471
+ * Make the wrapper exactly as wide as the canvas. A shrink-to-fit wrapper around a canvas that is sized in percent (the usual
472
+ * `canvas { width: min(960px, 100%) }`) is as wide as the canvas's width ATTRIBUTE (1280), so the picture sat at its left edge
473
+ * with an empty strip beside it. We measure the canvas as the page styled it (the wrapper made transparent to layout), give
474
+ * the wrapper that width, and let the canvas fill the wrapper. Re-measured when the window or the page's box changes.
475
+ */
476
+ fitWrap() {
477
+ if (!this.wrappedHere) return;
478
+ const wrap = this.wrapper, canvas = this.options.canvas;
479
+ if (document.fullscreenElement === wrap) {
480
+ wrap.style.width = "";
481
+ canvas.style.width = this.canvasInlineWidth;
482
+ canvas.style.height = this.canvasInlineHeight;
483
+ return;
484
+ }
485
+ canvas.style.width = this.canvasInlineWidth;
486
+ canvas.style.height = this.canvasInlineHeight;
487
+ const display = wrap.style.display;
488
+ wrap.style.display = "contents";
489
+ wrap.style.width = "";
490
+ const w = canvas.getBoundingClientRect().width;
491
+ wrap.style.display = display;
492
+ if (!(w > 0)) return;
493
+ wrap.style.width = `${w}px`;
494
+ canvas.style.width = "100%";
495
+ canvas.style.height = "auto";
496
+ }
497
+ bindFitWrap() {
498
+ if (!this.wrappedHere) return;
499
+ this.fitWrap();
500
+ let queued = false;
501
+ this.fitWrapHandler = () => {
502
+ if (queued) return;
503
+ queued = true;
504
+ requestAnimationFrame(() => {
505
+ queued = false;
506
+ this.fitWrap();
507
+ });
508
+ };
509
+ window.addEventListener("resize", this.fitWrapHandler);
510
+ const parent = this.wrapper.parentElement;
511
+ if (parent && typeof ResizeObserver !== "undefined") {
512
+ this.fitObserver = new ResizeObserver(this.fitWrapHandler);
513
+ this.fitObserver.observe(parent);
514
+ }
515
+ }
440
516
  bindMovieEvents() {
441
517
  this.onReady = () => {
442
518
  this.progressEl.setAttribute("aria-valuemax", String(this.movie.totalFrames));
@@ -549,12 +625,25 @@ var Controller = class _Controller {
549
625
  };
550
626
  container.addEventListener("wheel", this.wheelHandler, { passive: false });
551
627
  }
628
+ /**
629
+ * Does this browser have the Fullscreen API for an element? iPhone Safari does not (it can only put a <video> in fullscreen),
630
+ * and a button that does nothing is worse than none.
631
+ */
632
+ supportsFullscreen() {
633
+ return typeof this.wrapper.requestFullscreen === "function" && document.fullscreenEnabled !== false;
634
+ }
552
635
  bindFullscreen() {
553
636
  if (!this.fullscreenBtn) return;
637
+ if (!this.supportsFullscreen()) {
638
+ this.fullscreenBtn.remove();
639
+ this.fullscreenBtn = null;
640
+ return;
641
+ }
554
642
  this.fullscreenBtn.addEventListener("click", () => {
555
643
  void this.toggleFullscreen();
556
644
  });
557
645
  this.fullscreenChangeHandler = () => {
646
+ this.fitWrap();
558
647
  this.refreshFullscreenIcon();
559
648
  this.syncRootToCanvas();
560
649
  };
@@ -912,6 +1001,18 @@ var Controller = class _Controller {
912
1001
  this.resizeObserver.disconnect();
913
1002
  this.resizeObserver = null;
914
1003
  }
1004
+ if (this.fitObserver) {
1005
+ this.fitObserver.disconnect();
1006
+ this.fitObserver = null;
1007
+ }
1008
+ if (this.fitWrapHandler) {
1009
+ window.removeEventListener("resize", this.fitWrapHandler);
1010
+ this.fitWrapHandler = null;
1011
+ }
1012
+ if (this.wrappedHere) {
1013
+ this.options.canvas.style.width = this.canvasInlineWidth;
1014
+ this.options.canvas.style.height = this.canvasInlineHeight;
1015
+ }
915
1016
  this.root.remove();
916
1017
  if (this.wrappedHere) {
917
1018
  const parent = this.wrapper.parentElement;
@@ -960,6 +1061,6 @@ var Controller = class _Controller {
960
1061
  }
961
1062
  };
962
1063
 
963
- export { Controller, extensionForMimeType, formatTime, frameToPercent, pxToFraction, pxToFrame };
1064
+ export { CONTROLLER_CSS, Controller, extensionForMimeType, formatTime, frameToPercent, pxToFraction, pxToFrame };
964
1065
  //# sourceMappingURL=Controller.js.map
965
1066
  //# sourceMappingURL=Controller.js.map