pixi-effects 0.40.0 → 0.42.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 (57) hide show
  1. package/CHANGELOG.md +17 -0
  2. package/README.md +5 -5
  3. package/ai/chat-template.html +3 -3
  4. package/ai/tools/check.mjs +19 -2
  5. package/dist/Composition-N6BOROJ2.cjs +16 -0
  6. package/dist/{Composition-C3UEVHQG.cjs.map → Composition-N6BOROJ2.cjs.map} +1 -1
  7. package/dist/Composition-YGBQX3HZ.js +7 -0
  8. package/dist/{Composition-KRWH72MA.js.map → Composition-YGBQX3HZ.js.map} +1 -1
  9. package/dist/Controller.d.cts +1 -1
  10. package/dist/Controller.d.ts +1 -1
  11. package/dist/{Movie-CMq4KFmc.d.cts → Movie-BHGhg-fJ.d.cts} +88 -1
  12. package/dist/{Movie-CG6Jy1tx.d.ts → Movie-CBVFZ1kg.d.ts} +88 -1
  13. package/dist/{Spans-TWO4VN5H.js → Spans-6L2ZVECX.js} +4 -4
  14. package/dist/{Spans-TWO4VN5H.js.map → Spans-6L2ZVECX.js.map} +1 -1
  15. package/dist/{Spans-APNJHCYO.cjs → Spans-XTIWYSNO.cjs} +9 -9
  16. package/dist/{Spans-APNJHCYO.cjs.map → Spans-XTIWYSNO.cjs.map} +1 -1
  17. package/dist/{chunk-27PYJ4JY.cjs → chunk-3HZV3UHU.cjs} +67 -63
  18. package/dist/chunk-3HZV3UHU.cjs.map +1 -0
  19. package/dist/{chunk-LMWXR5OC.js → chunk-4QVQ5UJJ.js} +4 -4
  20. package/dist/{chunk-LMWXR5OC.js.map → chunk-4QVQ5UJJ.js.map} +1 -1
  21. package/dist/{chunk-26Z544SH.js → chunk-ICI6ACHY.js} +7 -3
  22. package/dist/chunk-ICI6ACHY.js.map +1 -0
  23. package/dist/{chunk-CT5CZVT5.js → chunk-NFJKBWYO.js} +243 -4
  24. package/dist/chunk-NFJKBWYO.js.map +1 -0
  25. package/dist/{chunk-LEL7UNYQ.cjs → chunk-WVZLAIKK.cjs} +278 -39
  26. package/dist/chunk-WVZLAIKK.cjs.map +1 -0
  27. package/dist/{chunk-7EI2N7WO.cjs → chunk-X62H3RBG.cjs} +4 -4
  28. package/dist/{chunk-7EI2N7WO.cjs.map → chunk-X62H3RBG.cjs.map} +1 -1
  29. package/dist/index.cjs +33 -33
  30. package/dist/index.d.cts +1 -1
  31. package/dist/index.d.ts +1 -1
  32. package/dist/index.js +3 -3
  33. package/dist/loader.css +1 -1
  34. package/dist/present.cjs +3 -3
  35. package/dist/present.d.cts +1 -1
  36. package/dist/present.d.ts +1 -1
  37. package/dist/present.js +3 -3
  38. package/dist/three.cjs +86 -7
  39. package/dist/three.cjs.map +1 -1
  40. package/dist/three.d.cts +27 -1
  41. package/dist/three.d.ts +27 -1
  42. package/dist/three.js +83 -4
  43. package/dist/three.js.map +1 -1
  44. package/docs/api.md +37 -0
  45. package/docs/dsl.md +1 -1
  46. package/llms-full.txt +173 -3
  47. package/package.json +1 -1
  48. package/skills/pixi-effects/SKILL.md +3 -2
  49. package/skills/pixi-effects/reference/cheatsheet.md +3 -0
  50. package/skills/pixi-effects/reference/recipes.md +129 -0
  51. package/skills/pixi-effects/template.html +6 -6
  52. package/dist/Composition-C3UEVHQG.cjs +0 -16
  53. package/dist/Composition-KRWH72MA.js +0 -7
  54. package/dist/chunk-26Z544SH.js.map +0 -1
  55. package/dist/chunk-27PYJ4JY.cjs.map +0 -1
  56. package/dist/chunk-CT5CZVT5.js.map +0 -1
  57. package/dist/chunk-LEL7UNYQ.cjs.map +0 -1
@@ -1,7 +1,7 @@
1
1
  'use strict';
2
2
 
3
- var chunk27PYJ4JY_cjs = require('./chunk-27PYJ4JY.cjs');
4
- var chunk7EI2N7WO_cjs = require('./chunk-7EI2N7WO.cjs');
3
+ var chunk3HZV3UHU_cjs = require('./chunk-3HZV3UHU.cjs');
4
+ var chunkX62H3RBG_cjs = require('./chunk-X62H3RBG.cjs');
5
5
  var chunkLQDDDIC4_cjs = require('./chunk-LQDDDIC4.cjs');
6
6
  var chunkJN7JHNZR_cjs = require('./chunk-JN7JHNZR.cjs');
7
7
  var chunk3SAS3ZE5_cjs = require('./chunk-3SAS3ZE5.cjs');
@@ -1105,8 +1105,8 @@ function expandTransitions(spec) {
1105
1105
  }
1106
1106
  const transitions = out.transitions;
1107
1107
  const sequences = out.sequences ?? [];
1108
- const remap = out.duration !== void 0 ? chunk7EI2N7WO_cjs.timeRemapOf(out) : null;
1109
- const parentDuration = remap ? chunk7EI2N7WO_cjs.contentLength(remap, out.duration) : out.duration ?? Infinity;
1108
+ const remap = out.duration !== void 0 ? chunkX62H3RBG_cjs.timeRemapOf(out) : null;
1109
+ const parentDuration = remap ? chunkX62H3RBG_cjs.contentLength(remap, out.duration) : out.duration ?? Infinity;
1110
1110
  const compW = out.width;
1111
1111
  const compH = out.height;
1112
1112
  const threeDByName = new Set(sequences.filter((s) => s.name && s.threeD).map((s) => s.name));
@@ -1174,16 +1174,16 @@ function expandTransitions(spec) {
1174
1174
  if (toUses > 1) {
1175
1175
  throw new Error(`pixi-effects: ${tag} sequence "${t.to}" is used as \`to\` in more than one transition`);
1176
1176
  }
1177
- const tStart = chunk7EI2N7WO_cjs.resolveAt(t.at, parentDuration);
1177
+ const tStart = chunkX62H3RBG_cjs.resolveAt(t.at, parentDuration);
1178
1178
  const tEnd = tStart + t.duration;
1179
1179
  const fromSeq = fromEntry.seq;
1180
- const fromStart = chunk7EI2N7WO_cjs.resolveAt(fromSeq.at, parentDuration);
1180
+ const fromStart = chunkX62H3RBG_cjs.resolveAt(fromSeq.at, parentDuration);
1181
1181
  const fromEnd = fromStart + (fromSeq.duration ?? parentDuration);
1182
1182
  if (tStart < fromStart - EPS || tEnd > fromEnd + EPS) {
1183
1183
  throw new Error(`pixi-effects: ${tag} window [${tStart}, ${tEnd}] is not covered by \`from\` "${t.from}" (lives [${fromStart}, ${fromEnd}], so it ends at ${fromEnd})`);
1184
1184
  }
1185
1185
  const toSeq = toEntry.seq;
1186
- const toStart = chunk7EI2N7WO_cjs.resolveAt(toSeq.at, parentDuration);
1186
+ const toStart = chunkX62H3RBG_cjs.resolveAt(toSeq.at, parentDuration);
1187
1187
  const toEnd = toStart + (toSeq.duration ?? parentDuration);
1188
1188
  if (tStart < toStart - EPS || tEnd > toEnd + EPS) {
1189
1189
  throw new Error(`pixi-effects: ${tag} window [${tStart}, ${tEnd}] is not covered by \`to\` "${t.to}" (lives [${toStart}, ${toEnd}], so it starts at ${toStart})`);
@@ -1243,7 +1243,7 @@ function wrapAsFullComposition(seq, compW, compH) {
1243
1243
  delete inner.at;
1244
1244
  delete inner.duration;
1245
1245
  const m = inner.mask;
1246
- const carriesMatte = m !== void 0 && chunk27PYJ4JY_cjs.maskSourceOf(m).kind === "refs";
1246
+ const carriesMatte = m !== void 0 && chunk3HZV3UHU_cjs.maskSourceOf(m).kind === "refs";
1247
1247
  if (carriesMatte) delete inner.mask;
1248
1248
  return {
1249
1249
  ...carriesMatte ? { mask: m } : {},
@@ -1513,7 +1513,7 @@ function canvasReport(root) {
1513
1513
  const problem = threw.length ? `${name} could not draw at ${threw.map((t) => `${t} s`).join(", ")} (its ${kind === "three" ? "update or render threw" : "draw threw"}, or the layer failed to build): ${kind === "three" ? "the warning above says why" : "it shows a magenta checkerboard there; the warning above says why"}, so the layer cannot be called deterministic` : differs.length ? `${name} does not draw the same picture for the same time (checked at ${differs.map(describeDiff).join(", ")}; drawn in two orders after one draw that was thrown away): ` + (kind === "three" ? "usually its update uses Math.random, Date.now, a clock, or state that builds up from call to call: a mixer advanced with mixer.update(dt) is one (pose a clip with mixer.setTime(t), or threeGltf(url).pose(t)), and so is a value moved by the time since the last call. A skinned model of your own (not loaded by threeGltf) that moves and casts a shadow does it too: three culls a skinned mesh by a bounding sphere that does not follow it, so set mesh.frustumCulled = false. Set every object from t, so playback, a seek and an export agree" : "usually its draw uses Math.random, Date.now or state that outlives the call (a value it keeps from the last call), so playback, a seek and an export would not agree: use info.random() (seeded) and derive everything from t. If it uses none of these, look at the canvases it draws from: make sprites and masks with info.canvas(w, h) (not document.createElement) and draw them into the picture before the strokes that depend on them") : "";
1514
1514
  out.push({ name: seq.spec.name ?? name, moments, drawMs, ok: !differs.length && !threw.length, problem, kind });
1515
1515
  }
1516
- if (seq instanceof chunk27PYJ4JY_cjs.CompositionSequence) walk2(seq);
1516
+ if (seq instanceof chunk3HZV3UHU_cjs.CompositionSequence) walk2(seq);
1517
1517
  };
1518
1518
  const walk2 = (comp) => {
1519
1519
  for (const { seq } of comp.layers()) {
@@ -1565,8 +1565,8 @@ function motionOf(keyframes, local, lives, keys = (k) => GEOMETRY.has(k)) {
1565
1565
  let length = 0;
1566
1566
  if (typeof kf.duration === "number") length = kf.duration;
1567
1567
  else if (kf.duration === "auto") {
1568
- const sp = chunk7EI2N7WO_cjs.parseSpring(kf.ease);
1569
- length = sp ? Math.round(chunk7EI2N7WO_cjs.springDuration(sp) * 1e3) / 1e3 : 0.5;
1568
+ const sp = chunkX62H3RBG_cjs.parseSpring(kf.ease);
1569
+ length = sp ? Math.round(chunkX62H3RBG_cjs.springDuration(sp) * 1e3) / 1e3 : 0.5;
1570
1570
  }
1571
1571
  if (!(length > 0)) continue;
1572
1572
  const kat = kf.at ?? 0;
@@ -1645,7 +1645,7 @@ function inspectScene(root, frame, time, canvas, opts = {}) {
1645
1645
  if (motion.ever) hasMotion.add(info);
1646
1646
  let shown = bounds ? clip(bounds, view) : null;
1647
1647
  const maskTarget = seq.maskSequence?.target;
1648
- const maskSrc = chunk27PYJ4JY_cjs.maskSourceOf(seq.spec.mask);
1648
+ const maskSrc = chunk3HZV3UHU_cjs.maskSourceOf(seq.spec.mask);
1649
1649
  if (shown && maskTarget && !(maskSrc.kind === "inline" && maskSrc.invert)) {
1650
1650
  const m = maskTarget.getBounds();
1651
1651
  shown = clip(shown, { x: m.x, y: m.y, width: m.width, height: m.height });
@@ -1654,7 +1654,7 @@ function inspectScene(root, frame, time, canvas, opts = {}) {
1654
1654
  const group = parentScene ?? (name !== void 0 && blending.has(name) ? name : void 0);
1655
1655
  if (group) scene.set(info, group);
1656
1656
  if (zero || seq.showsNothingYet) scaledToNothing.add(info);
1657
- if (seq instanceof chunk27PYJ4JY_cjs.CompositionSequence) walk2(seq, prefix + label + "/", insideThreeD || threeD, visible, group, motion, zero, parentAlpha * alpha);
1657
+ if (seq instanceof chunk3HZV3UHU_cjs.CompositionSequence) walk2(seq, prefix + label + "/", insideThreeD || threeD, visible, group, motion, zero, parentAlpha * alpha);
1658
1658
  });
1659
1659
  };
1660
1660
  walk2(root, "", false, true);
@@ -1731,6 +1731,202 @@ function inspectScene(root, frame, time, canvas, opts = {}) {
1731
1731
  return { frame, time, canvas, summary: { layers: layers.length, visible: layers.filter((l) => l.visible).length }, issues, layers: listed };
1732
1732
  }
1733
1733
 
1734
+ // src/core/pick.ts
1735
+ var scratch;
1736
+ var saidBlind = /* @__PURE__ */ new Set();
1737
+ var readResourceAlpha = (resource, x, y) => {
1738
+ try {
1739
+ if (scratch === void 0) {
1740
+ const c = document.createElement("canvas");
1741
+ c.width = c.height = 1;
1742
+ scratch = c.getContext("2d", { willReadFrequently: true });
1743
+ }
1744
+ if (!scratch) return null;
1745
+ scratch.clearRect(0, 0, 1, 1);
1746
+ scratch.drawImage(resource, x, y, 1, 1, 0, 0, 1, 1);
1747
+ return scratch.getImageData(0, 0, 1, 1).data[3] / 255;
1748
+ } catch {
1749
+ return null;
1750
+ }
1751
+ };
1752
+ function pointInQuad(q, x, y) {
1753
+ let pos = 0, neg = 0;
1754
+ for (let i = 0; i < 4; i++) {
1755
+ const ax = q[i * 2], ay = q[i * 2 + 1], bx = q[(i + 1) % 4 * 2], by = q[(i + 1) % 4 * 2 + 1];
1756
+ const cross = (bx - ax) * (y - ay) - (by - ay) * (x - ax);
1757
+ if (cross > 0) pos++;
1758
+ else if (cross < 0) neg++;
1759
+ }
1760
+ return pos === 0 || neg === 0;
1761
+ }
1762
+ function orderOf(node) {
1763
+ const key = [];
1764
+ for (let n = node; n && n.parent; n = n.parent) {
1765
+ key.unshift([n.zIndex ?? 0, Math.max(0, n.parent.children?.indexOf(n) ?? 0)]);
1766
+ }
1767
+ return key;
1768
+ }
1769
+ var above = (a, b) => {
1770
+ for (let i = 0; i < Math.min(a.length, b.length); i++) {
1771
+ if (a[i][0] !== b[i][0]) return b[i][0] - a[i][0];
1772
+ if (a[i][1] !== b[i][1]) return b[i][1] - a[i][1];
1773
+ }
1774
+ return b.length - a.length;
1775
+ };
1776
+ function pickScene(root, x, y, opts = {}, deps = {}) {
1777
+ if (opts.alpha !== void 0 && !(opts.alpha >= 0 && opts.alpha <= 1)) throw new Error(`pixi-effects: pick: alpha must be a number from 0 to 1 (a pixel at or below it is not a hit), got ${opts.alpha}`);
1778
+ const readAlpha = deps.readAlpha ?? readResourceAlpha;
1779
+ const found = [];
1780
+ const names = (list, name, path) => list.some((e) => e === path || name !== void 0 && e === name || path.startsWith(e + "/") || path.split("/").slice(0, -1).includes(e));
1781
+ const wanted = (name, path) => {
1782
+ if (opts.ignore && names(opts.ignore, name, path)) return false;
1783
+ if (opts.only && !names(opts.only, name, path)) return false;
1784
+ return true;
1785
+ };
1786
+ const inside = (b, p) => p.x >= b.x && p.x <= b.x + b.width && p.y >= b.y && p.y <= b.y + b.height;
1787
+ const walk2 = (comp, prefix, parentVisible) => {
1788
+ comp.layers().forEach(({ seq, display, threeD, carriers, quad }, i) => {
1789
+ const d = display;
1790
+ if (!d) return;
1791
+ const t = seq.target;
1792
+ const name = seq.spec.name;
1793
+ const path = prefix + (name ?? `${seq.spec.type}#${i}`);
1794
+ const alpha = (t?.alpha ?? 1) * carriers.reduce((a, c) => a * (c.target?.alpha ?? 1), 1);
1795
+ const alive = (threeD ? d.visible : t?.renderable ?? true) && carriers.every((c) => c.target?.renderable ?? true);
1796
+ const visible = parentVisible && alive && alpha > 0;
1797
+ if (!visible) return;
1798
+ const isComposition = seq instanceof chunk3HZV3UHU_cjs.CompositionSequence;
1799
+ if (isComposition && !threeD) {
1800
+ walk2(seq, path + "/", visible);
1801
+ return;
1802
+ }
1803
+ if (!wanted(name, path)) return;
1804
+ const point = { x, y };
1805
+ let hit = null;
1806
+ if (threeD) {
1807
+ if (quad && pointInQuad(quad, d.toLocal(point).x, d.toLocal(point).y)) hit = { name, path, type: seq.spec.type, kind: "card", point };
1808
+ } else {
1809
+ const local = d.toLocal(point);
1810
+ const lb = d.getLocalBounds();
1811
+ if (inside(lb, local)) {
1812
+ const three = typeof seq.pickAt === "function" ? seq.pickAt((local.x - lb.x) / lb.width, (local.y - lb.y) / lb.height) : void 0;
1813
+ if (three) {
1814
+ const planeOf = seq.dragPlane;
1815
+ if (planeOf) {
1816
+ three.grab = (plane = "screen") => {
1817
+ const along = planeOf.call(seq, three.point, plane);
1818
+ return { move: (cx, cy) => {
1819
+ const lp = d.toLocal({ x: cx, y: cy });
1820
+ const b = d.getLocalBounds();
1821
+ return along((lp.x - b.x) / b.width, (lp.y - b.y) / b.height);
1822
+ } };
1823
+ };
1824
+ }
1825
+ hit = { name, path, type: seq.spec.type, kind: "three", point, three };
1826
+ } else if (three === void 0 && (typeof d.containsPoint !== "function" || d.containsPoint(local))) {
1827
+ let opaque = true;
1828
+ const tex = opts.alpha !== void 0 && seq.spec.type !== "shader" ? d.texture : void 0;
1829
+ if (tex?.source?.resource) {
1830
+ const res = tex.source.resolution ?? 1;
1831
+ const a = readAlpha(tex.source.resource, (tex.frame.x + (local.x - lb.x) / lb.width * tex.frame.width) * res, (tex.frame.y + (local.y - lb.y) / lb.height * tex.frame.height) * res);
1832
+ if (a === null) {
1833
+ if (!saidBlind.has(path)) {
1834
+ saidBlind.add(path);
1835
+ console.warn(`pixi-effects: pick: the picture of layer "${path}" cannot be read for { alpha } (a picture from another origin: load it with crossorigin="anonymous" or from the same site), so the box counts as the picture`);
1836
+ }
1837
+ } else opaque = a > opts.alpha;
1838
+ }
1839
+ if (opaque) hit = { name, path, type: seq.spec.type, kind: "flat", point, local: { x: local.x - lb.x, y: local.y - lb.y } };
1840
+ }
1841
+ }
1842
+ }
1843
+ if (!hit) return;
1844
+ const maskTarget = seq.maskSequence?.target;
1845
+ const maskSrc = chunk3HZV3UHU_cjs.maskSourceOf(seq.spec.mask);
1846
+ if (maskTarget && !(maskSrc.kind === "inline" && maskSrc.invert) && !inside(maskTarget.getBounds(), point)) return;
1847
+ found.push({ hit, order: orderOf(d) });
1848
+ });
1849
+ };
1850
+ walk2(root, "", true);
1851
+ return found.sort((a, b) => above(a.order, b.order)).map((f) => f.hit);
1852
+ }
1853
+
1854
+ // src/core/hover.ts
1855
+ var keyOf = (h) => h ? `${h.path}${h.three ? `#${h.three.object}` : ""}` : null;
1856
+ function startHover(host, opts = {}) {
1857
+ const { canvas } = host;
1858
+ const request = host.requestFrame ?? ((fn) => requestAnimationFrame(fn));
1859
+ const cancel = host.cancelFrame ?? ((id) => cancelAnimationFrame(id));
1860
+ const cursor = opts.cursor === true ? "pointer" : opts.cursor || "";
1861
+ const pickOpts = {};
1862
+ if (opts.only) pickOpts.only = opts.only;
1863
+ if (opts.ignore) pickOpts.ignore = opts.ignore;
1864
+ let current = null;
1865
+ let last = null;
1866
+ let loop = null;
1867
+ let said = false;
1868
+ const call = (fn, hit) => {
1869
+ if (!fn) return;
1870
+ try {
1871
+ fn(hit);
1872
+ } catch (err) {
1873
+ if (!said) {
1874
+ said = true;
1875
+ console.warn("pixi-effects: movie.hover: a handler threw (said once) \u2014", err);
1876
+ }
1877
+ }
1878
+ };
1879
+ const setCursor = (on) => {
1880
+ if (cursor) canvas.style.cursor = on ? cursor : "";
1881
+ };
1882
+ const evaluate = () => {
1883
+ if (!last) return;
1884
+ const [x, y] = host.point(last);
1885
+ const hit = host.pick(x, y, pickOpts);
1886
+ if (keyOf(hit) === keyOf(current)) {
1887
+ current = hit ?? current;
1888
+ return;
1889
+ }
1890
+ const was = current;
1891
+ current = hit;
1892
+ if (was) call(opts.onLeave, was);
1893
+ if (hit) call(opts.onEnter, hit);
1894
+ setCursor(!!hit);
1895
+ };
1896
+ const tick = () => {
1897
+ loop = null;
1898
+ if (last) {
1899
+ evaluate();
1900
+ loop = request(tick);
1901
+ }
1902
+ };
1903
+ const onMove = (event) => {
1904
+ last = event;
1905
+ evaluate();
1906
+ if (loop === null) loop = request(tick);
1907
+ };
1908
+ const onLeave = () => {
1909
+ last = null;
1910
+ if (loop !== null) {
1911
+ cancel(loop);
1912
+ loop = null;
1913
+ }
1914
+ const was = current;
1915
+ current = null;
1916
+ if (was) call(opts.onLeave, was);
1917
+ setCursor(false);
1918
+ };
1919
+ canvas.addEventListener("pointermove", onMove);
1920
+ canvas.addEventListener("pointerdown", onMove);
1921
+ canvas.addEventListener("pointerleave", onLeave);
1922
+ return () => {
1923
+ canvas.removeEventListener("pointermove", onMove);
1924
+ canvas.removeEventListener("pointerdown", onMove);
1925
+ canvas.removeEventListener("pointerleave", onLeave);
1926
+ onLeave();
1927
+ };
1928
+ }
1929
+
1734
1930
  // src/core/timelineChart.ts
1735
1931
  var GROUP_MIN = 4;
1736
1932
  var MAX_KEYS = 200;
@@ -1738,7 +1934,7 @@ var round = (n) => Math.round(n * 1e6) / 1e6;
1738
1934
  var family = (name) => name.replace(/\d+/g, "#");
1739
1935
  function detailOf(seq) {
1740
1936
  const s = seq.spec;
1741
- const spans = chunk7EI2N7WO_cjs.spansMeta.get(seq.spec)?.preview;
1937
+ const spans = chunkX62H3RBG_cjs.spansMeta.get(seq.spec)?.preview;
1742
1938
  if (spans) return spans.length > 60 ? spans.slice(0, 57) + "\u2026" : spans;
1743
1939
  if (typeof s.text === "string") return s.text.length > 60 ? s.text.slice(0, 57) + "\u2026" : s.text;
1744
1940
  if (typeof s.asset === "string") return `asset \u201C${s.asset}\u201D`;
@@ -1754,10 +1950,10 @@ function walk(comp, parentStart, parentEnd, depth, prefix, rows, transitions, lo
1754
1950
  const start = parentStart + (seq.at ?? 0);
1755
1951
  const end = Math.min(parentEnd, seq.duration !== void 0 ? start + seq.duration : parentEnd);
1756
1952
  const span = end - start;
1757
- const keys = (seq.spec.keyframes ?? []).map((kf) => round(start + chunk7EI2N7WO_cjs.resolveAt(kf.at, span))).sort((a, b) => a - b).slice(0, MAX_KEYS);
1953
+ const keys = (seq.spec.keyframes ?? []).map((kf) => round(start + chunkX62H3RBG_cjs.resolveAt(kf.at, span))).sort((a, b) => a - b).slice(0, MAX_KEYS);
1758
1954
  const row = { path: prefix + name, name, type: seq.spec.type, depth, start: round(start), end: round(end), keys, detail: detailOf(seq), ...local ? { local } : {} };
1759
1955
  const inner = [];
1760
- const isComp = seq instanceof chunk27PYJ4JY_cjs.CompositionSequence;
1956
+ const isComp = seq instanceof chunk3HZV3UHU_cjs.CompositionSequence;
1761
1957
  if (isComp) {
1762
1958
  const label = seq.remapLabel();
1763
1959
  walk(seq, label ? 0 : start, label ? seq.contentSpan : end, depth + 1, prefix + name + "/", inner, transitions, label ? `${name} ${label}` : local);
@@ -1916,7 +2112,7 @@ function sheetLayout(n, columns, cellW, srcW, srcH, labelH) {
1916
2112
  }
1917
2113
 
1918
2114
  // src/core/vars.ts
1919
- var RESERVED_NAMES = ["w", "h", "W", "H", "GW", "GH", "t", "d", "T", "contain", "cover", "value", ...chunk7EI2N7WO_cjs.EXPRESSION_NAMES];
2115
+ var RESERVED_NAMES = ["w", "h", "W", "H", "GW", "GH", "t", "d", "T", "contain", "cover", "value", ...chunkX62H3RBG_cjs.EXPRESSION_NAMES];
1920
2116
  var NAME = /^[A-Za-z_][A-Za-z0-9_]*$/;
1921
2117
  var isPlain = (v) => !!v && typeof v === "object" && (Object.getPrototypeOf(v) === Object.prototype || Object.getPrototypeOf(v) === null);
1922
2118
  function normalizeVars(raw, where, warn = (m) => console.warn(m)) {
@@ -2455,8 +2651,8 @@ var Movie = class {
2455
2651
  this.stops = normalizeStops(applyVars(options.composition, this._vars)?.stops, this.duration, this.frameRate);
2456
2652
  this._posterFrame = poster.frame;
2457
2653
  this.background = options.background ?? "#000000";
2458
- this.timeline = chunk7EI2N7WO_cjs.createTimeline({ paused: true, defaults: { ease: "none" } });
2459
- chunk7EI2N7WO_cjs.lengthen(this.timeline, this.duration);
2654
+ this.timeline = chunkX62H3RBG_cjs.createTimeline({ paused: true, defaults: { ease: "none" } });
2655
+ chunkX62H3RBG_cjs.lengthen(this.timeline, this.duration);
2460
2656
  const baseAppOptions = {
2461
2657
  width: this.width,
2462
2658
  height: this.height,
@@ -2550,7 +2746,7 @@ var Movie = class {
2550
2746
  height: sizeWins ? height : spec.height ?? height,
2551
2747
  duration: spec.duration ?? this.duration
2552
2748
  } : void 0;
2553
- await chunk7EI2N7WO_cjs.ensureFilterLibrary(seededComposition);
2749
+ await chunkX62H3RBG_cjs.ensureFilterLibrary(seededComposition);
2554
2750
  if (chunkJN7JHNZR_cjs.usesAdvancedBlend(seededComposition)) await chunkJN7JHNZR_cjs.enableAdvancedBlend(this.app.renderer);
2555
2751
  const userComposition = seededComposition ? expandTransitions(seededComposition) : void 0;
2556
2752
  const rootSeqSpec = {
@@ -2561,7 +2757,7 @@ var Movie = class {
2561
2757
  ...userComposition
2562
2758
  };
2563
2759
  if (userComposition) carryTransitionWindows(userComposition, rootSeqSpec);
2564
- const sequence = new chunk27PYJ4JY_cjs.CompositionSequence(rootSeqSpec, null, rootShape);
2760
+ const sequence = new chunk3HZV3UHU_cjs.CompositionSequence(rootSeqSpec, null, rootShape);
2565
2761
  lp?.begin("build", countLayers(rootSeqSpec));
2566
2762
  try {
2567
2763
  await sequence.build();
@@ -2697,8 +2893,8 @@ var Movie = class {
2697
2893
  if (!changed) return { reason, ms: 0, audio: "none", frame: this.currentFrame, playing: this.isPlaying, unchanged: true };
2698
2894
  sequence = await this._construct(composition, vars, width, height, void 0, this._resized || sizeChanged);
2699
2895
  alive();
2700
- const timeline = chunk7EI2N7WO_cjs.createTimeline({ paused: true, defaults: { ease: "none" } });
2701
- chunk7EI2N7WO_cjs.lengthen(timeline, this.duration);
2896
+ const timeline = chunkX62H3RBG_cjs.createTimeline({ paused: true, defaults: { ease: "none" } });
2897
+ chunkX62H3RBG_cjs.lengthen(timeline, this.duration);
2702
2898
  sequence.bindTimeline(timeline);
2703
2899
  const hadSound = this.audioBuffer !== null;
2704
2900
  const audio = await this._mixSound(sequence, this._ensureAudioContext());
@@ -2785,6 +2981,14 @@ var Movie = class {
2785
2981
  await this._goto(frame);
2786
2982
  this.emit("seeked", { frame: this.currentFrame, totalFrames: this.totalFrames });
2787
2983
  }
2984
+ /**
2985
+ * Draw the frame on screen again, quietly (no `seeking` / `seeked`). For a paused movie when something outside it that a layer reads has changed: a `three` layer whose `update` reads the page's own
2986
+ * state (a dragged object's place) shows it after `await movie.redraw()`. A playing movie needs no call: the next frame draws it.
2987
+ */
2988
+ async redraw() {
2989
+ this._requireReady("redraw");
2990
+ await this._quietly(() => this.gotoFrame(this.currentFrame, true));
2991
+ }
2788
2992
  /** Run `fn` with `seeking` / `seeked` silenced: the movie is moving its own playhead, not answering a seek. */
2789
2993
  async _quietly(fn) {
2790
2994
  this._quiet++;
@@ -2988,6 +3192,41 @@ var Movie = class {
2988
3192
  * Where every layer is drawn at `frame` (canvas pixels, visibility) plus `issues` — text that is off
2989
3193
  * the canvas, cut by an edge, empty, or overlapping other text. Seeks there and stays there.
2990
3194
  */
3195
+ /**
3196
+ * What is under the canvas point (x, y) (canvas pixels: the movie's own `width` x `height`; `canvasPoint(event)` makes them from a pointer event) at the frame on screen: the layer on
3197
+ * top, or null. A flat layer is hit by its own shape, a `threeD` card by its projected quad, a `three` layer by a ray through its camera (`hit.three` has the object). It reads the
3198
+ * frame as the last draw left it, so after `await movie.snapshot(frame)` or any seek it answers for that frame. `opts.only` / `opts.ignore`: layer names or paths.
3199
+ */
3200
+ pick(x, y, opts = {}) {
3201
+ return this.pickAll(x, y, opts)[0] ?? null;
3202
+ }
3203
+ /** Every layer under the canvas point, the one on top first (see `pick`). */
3204
+ pickAll(x, y, opts = {}) {
3205
+ chunkTWZNOUM7_cjs.warnUnknownOptions("movie.pick()", opts, ["only", "ignore", "alpha"]);
3206
+ this._requireReady("pick");
3207
+ if (!Number.isFinite(x) || !Number.isFinite(y)) throw new Error(`pixi-effects: movie.pick(x, y) needs two numbers (canvas pixels), got ${x}, ${y}. For a pointer event: movie.pick(...movie.canvasPoint(event))`);
3208
+ return pickScene(this._rootSequence, x, y, opts);
3209
+ }
3210
+ /**
3211
+ * Which layer the pointer is over, as callbacks: `onEnter(hit)` when the layer (or, in a `three` layer, the object) under the pointer changes to another, `onLeave(hit)` for the one it
3212
+ * left (also when the pointer leaves the picture). While the pointer rests inside, it is asked again on every frame, so a picture that moves under a still pointer is followed.
3213
+ * `{ only, ignore }` as for `pick`; `cursor: true` (or a CSS cursor) shows it while something is hovered. Returns the function that stops it. A page-side tool: an exported video has no pointer.
3214
+ */
3215
+ hover(opts = {}) {
3216
+ chunkTWZNOUM7_cjs.warnUnknownOptions("movie.hover()", opts, ["onEnter", "onLeave", "cursor", "only", "ignore"]);
3217
+ this._requireReady("hover");
3218
+ const canvas = this.app?.canvas;
3219
+ if (!canvas) throw new Error("pixi-effects: movie.hover needs the movie's canvas");
3220
+ return startHover({ canvas, pick: (x, y, o) => this.pick(x, y, o), point: (e) => this.canvasPoint(e) }, opts);
3221
+ }
3222
+ /** A pointer event's place in canvas pixels (the canvas may be shown larger or smaller than its own size): `movie.pick(...movie.canvasPoint(event))`. */
3223
+ canvasPoint(event) {
3224
+ this._requireReady("canvasPoint");
3225
+ const canvas = this.app?.canvas;
3226
+ if (!canvas) throw new Error("pixi-effects: movie.canvasPoint needs the movie's canvas");
3227
+ const r = canvas.getBoundingClientRect();
3228
+ return [(event.clientX - r.left) * this.width / (r.width || this.width), (event.clientY - r.top) * this.height / (r.height || this.height)];
3229
+ }
2991
3230
  async inspect(frame = this.currentFrame, opts = { layers: "visible" }) {
2992
3231
  chunkTWZNOUM7_cjs.warnUnknownOptions("movie.inspect()", opts, ["layers"]);
2993
3232
  this._requireReady("inspect");
@@ -3422,7 +3661,7 @@ function countLayers(spec) {
3422
3661
  n += 1 + countLayers(s);
3423
3662
  const spans = s.spans;
3424
3663
  if (Array.isArray(spans)) n += spans.length;
3425
- const src = chunk27PYJ4JY_cjs.maskSourceOf(mask);
3664
+ const src = chunk3HZV3UHU_cjs.maskSourceOf(mask);
3426
3665
  if (src.kind === "inline") n += 1 + countLayers(src.spec);
3427
3666
  }
3428
3667
  return n;
@@ -3511,8 +3750,8 @@ function splitVertical(text, style, ts, right, top, align, by) {
3511
3750
 
3512
3751
  // src/presets/_ease.ts
3513
3752
  function parseEase2(name) {
3514
- chunk7EI2N7WO_cjs.checkEase(name, "a preset");
3515
- return chunk7EI2N7WO_cjs.parseEase(name);
3753
+ chunkX62H3RBG_cjs.checkEase(name, "a preset");
3754
+ return chunkX62H3RBG_cjs.parseEase(name);
3516
3755
  }
3517
3756
 
3518
3757
  // src/presets/kenBurns.ts
@@ -3647,17 +3886,17 @@ function orbit(opts) {
3647
3886
  throw new Error("pixi-effects: orbit() cannot combine `radius` with `dollyZoom` \u2014 the radius follows the animated fov");
3648
3887
  }
3649
3888
  const [cx, cy] = opts.center ?? [width / 2, height / 2];
3650
- const fixedR = opts.radius ?? chunk7EI2N7WO_cjs.homeDistance(height, fov ?? chunk7EI2N7WO_cjs.DEFAULT_FOV);
3889
+ const fixedR = opts.radius ?? chunkX62H3RBG_cjs.homeDistance(height, fov ?? chunkX62H3RBG_cjs.DEFAULT_FOV);
3651
3890
  const start = opts.start ?? -degrees / 2;
3652
3891
  const steps = Math.max(1, Math.round(duration * stepsPerSecond));
3653
3892
  const easeFn = parseEase2(ease);
3654
3893
  const dollyEase = dolly ? parseEase2(dolly.ease ?? ease) : null;
3655
3894
  const pos = (i) => {
3656
3895
  const p = i / steps;
3657
- const a = (start + degrees * easeFn(p)) * chunk7EI2N7WO_cjs.DEG;
3896
+ const a = (start + degrees * easeFn(p)) * chunkX62H3RBG_cjs.DEG;
3658
3897
  if (!dolly) return { x: cx + fixedR * Math.sin(a), z: fixedR * Math.cos(a) };
3659
3898
  const f = dolly.from + (dolly.to - dolly.from) * dollyEase(p);
3660
- const R = chunk7EI2N7WO_cjs.homeDistance(height, f);
3899
+ const R = chunkX62H3RBG_cjs.homeDistance(height, f);
3661
3900
  return { x: cx + R * Math.sin(a), z: R * Math.cos(a), fov: f };
3662
3901
  };
3663
3902
  const keyframes = [];
@@ -3712,7 +3951,7 @@ function particles(opts) {
3712
3951
  const round2 = (v) => Math.round(v * 1e6) / 1e6;
3713
3952
  const out = [];
3714
3953
  for (let i = 0; i < count; i++) {
3715
- const r = chunk7EI2N7WO_cjs.random(seed * 1000003 + i * 7919 + 17);
3954
+ const r = chunkX62H3RBG_cjs.random(seed * 1000003 + i * 7919 + 17);
3716
3955
  const pick = ([a2, b]) => a2 + r() * (b - a2);
3717
3956
  const born = at + r() * emit;
3718
3957
  const lifetime = pick(life);
@@ -3725,7 +3964,7 @@ function particles(opts) {
3725
3964
  const turn = spin ? pick(spin) : 0;
3726
3965
  const startAngle = spin ? r() * 360 : 0;
3727
3966
  const swayAmp = sway?.amp ?? 0, swayFreq = sway?.freq ?? 1;
3728
- const n0 = sway ? chunk7EI2N7WO_cjs.noise(0, seed * 1000003 + i) : 0;
3967
+ const n0 = sway ? chunkX62H3RBG_cjs.noise(0, seed * 1000003 + i) : 0;
3729
3968
  const at_ = (t) => {
3730
3969
  const axis = (p0, v0, acc) => {
3731
3970
  if (drag > 0) {
@@ -3734,7 +3973,7 @@ function particles(opts) {
3734
3973
  }
3735
3974
  return p0 + v0 * t + 0.5 * acc * t * t;
3736
3975
  };
3737
- const sw = sway ? swayAmp * (chunk7EI2N7WO_cjs.noise(t * swayFreq, seed * 1000003 + i) - n0) : 0;
3976
+ const sw = sway ? swayAmp * (chunkX62H3RBG_cjs.noise(t * swayFreq, seed * 1000003 + i) - n0) : 0;
3738
3977
  return { x: axis(x0, vx, wind) + sw, y: axis(y0, vy, gravity) };
3739
3978
  };
3740
3979
  const kfs = [];
@@ -3796,7 +4035,7 @@ function followPath(opts) {
3796
4035
  if (steps > MAX_STEPS) throw new Error(`followPath(): ${steps} samples is too many (max ${MAX_STEPS}); lower frameRate or duration`);
3797
4036
  const segments = [];
3798
4037
  let total = 0;
3799
- for (const line of chunk7EI2N7WO_cjs.flattenSvgPath(d)) {
4038
+ for (const line of chunkX62H3RBG_cjs.flattenSvgPath(d)) {
3800
4039
  const p = line.closed ? [...line.pts, line.pts[0], line.pts[1]] : line.pts;
3801
4040
  for (let i = 2; i < p.length; i += 2) {
3802
4041
  const len = Math.hypot(p[i] - p[i - 2], p[i + 1] - p[i - 1]);
@@ -3849,7 +4088,7 @@ function followPath(opts) {
3849
4088
  // src/core/pathSample.ts
3850
4089
  function samplePath(d) {
3851
4090
  if (typeof d !== "string" || d.trim() === "") throw new Error('samplePath(): d must be SVG path data, e.g. "M 40 300 C 200 20 400 20 560 300"');
3852
- const lines = chunk7EI2N7WO_cjs.flattenSvgPath(d);
4091
+ const lines = chunkX62H3RBG_cjs.flattenSvgPath(d);
3853
4092
  const segments = [];
3854
4093
  let length = 0;
3855
4094
  for (const line of lines) {
@@ -3880,7 +4119,7 @@ function samplePath(d) {
3880
4119
  return { x: g.x0 + (g.x1 - g.x0) * t, y: g.y0 + (g.y1 - g.y0) * t, angle: Math.atan2(g.y1 - g.y0, g.x1 - g.x0) };
3881
4120
  },
3882
4121
  trim(start = 0, end = 1) {
3883
- return chunk7EI2N7WO_cjs.trimPolylines(lines, start, end).map((l) => l.closed ? [...l.pts, l.pts[0], l.pts[1]] : l.pts);
4122
+ return chunkX62H3RBG_cjs.trimPolylines(lines, start, end).map((l) => l.closed ? [...l.pts, l.pts[0], l.pts[1]] : l.pts);
3884
4123
  }
3885
4124
  };
3886
4125
  }
@@ -3989,7 +4228,7 @@ function distances(n, from, grid, seed) {
3989
4228
  if (n === 0) return [];
3990
4229
  if (from === "random") {
3991
4230
  const order = Array.from({ length: n }, (_, i) => i);
3992
- const r = chunk7EI2N7WO_cjs.random(seed);
4231
+ const r = chunkX62H3RBG_cjs.random(seed);
3993
4232
  for (let i = n - 1; i > 0; i--) {
3994
4233
  const j = Math.floor(r() * (i + 1));
3995
4234
  [order[i], order[j]] = [order[j], order[i]];
@@ -4051,7 +4290,7 @@ function wiggle(opts) {
4051
4290
  to[n] = p.around;
4052
4291
  return;
4053
4292
  }
4054
- const off = (chunk7EI2N7WO_cjs.rand(seed * 7919 + pi * 104729 + s * 31 + 1) * 2 - 1) * p.amp;
4293
+ const off = (chunkX62H3RBG_cjs.rand(seed * 7919 + pi * 104729 + s * 31 + 1) * 2 - 1) * p.amp;
4055
4294
  to[n] = typeof p.around === "number" ? p.around + off : `(${p.around}) ${off < 0 ? "-" : "+"} ${Math.abs(off)}`;
4056
4295
  });
4057
4296
  out.push({ at: at + s * stepLen, to, duration: stepLen, ease });
@@ -4510,5 +4749,5 @@ exports.splitText = splitText;
4510
4749
  exports.stagger = stagger;
4511
4750
  exports.wiggle = wiggle;
4512
4751
  exports.withFade = withFade;
4513
- //# sourceMappingURL=chunk-LEL7UNYQ.cjs.map
4514
- //# sourceMappingURL=chunk-LEL7UNYQ.cjs.map
4752
+ //# sourceMappingURL=chunk-WVZLAIKK.cjs.map
4753
+ //# sourceMappingURL=chunk-WVZLAIKK.cjs.map