@pixerate/editor 0.126.0 → 0.128.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/agent/index.cjs +2 -2
- package/dist/agent/index.d.cts +3 -3
- package/dist/agent/index.d.ts +3 -3
- package/dist/agent/index.js +1 -1
- package/dist/animation/index.d.cts +2 -3
- package/dist/animation/index.d.ts +2 -3
- package/dist/animation-editor/index.cjs +63 -63
- package/dist/animation-editor/index.cjs.map +1 -1
- package/dist/animation-editor/index.d.cts +7 -9
- package/dist/animation-editor/index.d.ts +7 -9
- package/dist/animation-editor/index.js +10 -10
- package/dist/{arrange-BlfgJ_wt.d.ts → arrange-BUREmgD9.d.ts} +1 -1
- package/dist/{arrange-CD6Gh-pd.d.cts → arrange-CKXK_hf7.d.cts} +1 -1
- package/dist/{bezier-CWv6LQal.d.ts → bezier-CvgQiYRg.d.cts} +23 -3
- package/dist/{bezier-CBzKKDjl.d.cts → bezier-DP4NnJp3.d.ts} +23 -3
- package/dist/{brushTypes-IG7ZZjIN.d.cts → brushTypes-C7N3EBK7.d.cts} +56 -1
- package/dist/{brushTypes-IG7ZZjIN.d.ts → brushTypes-C7N3EBK7.d.ts} +56 -1
- package/dist/{brushstrokeRender-EELTV7U4.cjs → brushstrokeRender-JNHVEUFG.cjs} +29 -10
- package/dist/brushstrokeRender-JNHVEUFG.cjs.map +1 -0
- package/dist/{brushstrokeRender-LRV7GXGD.js → brushstrokeRender-SII6MG4R.js} +25 -6
- package/dist/brushstrokeRender-SII6MG4R.js.map +1 -0
- package/dist/{builtins-DMIsnNst.d.cts → builtins-Cc-bH-3U.d.cts} +3 -2
- package/dist/{builtins-B5-ipm5u.d.ts → builtins-ZU1GJnUA.d.ts} +3 -2
- package/dist/canvas/index.cjs +435 -38
- package/dist/canvas/index.cjs.map +1 -1
- package/dist/canvas/index.d.cts +206 -16
- package/dist/canvas/index.d.ts +206 -16
- package/dist/canvas/index.js +408 -11
- package/dist/canvas/index.js.map +1 -1
- package/dist/{chunk-MPQAO33O.js → chunk-A6KHMKI4.js} +3 -3
- package/dist/{chunk-V5YGPRCZ.cjs → chunk-BEGEWQHD.cjs} +41 -41
- package/dist/{chunk-V5YGPRCZ.cjs.map → chunk-BEGEWQHD.cjs.map} +1 -1
- package/dist/{chunk-N3FVUOZD.cjs → chunk-BJTCSLF6.cjs} +7 -7
- package/dist/{chunk-N3FVUOZD.cjs.map → chunk-BJTCSLF6.cjs.map} +1 -1
- package/dist/{chunk-7LQO6OHH.cjs → chunk-DDSO4HS7.cjs} +9 -5
- package/dist/chunk-DDSO4HS7.cjs.map +1 -0
- package/dist/{chunk-AKU2KQ47.cjs → chunk-DFSZO2B5.cjs} +70 -3
- package/dist/chunk-DFSZO2B5.cjs.map +1 -0
- package/dist/{chunk-WUDPFWAN.js → chunk-F7Q2NE2Y.js} +3 -3
- package/dist/{chunk-GUZ47CJM.cjs → chunk-IZ4WRXZA.cjs} +1 -1
- package/dist/{chunk-GUZ47CJM.cjs.map → chunk-IZ4WRXZA.cjs.map} +1 -1
- package/dist/{chunk-GLWZXQ4Q.js → chunk-KHNZ6MOU.js} +1 -1
- package/dist/chunk-KHNZ6MOU.js.map +1 -0
- package/dist/{chunk-GCJWMEYW.cjs → chunk-KSJLDRHK.cjs} +46 -46
- package/dist/{chunk-GCJWMEYW.cjs.map → chunk-KSJLDRHK.cjs.map} +1 -1
- package/dist/{chunk-2SDGZAJR.js → chunk-LSXXAJG3.js} +70 -3
- package/dist/chunk-LSXXAJG3.js.map +1 -0
- package/dist/{chunk-QQKTOA4U.cjs → chunk-ORE334I6.cjs} +132 -132
- package/dist/{chunk-QQKTOA4U.cjs.map → chunk-ORE334I6.cjs.map} +1 -1
- package/dist/{chunk-X6KTSFAE.js → chunk-Q4NBEVV5.js} +2 -2
- package/dist/{chunk-FPRIUR4T.js → chunk-ROZFX4QI.js} +3 -3
- package/dist/{chunk-J67OLQUQ.js → chunk-SVYE5HIM.js} +7 -3
- package/dist/chunk-SVYE5HIM.js.map +1 -0
- package/dist/{chunk-O5RVCDEC.cjs → chunk-XLUYXUBD.cjs} +88 -88
- package/dist/{chunk-O5RVCDEC.cjs.map → chunk-XLUYXUBD.cjs.map} +1 -1
- package/dist/{chunk-FG274FYT.js → chunk-YGJIER2R.js} +2 -2
- package/dist/commands/index.cjs +2 -2
- package/dist/commands/index.d.cts +3 -3
- package/dist/commands/index.d.ts +3 -3
- package/dist/commands/index.js +1 -1
- package/dist/effects/index.d.cts +6 -38
- package/dist/effects/index.d.ts +6 -38
- package/dist/evaluate-BbCeF6pt.d.ts +114 -0
- package/dist/evaluate-BsxYieLN.d.cts +114 -0
- package/dist/{export-Bp7ji2r4.d.cts → export-BkYQvV56.d.cts} +4 -4
- package/dist/{export-DVl4fWy5.d.cts → export-Cbr9IqpJ.d.cts} +1 -1
- package/dist/{export-C1s4eolZ.d.ts → export-ClPKUB-v.d.ts} +1 -1
- package/dist/{export-DAyPY94f.d.ts → export-DHrFJ8r_.d.ts} +4 -4
- package/dist/{helpers-i95Ob6Vi.d.cts → helpers-C5aAd44p.d.cts} +67 -4
- package/dist/{helpers-BBLlekuM.d.ts → helpers-CPWcK504.d.ts} +67 -4
- package/dist/image-editor/index.cjs +606 -606
- package/dist/image-editor/index.cjs.map +1 -1
- package/dist/image-editor/index.d.cts +12 -14
- package/dist/image-editor/index.d.ts +12 -14
- package/dist/image-editor/index.js +4 -4
- package/dist/{opentype-D80AVmMu.d.cts → opentype-BOewaGQx.d.cts} +1 -1
- package/dist/{opentype-14PDI5D2.d.ts → opentype-sQrF3Ry1.d.ts} +1 -1
- package/dist/paint/index.d.cts +2 -2
- package/dist/paint/index.d.ts +2 -2
- package/dist/paint-editor/index.cjs +9 -9
- package/dist/paint-editor/index.cjs.map +1 -1
- package/dist/paint-editor/index.d.cts +9 -11
- package/dist/paint-editor/index.d.ts +9 -11
- package/dist/paint-editor/index.js +4 -4
- package/dist/{pathfinder-DjISxkUv.d.ts → pathfinder-BwJx0_zM.d.ts} +2 -2
- package/dist/{pathfinder-C0UoFypx.d.cts → pathfinder-CnkW7zLI.d.cts} +2 -2
- package/dist/{player-cGLrngL_.d.ts → player-BK2kKbfd.d.ts} +1 -1
- package/dist/{player-CBtEoU2x.d.cts → player-CmbY3218.d.cts} +1 -1
- package/dist/pool-C7GtrL34.d.cts +34 -0
- package/dist/pool-CTrtciaN.d.ts +34 -0
- package/dist/{rand-CfinE-1c.d.cts → rand-DTXAssW1.d.cts} +4 -4
- package/dist/{rand-DtazXGOB.d.ts → rand-pQQ2sJTk.d.ts} +4 -4
- package/dist/{svg-DJKriJ4A.d.ts → svg-C4LlFnEw.d.cts} +2 -3
- package/dist/{svg-BkRL-fZJ.d.cts → svg-CUzZa0EO.d.ts} +2 -3
- package/dist/{tools-CM8Oe8F3.d.ts → tools-AugT22z3.d.ts} +1 -1
- package/dist/{tools-DZCv2kX1.d.cts → tools-BpCmUNxb.d.cts} +1 -1
- package/dist/vector/index.cjs +918 -918
- package/dist/vector/index.cjs.map +1 -1
- package/dist/vector/index.d.cts +11 -13
- package/dist/vector/index.d.ts +11 -13
- package/dist/vector/index.js +11 -11
- package/dist/video-editor/index.d.cts +6 -8
- package/dist/video-editor/index.d.ts +6 -8
- package/dist/{warp-k_PdtXOG.d.ts → warp-CIsO617e.d.ts} +1 -1
- package/dist/{warp-CH9MM435.d.cts → warp-D62N_5VK.d.cts} +1 -1
- package/package.json +1 -1
- package/skills/pixerate-canvas/SKILL.md +30 -2
- package/dist/behavior-Ymbld6jL.d.cts +0 -60
- package/dist/behavior-Ymbld6jL.d.ts +0 -60
- package/dist/brushstrokeRender-EELTV7U4.cjs.map +0 -1
- package/dist/brushstrokeRender-LRV7GXGD.js.map +0 -1
- package/dist/chunk-2SDGZAJR.js.map +0 -1
- package/dist/chunk-7LQO6OHH.cjs.map +0 -1
- package/dist/chunk-AKU2KQ47.cjs.map +0 -1
- package/dist/chunk-GLWZXQ4Q.js.map +0 -1
- package/dist/chunk-J67OLQUQ.js.map +0 -1
- package/dist/evaluate-2S3vSPLy.d.cts +0 -55
- package/dist/evaluate-CfmT6xA3.d.ts +0 -55
- package/dist/gradient-KDfCvhuj.d.cts +0 -66
- package/dist/gradient-KDfCvhuj.d.ts +0 -66
- /package/dist/{chunk-MPQAO33O.js.map → chunk-A6KHMKI4.js.map} +0 -0
- /package/dist/{chunk-WUDPFWAN.js.map → chunk-F7Q2NE2Y.js.map} +0 -0
- /package/dist/{chunk-X6KTSFAE.js.map → chunk-Q4NBEVV5.js.map} +0 -0
- /package/dist/{chunk-FPRIUR4T.js.map → chunk-ROZFX4QI.js.map} +0 -0
- /package/dist/{chunk-FG274FYT.js.map → chunk-YGJIER2R.js.map} +0 -0
package/dist/canvas/index.js
CHANGED
|
@@ -6,7 +6,7 @@ import {
|
|
|
6
6
|
hatchPath,
|
|
7
7
|
interiorPoint,
|
|
8
8
|
pathfinder
|
|
9
|
-
} from "../chunk-
|
|
9
|
+
} from "../chunk-Q4NBEVV5.js";
|
|
10
10
|
import {
|
|
11
11
|
ARCH_METRICS,
|
|
12
12
|
GANTT_METRICS,
|
|
@@ -49,8 +49,9 @@ import {
|
|
|
49
49
|
pathDataToPolyline,
|
|
50
50
|
profileAlongPolyline,
|
|
51
51
|
renderShapeBrushstroke,
|
|
52
|
-
validateCanvasBrush
|
|
53
|
-
|
|
52
|
+
validateCanvasBrush,
|
|
53
|
+
validateCanvasBrushPaint
|
|
54
|
+
} from "../chunk-SVYE5HIM.js";
|
|
54
55
|
import {
|
|
55
56
|
TokenError,
|
|
56
57
|
getTokenCollection,
|
|
@@ -82,6 +83,9 @@ import {
|
|
|
82
83
|
CAPTURE_OVERLAY_SELECTORS,
|
|
83
84
|
rasterizeElement
|
|
84
85
|
} from "../chunk-O2W2SP3C.js";
|
|
86
|
+
import {
|
|
87
|
+
EffectRendererPool
|
|
88
|
+
} from "../chunk-FJ3324TZ.js";
|
|
85
89
|
import {
|
|
86
90
|
createCaptureToolProvider,
|
|
87
91
|
unionCaptureRects
|
|
@@ -153,7 +157,7 @@ import {
|
|
|
153
157
|
unitePaths,
|
|
154
158
|
windingNumber,
|
|
155
159
|
xorPolygons
|
|
156
|
-
} from "../chunk-
|
|
160
|
+
} from "../chunk-YGJIER2R.js";
|
|
157
161
|
import {
|
|
158
162
|
CANVAS_GRADIENT_PRESETS,
|
|
159
163
|
PIXERATE_BRAND_GRADIENT,
|
|
@@ -180,7 +184,14 @@ import {
|
|
|
180
184
|
resolveCanvasGradient,
|
|
181
185
|
shapeToPolygon,
|
|
182
186
|
simplifyRing
|
|
183
|
-
} from "../chunk-
|
|
187
|
+
} from "../chunk-KHNZ6MOU.js";
|
|
188
|
+
import {
|
|
189
|
+
EffectUnsupportedError,
|
|
190
|
+
EffectsSlot,
|
|
191
|
+
createEffectToolProvider,
|
|
192
|
+
resolveEffectValues
|
|
193
|
+
} from "../chunk-QFSTQHZY.js";
|
|
194
|
+
import "../chunk-4FX5ACVG.js";
|
|
184
195
|
import {
|
|
185
196
|
ANIMATION_TIME_EPSILON,
|
|
186
197
|
AnimationPlayer,
|
|
@@ -1599,6 +1610,8 @@ function cloneScene(scene) {
|
|
|
1599
1610
|
viewport: { x: scene.viewport.x, y: scene.viewport.y, zoom: scene.viewport.zoom }
|
|
1600
1611
|
};
|
|
1601
1612
|
if (scene.nodeIds) copy.nodeIds = [...scene.nodeIds];
|
|
1613
|
+
const t = scene.transition;
|
|
1614
|
+
if (t && typeof t.effect === "string" && t.effect) copy.transition = JSON.parse(JSON.stringify(t));
|
|
1602
1615
|
return copy;
|
|
1603
1616
|
}
|
|
1604
1617
|
function parseCanvasScenes(input) {
|
|
@@ -7975,11 +7988,11 @@ function createCanvasComponentsController(options) {
|
|
|
7975
7988
|
},
|
|
7976
7989
|
canResetOverrides(id) {
|
|
7977
7990
|
const at = target(id);
|
|
7978
|
-
const
|
|
7979
|
-
if (!
|
|
7980
|
-
const changedProps = !
|
|
7981
|
-
const own =
|
|
7982
|
-
return own ||
|
|
7991
|
+
const info2 = at ? inspectCanvasInstance(nodes(), at) : void 0;
|
|
7992
|
+
if (!info2) return "Select an instance or a layer inside one.";
|
|
7993
|
+
const changedProps = !info2.nested && info2.properties.some((p) => p.value !== p.default);
|
|
7994
|
+
const own = info2.id === at ? info2.overrides.length > 0 : getNodeOverrides(nodes().find((n3) => n3.id === at)).length > 0;
|
|
7995
|
+
return own || info2.id === at && changedProps ? true : "No overrides to reset.";
|
|
7983
7996
|
},
|
|
7984
7997
|
/** Push the (outermost) instance's overrides to its main component. */
|
|
7985
7998
|
pushOverrides(id) {
|
|
@@ -8263,7 +8276,12 @@ function strokeToShapeNode(stroke, options = {}) {
|
|
|
8263
8276
|
strokeLinecap: stroke.style.cap ?? "round",
|
|
8264
8277
|
strokeLinejoin: stroke.style.join ?? "round",
|
|
8265
8278
|
...strokeGradient ? { strokeGradient } : {},
|
|
8266
|
-
...stroke.style.brush !== void 0 ? {
|
|
8279
|
+
...stroke.style.brush !== void 0 ? {
|
|
8280
|
+
brush: {
|
|
8281
|
+
...freehandBrushstroke(stroke.style.brush, localPoints, stroke.samples, options.profileLength),
|
|
8282
|
+
...stroke.style.paint !== void 0 ? { paint: stroke.style.paint } : {}
|
|
8283
|
+
}
|
|
8284
|
+
} : {},
|
|
8267
8285
|
fill: "none"
|
|
8268
8286
|
},
|
|
8269
8287
|
tool: stroke.tool
|
|
@@ -8432,7 +8450,11 @@ function createCanvasDrawController(options) {
|
|
|
8432
8450
|
if ((input.pressure || input.tiltX || input.tiltY) && style.brush === void 0) {
|
|
8433
8451
|
throw new Error("pressure and tilt profiles only shape brushed strokes: pass a brush too (see canvas.listBrushes).");
|
|
8434
8452
|
}
|
|
8453
|
+
if (style.paint !== void 0 && style.brush === void 0) {
|
|
8454
|
+
throw new Error("paint (gradient paint) only colours brushed strokes: pass a brush too (see canvas.listBrushes), or use color for a plain stroke.");
|
|
8455
|
+
}
|
|
8435
8456
|
if (style.brush !== void 0) await validateCanvasBrush(style.brush);
|
|
8457
|
+
if (style.paint !== void 0) style.paint = (await loadCanvasBrushstrokeRenderer()).resolveCanvasPaint(style.paint);
|
|
8436
8458
|
const id = options.generateNodeId?.() ?? `stroke-${Date.now()}-${Math.random().toString(36).slice(2, 7)}`;
|
|
8437
8459
|
const smoothed = input.streamline ? streamlinePoints(points, options.drawOptions?.streamline ?? 0.35) : [...points];
|
|
8438
8460
|
const node = strokeToShapeNode(
|
|
@@ -11601,6 +11623,371 @@ function createCanvasCaptureToolProvider(options) {
|
|
|
11601
11623
|
id: options.id ?? "canvas-capture"
|
|
11602
11624
|
});
|
|
11603
11625
|
}
|
|
11626
|
+
|
|
11627
|
+
// src/canvas/effects.ts
|
|
11628
|
+
var CANVAS_EFFECT_PLACES = {
|
|
11629
|
+
/** Paints a shape's inside. */
|
|
11630
|
+
fill: ["generator", "interactive"],
|
|
11631
|
+
/** Draws over a node's box. */
|
|
11632
|
+
overlay: ["overlay", "title", "interactive", "generator"],
|
|
11633
|
+
transition: ["transition"]
|
|
11634
|
+
};
|
|
11635
|
+
function canvasNodeEffect(node) {
|
|
11636
|
+
const shape = node.data?.shape;
|
|
11637
|
+
return isShapeDescription(shape) ? shape.effect : void 0;
|
|
11638
|
+
}
|
|
11639
|
+
function createCanvasEffectsController(options) {
|
|
11640
|
+
const slot = new EffectsSlot();
|
|
11641
|
+
if (options.effects) slot.set(options.effects);
|
|
11642
|
+
const describe = async (effect, place2) => {
|
|
11643
|
+
const d = await slot.require().describe(effect);
|
|
11644
|
+
const allowed = CANVAS_EFFECT_PLACES[place2];
|
|
11645
|
+
if (d.kind === "motion" || !d.uses.some((u) => allowed.includes(u))) {
|
|
11646
|
+
const where = place2 === "transition" ? "a scene transition" : place2 === "fill" ? "a fill" : "an overlay";
|
|
11647
|
+
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);
|
|
11648
|
+
}
|
|
11649
|
+
return d;
|
|
11650
|
+
};
|
|
11651
|
+
const shapeNode = (id) => {
|
|
11652
|
+
const node = options.getNodes().find((n3) => n3.id === id);
|
|
11653
|
+
if (!node) throw new Error(`There is no node "${id}"; the nodes are ${options.getNodes().map((n3) => `"${n3.id}"`).slice(0, 20).join(", ") || "none"}.`);
|
|
11654
|
+
const shape = node.data?.shape;
|
|
11655
|
+
if (!isShapeDescription(shape)) throw new Error(`Node "${id}" is not a shape; effects draw on shape nodes (data.shape).`);
|
|
11656
|
+
return { node, shape };
|
|
11657
|
+
};
|
|
11658
|
+
const commit = (label, next) => {
|
|
11659
|
+
const before = options.getNodes();
|
|
11660
|
+
if (options.history) options.history.execute({ name: label, execute: () => options.setNodes(next), undo: () => options.setNodes(before), redo: () => options.setNodes(next) });
|
|
11661
|
+
else options.setNodes(next);
|
|
11662
|
+
};
|
|
11663
|
+
const withEffect = (id, effect) => options.getNodes().map((n3) => {
|
|
11664
|
+
if (n3.id !== id) return n3;
|
|
11665
|
+
const shape = { ...n3.data.shape };
|
|
11666
|
+
if (effect) shape.effect = effect;
|
|
11667
|
+
else delete shape.effect;
|
|
11668
|
+
return { ...n3, data: { ...n3.data, shape } };
|
|
11669
|
+
});
|
|
11670
|
+
const mergeValues = (d, was, values) => {
|
|
11671
|
+
const base = { ...was };
|
|
11672
|
+
const given = {};
|
|
11673
|
+
for (const [k, v] of Object.entries(values)) {
|
|
11674
|
+
if (v !== null) given[k] = v;
|
|
11675
|
+
else if (k in d.controls.properties) base[k] = d.defaults[k];
|
|
11676
|
+
else throw new Error(`"${k}" is not a control of "${d.id}"; its controls are ${Object.keys(d.controls.properties).join(", ")}.`);
|
|
11677
|
+
}
|
|
11678
|
+
return { ...base, ...resolveEffectValues({ ...d, defaults: base }, given) };
|
|
11679
|
+
};
|
|
11680
|
+
return {
|
|
11681
|
+
/** The provider slot. */
|
|
11682
|
+
effects: slot,
|
|
11683
|
+
get effectsProvider() {
|
|
11684
|
+
return slot.provider;
|
|
11685
|
+
},
|
|
11686
|
+
/** Registers (or with `null` removes) the effects provider. */
|
|
11687
|
+
setEffectsProvider(provider) {
|
|
11688
|
+
slot.set(provider);
|
|
11689
|
+
},
|
|
11690
|
+
/** Registers `provider` and returns a function that removes it again. */
|
|
11691
|
+
useEffects(provider) {
|
|
11692
|
+
return slot.use(provider);
|
|
11693
|
+
},
|
|
11694
|
+
/** The shape nodes that carry effects (or one node's). */
|
|
11695
|
+
list(nodeId) {
|
|
11696
|
+
return options.getNodes().filter((n3) => !nodeId || n3.id === nodeId).map((n3) => ({ nodeId: n3.id, effect: canvasNodeEffect(n3) })).filter((e) => !!e.effect);
|
|
11697
|
+
},
|
|
11698
|
+
/**
|
|
11699
|
+
* Draws a provider effect on a shape node: `fill` paints its inside (generators), `overlay`
|
|
11700
|
+
* draws over its box (overlays, titles, interactive effects). Replaces any effect it had. One
|
|
11701
|
+
* undoable step. Returns the effect.
|
|
11702
|
+
*/
|
|
11703
|
+
async applyNodeEffect(nodeId, effect, opts = {}) {
|
|
11704
|
+
shapeNode(nodeId);
|
|
11705
|
+
const mode = opts.mode ?? "fill";
|
|
11706
|
+
const d = await describe(effect, mode);
|
|
11707
|
+
if (opts.opacity !== void 0 && !(opts.opacity >= 0 && opts.opacity <= 1)) throw new Error("opacity must be from 0 to 1.");
|
|
11708
|
+
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 } : {} };
|
|
11709
|
+
commit(`Add effect ${d.name}`, withEffect(nodeId, next));
|
|
11710
|
+
return next;
|
|
11711
|
+
},
|
|
11712
|
+
/** Changes a node's effect: `values` merged (`null` resets), `mode`, `opacity`, `playing`. One undoable step. */
|
|
11713
|
+
async updateNodeEffect(nodeId, changes) {
|
|
11714
|
+
const { shape } = shapeNode(nodeId);
|
|
11715
|
+
const was = shape.effect;
|
|
11716
|
+
if (!was) throw new Error(`Node "${nodeId}" has no effect; add one with apply_effect.`);
|
|
11717
|
+
const next = { ...was };
|
|
11718
|
+
if (changes.values || changes.mode) {
|
|
11719
|
+
const d = await describe(was.effect, changes.mode ?? was.mode);
|
|
11720
|
+
if (changes.values) next.values = mergeValues(d, was.values, changes.values);
|
|
11721
|
+
if (changes.mode) next.mode = changes.mode;
|
|
11722
|
+
} else slot.require();
|
|
11723
|
+
if (changes.opacity !== void 0) {
|
|
11724
|
+
if (!(changes.opacity >= 0 && changes.opacity <= 1)) throw new Error("opacity must be from 0 to 1.");
|
|
11725
|
+
if (changes.opacity === 1) delete next.opacity;
|
|
11726
|
+
else next.opacity = changes.opacity;
|
|
11727
|
+
}
|
|
11728
|
+
if (changes.playing !== void 0) {
|
|
11729
|
+
if (changes.playing) delete next.playing;
|
|
11730
|
+
else next.playing = false;
|
|
11731
|
+
}
|
|
11732
|
+
commit(`Change effect ${was.name ?? was.effect}`, withEffect(nodeId, next));
|
|
11733
|
+
return next;
|
|
11734
|
+
},
|
|
11735
|
+
/** Removes a node's effect (works without a provider). One undoable step. */
|
|
11736
|
+
removeNodeEffect(nodeId) {
|
|
11737
|
+
const { shape } = shapeNode(nodeId);
|
|
11738
|
+
if (!shape.effect) throw new Error(`Node "${nodeId}" has no effect.`);
|
|
11739
|
+
commit(`Remove effect ${shape.effect.name ?? shape.effect.effect}`, withEffect(nodeId, void 0));
|
|
11740
|
+
return shape.effect;
|
|
11741
|
+
},
|
|
11742
|
+
/** The scenes, in order (empty without a Scenes store). */
|
|
11743
|
+
scenes() {
|
|
11744
|
+
return options.scenes?.getState().scenes ?? [];
|
|
11745
|
+
},
|
|
11746
|
+
/** The transition into a scene, if it has one. */
|
|
11747
|
+
sceneTransition(sceneId) {
|
|
11748
|
+
return options.scenes?.getScene(sceneId)?.transition;
|
|
11749
|
+
},
|
|
11750
|
+
/**
|
|
11751
|
+
* Sets (or with `effect` null removes) the provider transition played when the canvas moves to
|
|
11752
|
+
* a scene. Returns the transition.
|
|
11753
|
+
*/
|
|
11754
|
+
async setSceneTransition(sceneId, effect, opts = {}) {
|
|
11755
|
+
const scenes2 = options.scenes;
|
|
11756
|
+
if (!scenes2) throw new Error("This canvas has no Scenes store.");
|
|
11757
|
+
if (!scenes2.getScene(sceneId)) throw new Error(`There is no scene "${sceneId}"; the scenes are ${scenes2.getState().scenes.map((s) => `"${s.id}"`).join(", ") || "none"}.`);
|
|
11758
|
+
if (effect === null) {
|
|
11759
|
+
scenes2.update(sceneId, { transition: void 0 });
|
|
11760
|
+
return null;
|
|
11761
|
+
}
|
|
11762
|
+
const d = await describe(effect, "transition");
|
|
11763
|
+
const duration = opts.duration ?? d.duration ?? 600;
|
|
11764
|
+
if (!(duration > 0)) throw new Error("duration must be a number of ms above 0.");
|
|
11765
|
+
const transition = { effect: d.id, provider: slot.require().id, name: d.name, values: resolveEffectValues(d, opts.values), duration };
|
|
11766
|
+
scenes2.update(sceneId, { transition });
|
|
11767
|
+
return transition;
|
|
11768
|
+
}
|
|
11769
|
+
};
|
|
11770
|
+
}
|
|
11771
|
+
|
|
11772
|
+
// src/canvas/effect-player.ts
|
|
11773
|
+
var defaultClock = {
|
|
11774
|
+
now: () => typeof performance !== "undefined" ? performance.now() : Date.now(),
|
|
11775
|
+
frame: (cb) => {
|
|
11776
|
+
if (typeof requestAnimationFrame === "undefined") {
|
|
11777
|
+
const t = setTimeout(cb, 16);
|
|
11778
|
+
return () => clearTimeout(t);
|
|
11779
|
+
}
|
|
11780
|
+
const id = requestAnimationFrame(cb);
|
|
11781
|
+
return () => cancelAnimationFrame(id);
|
|
11782
|
+
}
|
|
11783
|
+
};
|
|
11784
|
+
var canvasEffectsPool = new EffectRendererPool({ maxRenderers: 6 });
|
|
11785
|
+
function draw(target, p) {
|
|
11786
|
+
const ctx = target.getContext("2d");
|
|
11787
|
+
if (!ctx || typeof ImageData === "undefined") return;
|
|
11788
|
+
ctx.putImageData(new ImageData(new Uint8ClampedArray(p.data), p.width, p.height), 0, 0);
|
|
11789
|
+
}
|
|
11790
|
+
function runCanvasNodeEffect(options) {
|
|
11791
|
+
const { provider, effect, target } = options;
|
|
11792
|
+
const clock = options.clock ?? defaultClock;
|
|
11793
|
+
const pool = options.pool ?? canvasEffectsPool;
|
|
11794
|
+
const size2 = { width: Math.max(1, Math.round(target.width)), height: Math.max(1, Math.round(target.height)) };
|
|
11795
|
+
const start = clock.now();
|
|
11796
|
+
let stopped = false;
|
|
11797
|
+
let cancel = () => {
|
|
11798
|
+
};
|
|
11799
|
+
const tick = () => {
|
|
11800
|
+
if (stopped) return;
|
|
11801
|
+
const time = options.animate === false || effect.playing === false ? 0 : clock.now() - start;
|
|
11802
|
+
pool.render(provider, effect.effect, size2, { time, values: effect.values }).then(
|
|
11803
|
+
(p) => {
|
|
11804
|
+
if (stopped) return;
|
|
11805
|
+
draw(target, p);
|
|
11806
|
+
if (options.animate !== false && effect.playing !== false) cancel = clock.frame(tick);
|
|
11807
|
+
},
|
|
11808
|
+
(e) => {
|
|
11809
|
+
if (!stopped) options.onError?.(e);
|
|
11810
|
+
}
|
|
11811
|
+
);
|
|
11812
|
+
};
|
|
11813
|
+
tick();
|
|
11814
|
+
return {
|
|
11815
|
+
stop() {
|
|
11816
|
+
stopped = true;
|
|
11817
|
+
cancel();
|
|
11818
|
+
}
|
|
11819
|
+
};
|
|
11820
|
+
}
|
|
11821
|
+
async function playCanvasSceneTransition(options) {
|
|
11822
|
+
const { provider, transition, from, to, target } = options;
|
|
11823
|
+
const clock = options.clock ?? defaultClock;
|
|
11824
|
+
const pool = options.pool ?? canvasEffectsPool;
|
|
11825
|
+
const duration = transition.duration ?? 600;
|
|
11826
|
+
const size2 = { width: from.width, height: from.height };
|
|
11827
|
+
const start = clock.now();
|
|
11828
|
+
for (; ; ) {
|
|
11829
|
+
const time = Math.min(duration, clock.now() - start);
|
|
11830
|
+
const p = await pool.render(provider, transition.effect, size2, { time, progress: time / duration, values: transition.values, inputs: { from, to } });
|
|
11831
|
+
draw(target, p);
|
|
11832
|
+
if (time >= duration) return;
|
|
11833
|
+
await new Promise((resolve) => clock.frame(resolve));
|
|
11834
|
+
}
|
|
11835
|
+
}
|
|
11836
|
+
async function captureCanvasView(element, size2) {
|
|
11837
|
+
const url = await rasterizeElement(element, { pixelWidth: size2.width, pixelHeight: size2.height, exclude: CAPTURE_OVERLAY_SELECTORS });
|
|
11838
|
+
const doc = element.ownerDocument;
|
|
11839
|
+
const image = new doc.defaultView.Image();
|
|
11840
|
+
await new Promise((resolve, reject) => {
|
|
11841
|
+
image.onload = () => resolve();
|
|
11842
|
+
image.onerror = () => reject(new Error("The canvas view could not be captured."));
|
|
11843
|
+
image.src = url;
|
|
11844
|
+
});
|
|
11845
|
+
const canvas = doc.createElement("canvas");
|
|
11846
|
+
canvas.width = size2.width;
|
|
11847
|
+
canvas.height = size2.height;
|
|
11848
|
+
const ctx = canvas.getContext("2d");
|
|
11849
|
+
if (!ctx) throw new Error("The canvas view could not be captured (no 2D canvas).");
|
|
11850
|
+
ctx.drawImage(image, 0, 0, size2.width, size2.height);
|
|
11851
|
+
const data = ctx.getImageData(0, 0, size2.width, size2.height).data;
|
|
11852
|
+
return { width: size2.width, height: size2.height, data };
|
|
11853
|
+
}
|
|
11854
|
+
async function transitionCanvasView(options) {
|
|
11855
|
+
const { element } = options;
|
|
11856
|
+
const clock = options.clock ?? defaultClock;
|
|
11857
|
+
const scale = options.scale ?? Math.min(1, typeof devicePixelRatio === "number" ? devicePixelRatio : 1);
|
|
11858
|
+
const size2 = { width: Math.max(1, Math.round(element.clientWidth * scale)), height: Math.max(1, Math.round(element.clientHeight * scale)) };
|
|
11859
|
+
let from;
|
|
11860
|
+
try {
|
|
11861
|
+
from = await captureCanvasView(element, size2);
|
|
11862
|
+
} catch {
|
|
11863
|
+
options.jump();
|
|
11864
|
+
return;
|
|
11865
|
+
}
|
|
11866
|
+
const overlay = element.ownerDocument.createElement("canvas");
|
|
11867
|
+
overlay.width = size2.width;
|
|
11868
|
+
overlay.height = size2.height;
|
|
11869
|
+
overlay.setAttribute("data-capture", "overlay");
|
|
11870
|
+
overlay.setAttribute("data-canvas-transition", options.transition.effect);
|
|
11871
|
+
Object.assign(overlay.style, { position: "absolute", inset: "0", width: "100%", height: "100%", pointerEvents: "none", zIndex: "1000" });
|
|
11872
|
+
overlay.getContext("2d")?.putImageData(new ImageData(new Uint8ClampedArray(from.data), size2.width, size2.height), 0, 0);
|
|
11873
|
+
if (getComputedStyle(element).position === "static") element.style.position = "relative";
|
|
11874
|
+
element.appendChild(overlay);
|
|
11875
|
+
try {
|
|
11876
|
+
options.jump();
|
|
11877
|
+
await new Promise((r) => clock.frame(() => clock.frame(r)));
|
|
11878
|
+
const to = await captureCanvasView(element, size2);
|
|
11879
|
+
await playCanvasSceneTransition({ provider: options.provider, transition: options.transition, from, to, target: overlay, clock });
|
|
11880
|
+
} catch {
|
|
11881
|
+
} finally {
|
|
11882
|
+
overlay.remove();
|
|
11883
|
+
}
|
|
11884
|
+
}
|
|
11885
|
+
|
|
11886
|
+
// src/canvas/effect-tools.ts
|
|
11887
|
+
var info = (e) => ({
|
|
11888
|
+
id: "effect",
|
|
11889
|
+
effect: e.effect,
|
|
11890
|
+
...e.name ? { name: e.name } : {},
|
|
11891
|
+
...e.provider ? { provider: e.provider } : {},
|
|
11892
|
+
role: e.mode === "fill" ? "fill" : "overlay",
|
|
11893
|
+
enabled: e.playing !== false,
|
|
11894
|
+
opacity: e.opacity ?? 1,
|
|
11895
|
+
values: { ...e.values }
|
|
11896
|
+
});
|
|
11897
|
+
function createCanvasEffectsToolProvider(controller, options = {}) {
|
|
11898
|
+
const readOnlyReason = () => {
|
|
11899
|
+
const ro = typeof options.readOnly === "function" ? options.readOnly() : options.readOnly;
|
|
11900
|
+
return ro ? typeof ro === "string" ? ro : "The canvas is read-only." : null;
|
|
11901
|
+
};
|
|
11902
|
+
const result = (nodeId, effect) => {
|
|
11903
|
+
const now = controller.list(nodeId)[0]?.effect;
|
|
11904
|
+
return { target: nodeId, effect: info(effect ?? now), effects: now ? [info(now)] : [] };
|
|
11905
|
+
};
|
|
11906
|
+
const once = (instance) => {
|
|
11907
|
+
if (instance !== "effect") throw new Error('A shape node has one effect, "effect".');
|
|
11908
|
+
};
|
|
11909
|
+
return createEffectToolProvider({
|
|
11910
|
+
id: options.id ?? "canvas-effects",
|
|
11911
|
+
namespace: options.namespace,
|
|
11912
|
+
target: { noun: "node", id: "A shape node id on the canvas (a node with data.shape)." },
|
|
11913
|
+
applyUses: ["generator", "overlay", "title", "interactive"],
|
|
11914
|
+
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.",
|
|
11915
|
+
provider: () => controller.effectsProvider,
|
|
11916
|
+
readOnlyReason,
|
|
11917
|
+
list: (target) => controller.list(target).map((e) => ({ target: e.nodeId, effects: [info(e.effect)] })),
|
|
11918
|
+
apply: async (target, effect, args) => {
|
|
11919
|
+
if (args.index !== void 0) throw new Error("index does not apply: a shape node has one effect.");
|
|
11920
|
+
const provider = controller.effectsProvider;
|
|
11921
|
+
const d = provider ? await provider.describe(effect) : null;
|
|
11922
|
+
const mode = d && !d.uses.includes("generator") ? "overlay" : "fill";
|
|
11923
|
+
const applied = await controller.applyNodeEffect(target, effect, { mode, values: args.values, opacity: args.opacity });
|
|
11924
|
+
if (args.enabled === false) await controller.updateNodeEffect(target, { playing: false });
|
|
11925
|
+
return result(target, args.enabled === false ? { ...applied, playing: false } : applied);
|
|
11926
|
+
},
|
|
11927
|
+
update: async (target, instance, changes) => {
|
|
11928
|
+
once(instance);
|
|
11929
|
+
if (changes.index !== void 0) throw new Error("index does not apply: a shape node has one effect.");
|
|
11930
|
+
const e = await controller.updateNodeEffect(target, { values: changes.values, opacity: changes.opacity, playing: changes.enabled });
|
|
11931
|
+
return result(target, e);
|
|
11932
|
+
},
|
|
11933
|
+
remove: async (target, instance) => {
|
|
11934
|
+
once(instance);
|
|
11935
|
+
const was = controller.removeNodeEffect(target);
|
|
11936
|
+
return { target, effect: info(was), effects: [] };
|
|
11937
|
+
},
|
|
11938
|
+
subscribe: options.subscribe ? (notify) => options.subscribe(notify) : (notify) => controller.effects.subscribe(() => notify()),
|
|
11939
|
+
extraTools: (ctx) => [
|
|
11940
|
+
{
|
|
11941
|
+
name: "set_canvas_effect_mode",
|
|
11942
|
+
title: "Set a node effect's mode",
|
|
11943
|
+
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.",
|
|
11944
|
+
inputSchema: {
|
|
11945
|
+
type: "object",
|
|
11946
|
+
properties: {
|
|
11947
|
+
node: { type: "string", minLength: 1, description: "A shape node id from list_applied_effects." },
|
|
11948
|
+
mode: { type: "string", enum: ["fill", "overlay"], description: "fill or overlay." }
|
|
11949
|
+
},
|
|
11950
|
+
required: ["node", "mode"],
|
|
11951
|
+
additionalProperties: false
|
|
11952
|
+
},
|
|
11953
|
+
annotations: { title: "Set a node effect's mode", ...ctx.annotations.writeIdempotent },
|
|
11954
|
+
handler: async (args) => {
|
|
11955
|
+
ctx.requireProvider();
|
|
11956
|
+
return { node: args.node, effect: info(await controller.updateNodeEffect(args.node, { mode: args.mode })) };
|
|
11957
|
+
}
|
|
11958
|
+
},
|
|
11959
|
+
{
|
|
11960
|
+
name: "list_scene_transitions",
|
|
11961
|
+
title: "List scene transitions",
|
|
11962
|
+
description: "Lists the canvas Scenes in order with the transition effect played when moving to each (or null). Works without a provider.",
|
|
11963
|
+
inputSchema: { type: "object", properties: {}, additionalProperties: false },
|
|
11964
|
+
annotations: { title: "List scene transitions", ...ctx.annotations.read },
|
|
11965
|
+
handler: () => ({ scenes: controller.scenes().map((s) => ({ id: s.id, name: s.name, transition: s.transition ?? null })) })
|
|
11966
|
+
},
|
|
11967
|
+
{
|
|
11968
|
+
name: "set_scene_transition",
|
|
11969
|
+
title: "Set a scene transition",
|
|
11970
|
+
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.",
|
|
11971
|
+
inputSchema: {
|
|
11972
|
+
type: "object",
|
|
11973
|
+
properties: {
|
|
11974
|
+
scene: { type: "string", minLength: 1, description: "A scene id from list_scene_transitions." },
|
|
11975
|
+
effect: { anyOf: [ctx.effectIdSchema(["transition"]), { type: "null" }], description: "A transition effect id from search_effects, or null to remove the transition." },
|
|
11976
|
+
values: ctx.valuesSchema("Control values over the effect's defaults"),
|
|
11977
|
+
duration: { type: "number", exclusiveMinimum: 0, description: "Length in ms (default: the effect's own, else 600)." }
|
|
11978
|
+
},
|
|
11979
|
+
required: ["scene", "effect"],
|
|
11980
|
+
additionalProperties: false
|
|
11981
|
+
},
|
|
11982
|
+
annotations: { title: "Set a scene transition", ...ctx.annotations.writeIdempotent },
|
|
11983
|
+
handler: async (args) => {
|
|
11984
|
+
if (args.effect !== null) ctx.requireProvider();
|
|
11985
|
+
return { scene: args.scene, transition: await controller.setSceneTransition(args.scene, args.effect, { values: args.values, duration: args.duration }) };
|
|
11986
|
+
}
|
|
11987
|
+
}
|
|
11988
|
+
]
|
|
11989
|
+
});
|
|
11990
|
+
}
|
|
11604
11991
|
export {
|
|
11605
11992
|
ANIMATABLE_PROPERTIES,
|
|
11606
11993
|
ANIMATION_DATA_KEY,
|
|
@@ -11615,6 +12002,7 @@ export {
|
|
|
11615
12002
|
CANVAS_CAPTURE_OVERLAY_SELECTORS,
|
|
11616
12003
|
CANVAS_CAPTURE_VIEWPORT_SELECTOR,
|
|
11617
12004
|
CANVAS_COMPONENT_DRAG_TYPE,
|
|
12005
|
+
CANVAS_EFFECT_PLACES,
|
|
11618
12006
|
CANVAS_GRADIENT_PRESETS,
|
|
11619
12007
|
CANVAS_TOKEN_PROPERTIES,
|
|
11620
12008
|
CARD_INSPECTION_ZONES,
|
|
@@ -11697,8 +12085,11 @@ export {
|
|
|
11697
12085
|
canCombineShapeNodes,
|
|
11698
12086
|
canOffsetShapeNodes,
|
|
11699
12087
|
canOutlineShapeNodeStrokes,
|
|
12088
|
+
canvasEffectsPool,
|
|
11700
12089
|
canvasHeight,
|
|
11701
12090
|
canvasNodeCaptureRect,
|
|
12091
|
+
canvasNodeEffect,
|
|
12092
|
+
captureCanvasView,
|
|
11702
12093
|
centerNodes,
|
|
11703
12094
|
centre,
|
|
11704
12095
|
clampAnimationValue,
|
|
@@ -11728,6 +12119,8 @@ export {
|
|
|
11728
12119
|
createCanvasComponent,
|
|
11729
12120
|
createCanvasComponentsController,
|
|
11730
12121
|
createCanvasDrawController,
|
|
12122
|
+
createCanvasEffectsController,
|
|
12123
|
+
createCanvasEffectsToolProvider,
|
|
11731
12124
|
createCanvasInstance,
|
|
11732
12125
|
createCanvasScenesStore,
|
|
11733
12126
|
createDefaultStrokeStyle,
|
|
@@ -11964,6 +12357,7 @@ export {
|
|
|
11964
12357
|
pieScene,
|
|
11965
12358
|
placeBeside,
|
|
11966
12359
|
planarSplit,
|
|
12360
|
+
playCanvasSceneTransition,
|
|
11967
12361
|
playWebAnimations,
|
|
11968
12362
|
pointInBezierPath,
|
|
11969
12363
|
pointInMultiPolygon,
|
|
@@ -12009,6 +12403,7 @@ export {
|
|
|
12009
12403
|
revertShapeCombination,
|
|
12010
12404
|
rgbToOklab,
|
|
12011
12405
|
ringSignedArea,
|
|
12406
|
+
runCanvasNodeEffect,
|
|
12012
12407
|
runMultiNodeTransition,
|
|
12013
12408
|
sampleAnimationFrames,
|
|
12014
12409
|
scaleKeyframes,
|
|
@@ -12067,12 +12462,14 @@ export {
|
|
|
12067
12462
|
toWebAnimations,
|
|
12068
12463
|
toggleLockNodes,
|
|
12069
12464
|
transformBezierPath,
|
|
12465
|
+
transitionCanvasView,
|
|
12070
12466
|
trimSegments,
|
|
12071
12467
|
unionPolygons,
|
|
12072
12468
|
unitePaths,
|
|
12073
12469
|
unlockNodes,
|
|
12074
12470
|
updateKeyframe,
|
|
12075
12471
|
validateCanvasBrush,
|
|
12472
|
+
validateCanvasBrushPaint,
|
|
12076
12473
|
windingNumber,
|
|
12077
12474
|
withNodeAnimation,
|
|
12078
12475
|
withStyleOpacity,
|