@pixerate/editor 0.125.0 → 0.127.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 (124) hide show
  1. package/dist/animation/index.d.cts +2 -3
  2. package/dist/animation/index.d.ts +2 -3
  3. package/dist/animation-editor/index.cjs +71 -73
  4. package/dist/animation-editor/index.cjs.map +1 -1
  5. package/dist/animation-editor/index.d.cts +6 -8
  6. package/dist/animation-editor/index.d.ts +6 -8
  7. package/dist/animation-editor/index.js +11 -13
  8. package/dist/animation-editor/index.js.map +1 -1
  9. package/dist/{bezier-CBzKKDjl.d.cts → bezier-DBz0IgEt.d.cts} +22 -2
  10. package/dist/{bezier-CWv6LQal.d.ts → bezier-DWI5MEkI.d.ts} +22 -2
  11. package/dist/{brushstrokeRender-LRV7GXGD.js → brushstrokeRender-7RUITO42.js} +3 -3
  12. package/dist/{brushstrokeRender-EELTV7U4.cjs → brushstrokeRender-OW6ENCZG.cjs} +7 -7
  13. package/dist/{brushstrokeRender-EELTV7U4.cjs.map → brushstrokeRender-OW6ENCZG.cjs.map} +1 -1
  14. package/dist/canvas/index.cjs +423 -37
  15. package/dist/canvas/index.cjs.map +1 -1
  16. package/dist/canvas/index.d.cts +189 -11
  17. package/dist/canvas/index.d.ts +189 -11
  18. package/dist/canvas/index.js +395 -9
  19. package/dist/canvas/index.js.map +1 -1
  20. package/dist/{chunk-7LQO6OHH.cjs → chunk-33SO3QWP.cjs} +4 -4
  21. package/dist/{chunk-7LQO6OHH.cjs.map → chunk-33SO3QWP.cjs.map} +1 -1
  22. package/dist/{chunk-MPQAO33O.js → chunk-A6KHMKI4.js} +3 -3
  23. package/dist/{chunk-V5YGPRCZ.cjs → chunk-BEGEWQHD.cjs} +41 -41
  24. package/dist/{chunk-V5YGPRCZ.cjs.map → chunk-BEGEWQHD.cjs.map} +1 -1
  25. package/dist/{chunk-N3FVUOZD.cjs → chunk-BJTCSLF6.cjs} +7 -7
  26. package/dist/{chunk-N3FVUOZD.cjs.map → chunk-BJTCSLF6.cjs.map} +1 -1
  27. package/dist/{chunk-WUDPFWAN.js → chunk-F7Q2NE2Y.js} +3 -3
  28. package/dist/{chunk-G6WXWS4Y.js → chunk-FICSDDDF.js} +2 -2
  29. package/dist/{chunk-GUZ47CJM.cjs → chunk-IZ4WRXZA.cjs} +1 -1
  30. package/dist/{chunk-GUZ47CJM.cjs.map → chunk-IZ4WRXZA.cjs.map} +1 -1
  31. package/dist/{chunk-QDJKUBWE.cjs → chunk-JODTP4DP.cjs} +176 -5
  32. package/dist/chunk-JODTP4DP.cjs.map +1 -0
  33. package/dist/{chunk-GLWZXQ4Q.js → chunk-KHNZ6MOU.js} +1 -1
  34. package/dist/chunk-KHNZ6MOU.js.map +1 -0
  35. package/dist/{chunk-GCJWMEYW.cjs → chunk-KSJLDRHK.cjs} +46 -46
  36. package/dist/{chunk-GCJWMEYW.cjs.map → chunk-KSJLDRHK.cjs.map} +1 -1
  37. package/dist/{chunk-QQKTOA4U.cjs → chunk-ORE334I6.cjs} +132 -132
  38. package/dist/{chunk-QQKTOA4U.cjs.map → chunk-ORE334I6.cjs.map} +1 -1
  39. package/dist/{chunk-X6KTSFAE.js → chunk-Q4NBEVV5.js} +2 -2
  40. package/dist/{chunk-XZWOLXWA.js → chunk-QFSTQHZY.js} +174 -3
  41. package/dist/chunk-QFSTQHZY.js.map +1 -0
  42. package/dist/{chunk-FPRIUR4T.js → chunk-ROZFX4QI.js} +3 -3
  43. package/dist/{chunk-UXFI3CDZ.cjs → chunk-SFCPGMCT.cjs} +8 -8
  44. package/dist/{chunk-UXFI3CDZ.cjs.map → chunk-SFCPGMCT.cjs.map} +1 -1
  45. package/dist/{chunk-J67OLQUQ.js → chunk-UABQTYQF.js} +3 -3
  46. package/dist/{chunk-O5RVCDEC.cjs → chunk-XLUYXUBD.cjs} +88 -88
  47. package/dist/{chunk-O5RVCDEC.cjs.map → chunk-XLUYXUBD.cjs.map} +1 -1
  48. package/dist/{chunk-FG274FYT.js → chunk-YGJIER2R.js} +2 -2
  49. package/dist/effects/index.cjs +32 -34
  50. package/dist/effects/index.cjs.map +1 -1
  51. package/dist/effects/index.d.cts +6 -38
  52. package/dist/effects/index.d.ts +6 -38
  53. package/dist/effects/index.js +3 -5
  54. package/dist/effects/index.js.map +1 -1
  55. package/dist/evaluate-BbCeF6pt.d.ts +114 -0
  56. package/dist/evaluate-BsxYieLN.d.cts +114 -0
  57. package/dist/{export-DVl4fWy5.d.cts → export-Cbr9IqpJ.d.cts} +1 -1
  58. package/dist/{export-Bp7ji2r4.d.cts → export-CcldobdN.d.cts} +3 -3
  59. package/dist/{export-C1s4eolZ.d.ts → export-ClPKUB-v.d.ts} +1 -1
  60. package/dist/{export-DAyPY94f.d.ts → export-PGhOpDmz.d.ts} +3 -3
  61. package/dist/{helpers-i95Ob6Vi.d.cts → helpers-C5aAd44p.d.cts} +67 -4
  62. package/dist/{helpers-BBLlekuM.d.ts → helpers-CPWcK504.d.ts} +67 -4
  63. package/dist/image-editor/index.cjs +619 -621
  64. package/dist/image-editor/index.cjs.map +1 -1
  65. package/dist/image-editor/index.d.cts +9 -11
  66. package/dist/image-editor/index.d.ts +9 -11
  67. package/dist/image-editor/index.js +6 -8
  68. package/dist/image-editor/index.js.map +1 -1
  69. package/dist/{opentype-14PDI5D2.d.ts → opentype-01rMIuQR.d.ts} +1 -1
  70. package/dist/{opentype-D80AVmMu.d.cts → opentype-BgqvmGT-.d.cts} +1 -1
  71. package/dist/paint-editor/index.cjs +737 -117
  72. package/dist/paint-editor/index.cjs.map +1 -1
  73. package/dist/paint-editor/index.d.cts +248 -13
  74. package/dist/paint-editor/index.d.ts +248 -13
  75. package/dist/paint-editor/index.js +635 -15
  76. package/dist/paint-editor/index.js.map +1 -1
  77. package/dist/{pathfinder-DjISxkUv.d.ts → pathfinder-D-h0Imus.d.ts} +1 -1
  78. package/dist/{pathfinder-C0UoFypx.d.cts → pathfinder-DgG0Z9Lr.d.cts} +1 -1
  79. package/dist/{player-cGLrngL_.d.ts → player-BK2kKbfd.d.ts} +1 -1
  80. package/dist/{player-CBtEoU2x.d.cts → player-CmbY3218.d.cts} +1 -1
  81. package/dist/pool-C7GtrL34.d.cts +34 -0
  82. package/dist/pool-CTrtciaN.d.ts +34 -0
  83. package/dist/{svg-DJKriJ4A.d.ts → svg-BRLwcibA.d.ts} +2 -3
  84. package/dist/{svg-BkRL-fZJ.d.cts → svg-DLBBPQP9.d.cts} +2 -3
  85. package/dist/{tools-CM8Oe8F3.d.ts → tools-AugT22z3.d.ts} +1 -1
  86. package/dist/{tools-DZCv2kX1.d.cts → tools-BpCmUNxb.d.cts} +1 -1
  87. package/dist/vector/index.cjs +923 -925
  88. package/dist/vector/index.cjs.map +1 -1
  89. package/dist/vector/index.d.cts +10 -12
  90. package/dist/vector/index.d.ts +10 -12
  91. package/dist/vector/index.js +11 -13
  92. package/dist/vector/index.js.map +1 -1
  93. package/dist/video-editor/index.cjs +269 -271
  94. package/dist/video-editor/index.cjs.map +1 -1
  95. package/dist/video-editor/index.d.cts +6 -8
  96. package/dist/video-editor/index.d.ts +6 -8
  97. package/dist/video-editor/index.js +3 -5
  98. package/dist/video-editor/index.js.map +1 -1
  99. package/dist/{warp-CH9MM435.d.cts → warp-C13L_GRn.d.cts} +1 -1
  100. package/dist/{warp-k_PdtXOG.d.ts → warp-q8FK5HHi.d.ts} +1 -1
  101. package/package.json +1 -1
  102. package/skills/pixerate-canvas/SKILL.md +19 -0
  103. package/skills/pixerate-paint-editor/SKILL.md +40 -1
  104. package/dist/behavior-Ymbld6jL.d.cts +0 -60
  105. package/dist/behavior-Ymbld6jL.d.ts +0 -60
  106. package/dist/chunk-GLWZXQ4Q.js.map +0 -1
  107. package/dist/chunk-IOXKXQNW.cjs +0 -178
  108. package/dist/chunk-IOXKXQNW.cjs.map +0 -1
  109. package/dist/chunk-QDJKUBWE.cjs.map +0 -1
  110. package/dist/chunk-TGKT5QAB.js +0 -178
  111. package/dist/chunk-TGKT5QAB.js.map +0 -1
  112. package/dist/chunk-XZWOLXWA.js.map +0 -1
  113. package/dist/evaluate-2S3vSPLy.d.cts +0 -55
  114. package/dist/evaluate-CfmT6xA3.d.ts +0 -55
  115. package/dist/gradient-KDfCvhuj.d.cts +0 -66
  116. package/dist/gradient-KDfCvhuj.d.ts +0 -66
  117. /package/dist/{brushstrokeRender-LRV7GXGD.js.map → brushstrokeRender-7RUITO42.js.map} +0 -0
  118. /package/dist/{chunk-MPQAO33O.js.map → chunk-A6KHMKI4.js.map} +0 -0
  119. /package/dist/{chunk-WUDPFWAN.js.map → chunk-F7Q2NE2Y.js.map} +0 -0
  120. /package/dist/{chunk-G6WXWS4Y.js.map → chunk-FICSDDDF.js.map} +0 -0
  121. /package/dist/{chunk-X6KTSFAE.js.map → chunk-Q4NBEVV5.js.map} +0 -0
  122. /package/dist/{chunk-FPRIUR4T.js.map → chunk-ROZFX4QI.js.map} +0 -0
  123. /package/dist/{chunk-J67OLQUQ.js.map → chunk-UABQTYQF.js.map} +0 -0
  124. /package/dist/{chunk-FG274FYT.js.map → chunk-YGJIER2R.js.map} +0 -0
@@ -6,7 +6,7 @@ import {
6
6
  hatchPath,
7
7
  interiorPoint,
8
8
  pathfinder
9
- } from "../chunk-X6KTSFAE.js";
9
+ } from "../chunk-Q4NBEVV5.js";
10
10
  import {
11
11
  ARCH_METRICS,
12
12
  GANTT_METRICS,
@@ -50,7 +50,7 @@ import {
50
50
  profileAlongPolyline,
51
51
  renderShapeBrushstroke,
52
52
  validateCanvasBrush
53
- } from "../chunk-J67OLQUQ.js";
53
+ } from "../chunk-UABQTYQF.js";
54
54
  import {
55
55
  TokenError,
56
56
  getTokenCollection,
@@ -82,6 +82,9 @@ import {
82
82
  CAPTURE_OVERLAY_SELECTORS,
83
83
  rasterizeElement
84
84
  } from "../chunk-O2W2SP3C.js";
85
+ import {
86
+ EffectRendererPool
87
+ } from "../chunk-FJ3324TZ.js";
85
88
  import {
86
89
  createCaptureToolProvider,
87
90
  unionCaptureRects
@@ -153,7 +156,7 @@ import {
153
156
  unitePaths,
154
157
  windingNumber,
155
158
  xorPolygons
156
- } from "../chunk-FG274FYT.js";
159
+ } from "../chunk-YGJIER2R.js";
157
160
  import {
158
161
  CANVAS_GRADIENT_PRESETS,
159
162
  PIXERATE_BRAND_GRADIENT,
@@ -180,7 +183,14 @@ import {
180
183
  resolveCanvasGradient,
181
184
  shapeToPolygon,
182
185
  simplifyRing
183
- } from "../chunk-GLWZXQ4Q.js";
186
+ } from "../chunk-KHNZ6MOU.js";
187
+ import {
188
+ EffectUnsupportedError,
189
+ EffectsSlot,
190
+ createEffectToolProvider,
191
+ resolveEffectValues
192
+ } from "../chunk-QFSTQHZY.js";
193
+ import "../chunk-4FX5ACVG.js";
184
194
  import {
185
195
  ANIMATION_TIME_EPSILON,
186
196
  AnimationPlayer,
@@ -1599,6 +1609,8 @@ function cloneScene(scene) {
1599
1609
  viewport: { x: scene.viewport.x, y: scene.viewport.y, zoom: scene.viewport.zoom }
1600
1610
  };
1601
1611
  if (scene.nodeIds) copy.nodeIds = [...scene.nodeIds];
1612
+ const t = scene.transition;
1613
+ if (t && typeof t.effect === "string" && t.effect) copy.transition = JSON.parse(JSON.stringify(t));
1602
1614
  return copy;
1603
1615
  }
1604
1616
  function parseCanvasScenes(input) {
@@ -7975,11 +7987,11 @@ function createCanvasComponentsController(options) {
7975
7987
  },
7976
7988
  canResetOverrides(id) {
7977
7989
  const at = target(id);
7978
- const info = at ? inspectCanvasInstance(nodes(), at) : void 0;
7979
- if (!info) return "Select an instance or a layer inside one.";
7980
- const changedProps = !info.nested && info.properties.some((p) => p.value !== p.default);
7981
- const own = info.id === at ? info.overrides.length > 0 : getNodeOverrides(nodes().find((n3) => n3.id === at)).length > 0;
7982
- return own || info.id === at && changedProps ? true : "No overrides to reset.";
7990
+ const info2 = at ? inspectCanvasInstance(nodes(), at) : void 0;
7991
+ if (!info2) return "Select an instance or a layer inside one.";
7992
+ const changedProps = !info2.nested && info2.properties.some((p) => p.value !== p.default);
7993
+ const own = info2.id === at ? info2.overrides.length > 0 : getNodeOverrides(nodes().find((n3) => n3.id === at)).length > 0;
7994
+ return own || info2.id === at && changedProps ? true : "No overrides to reset.";
7983
7995
  },
7984
7996
  /** Push the (outermost) instance's overrides to its main component. */
7985
7997
  pushOverrides(id) {
@@ -11601,6 +11613,371 @@ function createCanvasCaptureToolProvider(options) {
11601
11613
  id: options.id ?? "canvas-capture"
11602
11614
  });
11603
11615
  }
11616
+
11617
+ // src/canvas/effects.ts
11618
+ var CANVAS_EFFECT_PLACES = {
11619
+ /** Paints a shape's inside. */
11620
+ fill: ["generator", "interactive"],
11621
+ /** Draws over a node's box. */
11622
+ overlay: ["overlay", "title", "interactive", "generator"],
11623
+ transition: ["transition"]
11624
+ };
11625
+ function canvasNodeEffect(node) {
11626
+ const shape = node.data?.shape;
11627
+ return isShapeDescription(shape) ? shape.effect : void 0;
11628
+ }
11629
+ function createCanvasEffectsController(options) {
11630
+ const slot = new EffectsSlot();
11631
+ if (options.effects) slot.set(options.effects);
11632
+ const describe = async (effect, place2) => {
11633
+ const d = await slot.require().describe(effect);
11634
+ const allowed = CANVAS_EFFECT_PLACES[place2];
11635
+ if (d.kind === "motion" || !d.uses.some((u) => allowed.includes(u))) {
11636
+ const where = place2 === "transition" ? "a scene transition" : place2 === "fill" ? "a fill" : "an overlay";
11637
+ throw new EffectUnsupportedError(`"${d.id}" (${d.name}) is a ${d.uses.join(" / ")} effect and cannot be ${where}, which takes ${allowed.join(" or ")} effects; search_effects with use "${allowed[0]}" lists ones that fit.`, d.id);
11638
+ }
11639
+ return d;
11640
+ };
11641
+ const shapeNode = (id) => {
11642
+ const node = options.getNodes().find((n3) => n3.id === id);
11643
+ if (!node) throw new Error(`There is no node "${id}"; the nodes are ${options.getNodes().map((n3) => `"${n3.id}"`).slice(0, 20).join(", ") || "none"}.`);
11644
+ const shape = node.data?.shape;
11645
+ if (!isShapeDescription(shape)) throw new Error(`Node "${id}" is not a shape; effects draw on shape nodes (data.shape).`);
11646
+ return { node, shape };
11647
+ };
11648
+ const commit = (label, next) => {
11649
+ const before = options.getNodes();
11650
+ if (options.history) options.history.execute({ name: label, execute: () => options.setNodes(next), undo: () => options.setNodes(before), redo: () => options.setNodes(next) });
11651
+ else options.setNodes(next);
11652
+ };
11653
+ const withEffect = (id, effect) => options.getNodes().map((n3) => {
11654
+ if (n3.id !== id) return n3;
11655
+ const shape = { ...n3.data.shape };
11656
+ if (effect) shape.effect = effect;
11657
+ else delete shape.effect;
11658
+ return { ...n3, data: { ...n3.data, shape } };
11659
+ });
11660
+ const mergeValues = (d, was, values) => {
11661
+ const base = { ...was };
11662
+ const given = {};
11663
+ for (const [k, v] of Object.entries(values)) {
11664
+ if (v !== null) given[k] = v;
11665
+ else if (k in d.controls.properties) base[k] = d.defaults[k];
11666
+ else throw new Error(`"${k}" is not a control of "${d.id}"; its controls are ${Object.keys(d.controls.properties).join(", ")}.`);
11667
+ }
11668
+ return { ...base, ...resolveEffectValues({ ...d, defaults: base }, given) };
11669
+ };
11670
+ return {
11671
+ /** The provider slot. */
11672
+ effects: slot,
11673
+ get effectsProvider() {
11674
+ return slot.provider;
11675
+ },
11676
+ /** Registers (or with `null` removes) the effects provider. */
11677
+ setEffectsProvider(provider) {
11678
+ slot.set(provider);
11679
+ },
11680
+ /** Registers `provider` and returns a function that removes it again. */
11681
+ useEffects(provider) {
11682
+ return slot.use(provider);
11683
+ },
11684
+ /** The shape nodes that carry effects (or one node's). */
11685
+ list(nodeId) {
11686
+ return options.getNodes().filter((n3) => !nodeId || n3.id === nodeId).map((n3) => ({ nodeId: n3.id, effect: canvasNodeEffect(n3) })).filter((e) => !!e.effect);
11687
+ },
11688
+ /**
11689
+ * Draws a provider effect on a shape node: `fill` paints its inside (generators), `overlay`
11690
+ * draws over its box (overlays, titles, interactive effects). Replaces any effect it had. One
11691
+ * undoable step. Returns the effect.
11692
+ */
11693
+ async applyNodeEffect(nodeId, effect, opts = {}) {
11694
+ shapeNode(nodeId);
11695
+ const mode = opts.mode ?? "fill";
11696
+ const d = await describe(effect, mode);
11697
+ if (opts.opacity !== void 0 && !(opts.opacity >= 0 && opts.opacity <= 1)) throw new Error("opacity must be from 0 to 1.");
11698
+ const next = { effect: d.id, provider: slot.require().id, name: d.name, values: resolveEffectValues(d, opts.values), mode, ...opts.opacity !== void 0 && opts.opacity !== 1 ? { opacity: opts.opacity } : {} };
11699
+ commit(`Add effect ${d.name}`, withEffect(nodeId, next));
11700
+ return next;
11701
+ },
11702
+ /** Changes a node's effect: `values` merged (`null` resets), `mode`, `opacity`, `playing`. One undoable step. */
11703
+ async updateNodeEffect(nodeId, changes) {
11704
+ const { shape } = shapeNode(nodeId);
11705
+ const was = shape.effect;
11706
+ if (!was) throw new Error(`Node "${nodeId}" has no effect; add one with apply_effect.`);
11707
+ const next = { ...was };
11708
+ if (changes.values || changes.mode) {
11709
+ const d = await describe(was.effect, changes.mode ?? was.mode);
11710
+ if (changes.values) next.values = mergeValues(d, was.values, changes.values);
11711
+ if (changes.mode) next.mode = changes.mode;
11712
+ } else slot.require();
11713
+ if (changes.opacity !== void 0) {
11714
+ if (!(changes.opacity >= 0 && changes.opacity <= 1)) throw new Error("opacity must be from 0 to 1.");
11715
+ if (changes.opacity === 1) delete next.opacity;
11716
+ else next.opacity = changes.opacity;
11717
+ }
11718
+ if (changes.playing !== void 0) {
11719
+ if (changes.playing) delete next.playing;
11720
+ else next.playing = false;
11721
+ }
11722
+ commit(`Change effect ${was.name ?? was.effect}`, withEffect(nodeId, next));
11723
+ return next;
11724
+ },
11725
+ /** Removes a node's effect (works without a provider). One undoable step. */
11726
+ removeNodeEffect(nodeId) {
11727
+ const { shape } = shapeNode(nodeId);
11728
+ if (!shape.effect) throw new Error(`Node "${nodeId}" has no effect.`);
11729
+ commit(`Remove effect ${shape.effect.name ?? shape.effect.effect}`, withEffect(nodeId, void 0));
11730
+ return shape.effect;
11731
+ },
11732
+ /** The scenes, in order (empty without a Scenes store). */
11733
+ scenes() {
11734
+ return options.scenes?.getState().scenes ?? [];
11735
+ },
11736
+ /** The transition into a scene, if it has one. */
11737
+ sceneTransition(sceneId) {
11738
+ return options.scenes?.getScene(sceneId)?.transition;
11739
+ },
11740
+ /**
11741
+ * Sets (or with `effect` null removes) the provider transition played when the canvas moves to
11742
+ * a scene. Returns the transition.
11743
+ */
11744
+ async setSceneTransition(sceneId, effect, opts = {}) {
11745
+ const scenes2 = options.scenes;
11746
+ if (!scenes2) throw new Error("This canvas has no Scenes store.");
11747
+ if (!scenes2.getScene(sceneId)) throw new Error(`There is no scene "${sceneId}"; the scenes are ${scenes2.getState().scenes.map((s) => `"${s.id}"`).join(", ") || "none"}.`);
11748
+ if (effect === null) {
11749
+ scenes2.update(sceneId, { transition: void 0 });
11750
+ return null;
11751
+ }
11752
+ const d = await describe(effect, "transition");
11753
+ const duration = opts.duration ?? d.duration ?? 600;
11754
+ if (!(duration > 0)) throw new Error("duration must be a number of ms above 0.");
11755
+ const transition = { effect: d.id, provider: slot.require().id, name: d.name, values: resolveEffectValues(d, opts.values), duration };
11756
+ scenes2.update(sceneId, { transition });
11757
+ return transition;
11758
+ }
11759
+ };
11760
+ }
11761
+
11762
+ // src/canvas/effect-player.ts
11763
+ var defaultClock = {
11764
+ now: () => typeof performance !== "undefined" ? performance.now() : Date.now(),
11765
+ frame: (cb) => {
11766
+ if (typeof requestAnimationFrame === "undefined") {
11767
+ const t = setTimeout(cb, 16);
11768
+ return () => clearTimeout(t);
11769
+ }
11770
+ const id = requestAnimationFrame(cb);
11771
+ return () => cancelAnimationFrame(id);
11772
+ }
11773
+ };
11774
+ var canvasEffectsPool = new EffectRendererPool({ maxRenderers: 6 });
11775
+ function draw(target, p) {
11776
+ const ctx = target.getContext("2d");
11777
+ if (!ctx || typeof ImageData === "undefined") return;
11778
+ ctx.putImageData(new ImageData(new Uint8ClampedArray(p.data), p.width, p.height), 0, 0);
11779
+ }
11780
+ function runCanvasNodeEffect(options) {
11781
+ const { provider, effect, target } = options;
11782
+ const clock = options.clock ?? defaultClock;
11783
+ const pool = options.pool ?? canvasEffectsPool;
11784
+ const size2 = { width: Math.max(1, Math.round(target.width)), height: Math.max(1, Math.round(target.height)) };
11785
+ const start = clock.now();
11786
+ let stopped = false;
11787
+ let cancel = () => {
11788
+ };
11789
+ const tick = () => {
11790
+ if (stopped) return;
11791
+ const time = options.animate === false || effect.playing === false ? 0 : clock.now() - start;
11792
+ pool.render(provider, effect.effect, size2, { time, values: effect.values }).then(
11793
+ (p) => {
11794
+ if (stopped) return;
11795
+ draw(target, p);
11796
+ if (options.animate !== false && effect.playing !== false) cancel = clock.frame(tick);
11797
+ },
11798
+ (e) => {
11799
+ if (!stopped) options.onError?.(e);
11800
+ }
11801
+ );
11802
+ };
11803
+ tick();
11804
+ return {
11805
+ stop() {
11806
+ stopped = true;
11807
+ cancel();
11808
+ }
11809
+ };
11810
+ }
11811
+ async function playCanvasSceneTransition(options) {
11812
+ const { provider, transition, from, to, target } = options;
11813
+ const clock = options.clock ?? defaultClock;
11814
+ const pool = options.pool ?? canvasEffectsPool;
11815
+ const duration = transition.duration ?? 600;
11816
+ const size2 = { width: from.width, height: from.height };
11817
+ const start = clock.now();
11818
+ for (; ; ) {
11819
+ const time = Math.min(duration, clock.now() - start);
11820
+ const p = await pool.render(provider, transition.effect, size2, { time, progress: time / duration, values: transition.values, inputs: { from, to } });
11821
+ draw(target, p);
11822
+ if (time >= duration) return;
11823
+ await new Promise((resolve) => clock.frame(resolve));
11824
+ }
11825
+ }
11826
+ async function captureCanvasView(element, size2) {
11827
+ const url = await rasterizeElement(element, { pixelWidth: size2.width, pixelHeight: size2.height, exclude: CAPTURE_OVERLAY_SELECTORS });
11828
+ const doc = element.ownerDocument;
11829
+ const image = new doc.defaultView.Image();
11830
+ await new Promise((resolve, reject) => {
11831
+ image.onload = () => resolve();
11832
+ image.onerror = () => reject(new Error("The canvas view could not be captured."));
11833
+ image.src = url;
11834
+ });
11835
+ const canvas = doc.createElement("canvas");
11836
+ canvas.width = size2.width;
11837
+ canvas.height = size2.height;
11838
+ const ctx = canvas.getContext("2d");
11839
+ if (!ctx) throw new Error("The canvas view could not be captured (no 2D canvas).");
11840
+ ctx.drawImage(image, 0, 0, size2.width, size2.height);
11841
+ const data = ctx.getImageData(0, 0, size2.width, size2.height).data;
11842
+ return { width: size2.width, height: size2.height, data };
11843
+ }
11844
+ async function transitionCanvasView(options) {
11845
+ const { element } = options;
11846
+ const clock = options.clock ?? defaultClock;
11847
+ const scale = options.scale ?? Math.min(1, typeof devicePixelRatio === "number" ? devicePixelRatio : 1);
11848
+ const size2 = { width: Math.max(1, Math.round(element.clientWidth * scale)), height: Math.max(1, Math.round(element.clientHeight * scale)) };
11849
+ let from;
11850
+ try {
11851
+ from = await captureCanvasView(element, size2);
11852
+ } catch {
11853
+ options.jump();
11854
+ return;
11855
+ }
11856
+ const overlay = element.ownerDocument.createElement("canvas");
11857
+ overlay.width = size2.width;
11858
+ overlay.height = size2.height;
11859
+ overlay.setAttribute("data-capture", "overlay");
11860
+ overlay.setAttribute("data-canvas-transition", options.transition.effect);
11861
+ Object.assign(overlay.style, { position: "absolute", inset: "0", width: "100%", height: "100%", pointerEvents: "none", zIndex: "1000" });
11862
+ overlay.getContext("2d")?.putImageData(new ImageData(new Uint8ClampedArray(from.data), size2.width, size2.height), 0, 0);
11863
+ if (getComputedStyle(element).position === "static") element.style.position = "relative";
11864
+ element.appendChild(overlay);
11865
+ try {
11866
+ options.jump();
11867
+ await new Promise((r) => clock.frame(() => clock.frame(r)));
11868
+ const to = await captureCanvasView(element, size2);
11869
+ await playCanvasSceneTransition({ provider: options.provider, transition: options.transition, from, to, target: overlay, clock });
11870
+ } catch {
11871
+ } finally {
11872
+ overlay.remove();
11873
+ }
11874
+ }
11875
+
11876
+ // src/canvas/effect-tools.ts
11877
+ var info = (e) => ({
11878
+ id: "effect",
11879
+ effect: e.effect,
11880
+ ...e.name ? { name: e.name } : {},
11881
+ ...e.provider ? { provider: e.provider } : {},
11882
+ role: e.mode === "fill" ? "fill" : "overlay",
11883
+ enabled: e.playing !== false,
11884
+ opacity: e.opacity ?? 1,
11885
+ values: { ...e.values }
11886
+ });
11887
+ function createCanvasEffectsToolProvider(controller, options = {}) {
11888
+ const readOnlyReason = () => {
11889
+ const ro = typeof options.readOnly === "function" ? options.readOnly() : options.readOnly;
11890
+ return ro ? typeof ro === "string" ? ro : "The canvas is read-only." : null;
11891
+ };
11892
+ const result = (nodeId, effect) => {
11893
+ const now = controller.list(nodeId)[0]?.effect;
11894
+ return { target: nodeId, effect: info(effect ?? now), effects: now ? [info(now)] : [] };
11895
+ };
11896
+ const once = (instance) => {
11897
+ if (instance !== "effect") throw new Error('A shape node has one effect, "effect".');
11898
+ };
11899
+ return createEffectToolProvider({
11900
+ id: options.id ?? "canvas-effects",
11901
+ namespace: options.namespace,
11902
+ target: { noun: "node", id: "A shape node id on the canvas (a node with data.shape)." },
11903
+ applyUses: ["generator", "overlay", "title", "interactive"],
11904
+ applyDetails: "Generators fill the shape (clipped to its outline); overlays, titles and interactive effects draw over the node's box (set_canvas_effect_mode switches). A node has one effect: this replaces it. enabled false holds the first frame; opacity mixes it. index does not apply.",
11905
+ provider: () => controller.effectsProvider,
11906
+ readOnlyReason,
11907
+ list: (target) => controller.list(target).map((e) => ({ target: e.nodeId, effects: [info(e.effect)] })),
11908
+ apply: async (target, effect, args) => {
11909
+ if (args.index !== void 0) throw new Error("index does not apply: a shape node has one effect.");
11910
+ const provider = controller.effectsProvider;
11911
+ const d = provider ? await provider.describe(effect) : null;
11912
+ const mode = d && !d.uses.includes("generator") ? "overlay" : "fill";
11913
+ const applied = await controller.applyNodeEffect(target, effect, { mode, values: args.values, opacity: args.opacity });
11914
+ if (args.enabled === false) await controller.updateNodeEffect(target, { playing: false });
11915
+ return result(target, args.enabled === false ? { ...applied, playing: false } : applied);
11916
+ },
11917
+ update: async (target, instance, changes) => {
11918
+ once(instance);
11919
+ if (changes.index !== void 0) throw new Error("index does not apply: a shape node has one effect.");
11920
+ const e = await controller.updateNodeEffect(target, { values: changes.values, opacity: changes.opacity, playing: changes.enabled });
11921
+ return result(target, e);
11922
+ },
11923
+ remove: async (target, instance) => {
11924
+ once(instance);
11925
+ const was = controller.removeNodeEffect(target);
11926
+ return { target, effect: info(was), effects: [] };
11927
+ },
11928
+ subscribe: options.subscribe ? (notify) => options.subscribe(notify) : (notify) => controller.effects.subscribe(() => notify()),
11929
+ extraTools: (ctx) => [
11930
+ {
11931
+ name: "set_canvas_effect_mode",
11932
+ title: "Set a node effect's mode",
11933
+ description: "Switches a shape node's effect between fill (paints the inside, clipped to the outline) and overlay (draws over the node's box). One undoable step. Returns the effect.",
11934
+ inputSchema: {
11935
+ type: "object",
11936
+ properties: {
11937
+ node: { type: "string", minLength: 1, description: "A shape node id from list_applied_effects." },
11938
+ mode: { type: "string", enum: ["fill", "overlay"], description: "fill or overlay." }
11939
+ },
11940
+ required: ["node", "mode"],
11941
+ additionalProperties: false
11942
+ },
11943
+ annotations: { title: "Set a node effect's mode", ...ctx.annotations.writeIdempotent },
11944
+ handler: async (args) => {
11945
+ ctx.requireProvider();
11946
+ return { node: args.node, effect: info(await controller.updateNodeEffect(args.node, { mode: args.mode })) };
11947
+ }
11948
+ },
11949
+ {
11950
+ name: "list_scene_transitions",
11951
+ title: "List scene transitions",
11952
+ description: "Lists the canvas Scenes in order with the transition effect played when moving to each (or null). Works without a provider.",
11953
+ inputSchema: { type: "object", properties: {}, additionalProperties: false },
11954
+ annotations: { title: "List scene transitions", ...ctx.annotations.read },
11955
+ handler: () => ({ scenes: controller.scenes().map((s) => ({ id: s.id, name: s.name, transition: s.transition ?? null })) })
11956
+ },
11957
+ {
11958
+ name: "set_scene_transition",
11959
+ title: "Set a scene transition",
11960
+ description: "Sets the provider transition effect (search_effects with use transition) played when the canvas moves to a scene: the view before and after are captured and the effect blends between them. effect null removes it. Returns the transition.",
11961
+ inputSchema: {
11962
+ type: "object",
11963
+ properties: {
11964
+ scene: { type: "string", minLength: 1, description: "A scene id from list_scene_transitions." },
11965
+ effect: { anyOf: [ctx.effectIdSchema(["transition"]), { type: "null" }], description: "A transition effect id from search_effects, or null to remove the transition." },
11966
+ values: ctx.valuesSchema("Control values over the effect's defaults"),
11967
+ duration: { type: "number", exclusiveMinimum: 0, description: "Length in ms (default: the effect's own, else 600)." }
11968
+ },
11969
+ required: ["scene", "effect"],
11970
+ additionalProperties: false
11971
+ },
11972
+ annotations: { title: "Set a scene transition", ...ctx.annotations.writeIdempotent },
11973
+ handler: async (args) => {
11974
+ if (args.effect !== null) ctx.requireProvider();
11975
+ return { scene: args.scene, transition: await controller.setSceneTransition(args.scene, args.effect, { values: args.values, duration: args.duration }) };
11976
+ }
11977
+ }
11978
+ ]
11979
+ });
11980
+ }
11604
11981
  export {
11605
11982
  ANIMATABLE_PROPERTIES,
11606
11983
  ANIMATION_DATA_KEY,
@@ -11615,6 +11992,7 @@ export {
11615
11992
  CANVAS_CAPTURE_OVERLAY_SELECTORS,
11616
11993
  CANVAS_CAPTURE_VIEWPORT_SELECTOR,
11617
11994
  CANVAS_COMPONENT_DRAG_TYPE,
11995
+ CANVAS_EFFECT_PLACES,
11618
11996
  CANVAS_GRADIENT_PRESETS,
11619
11997
  CANVAS_TOKEN_PROPERTIES,
11620
11998
  CARD_INSPECTION_ZONES,
@@ -11697,8 +12075,11 @@ export {
11697
12075
  canCombineShapeNodes,
11698
12076
  canOffsetShapeNodes,
11699
12077
  canOutlineShapeNodeStrokes,
12078
+ canvasEffectsPool,
11700
12079
  canvasHeight,
11701
12080
  canvasNodeCaptureRect,
12081
+ canvasNodeEffect,
12082
+ captureCanvasView,
11702
12083
  centerNodes,
11703
12084
  centre,
11704
12085
  clampAnimationValue,
@@ -11728,6 +12109,8 @@ export {
11728
12109
  createCanvasComponent,
11729
12110
  createCanvasComponentsController,
11730
12111
  createCanvasDrawController,
12112
+ createCanvasEffectsController,
12113
+ createCanvasEffectsToolProvider,
11731
12114
  createCanvasInstance,
11732
12115
  createCanvasScenesStore,
11733
12116
  createDefaultStrokeStyle,
@@ -11964,6 +12347,7 @@ export {
11964
12347
  pieScene,
11965
12348
  placeBeside,
11966
12349
  planarSplit,
12350
+ playCanvasSceneTransition,
11967
12351
  playWebAnimations,
11968
12352
  pointInBezierPath,
11969
12353
  pointInMultiPolygon,
@@ -12009,6 +12393,7 @@ export {
12009
12393
  revertShapeCombination,
12010
12394
  rgbToOklab,
12011
12395
  ringSignedArea,
12396
+ runCanvasNodeEffect,
12012
12397
  runMultiNodeTransition,
12013
12398
  sampleAnimationFrames,
12014
12399
  scaleKeyframes,
@@ -12067,6 +12452,7 @@ export {
12067
12452
  toWebAnimations,
12068
12453
  toggleLockNodes,
12069
12454
  transformBezierPath,
12455
+ transitionCanvasView,
12070
12456
  trimSegments,
12071
12457
  unionPolygons,
12072
12458
  unitePaths,