@pixerate/editor 0.241.0 → 0.242.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/animation-editor/controller.d.cts +10 -0
- package/dist/animation-editor/controller.d.ts +10 -0
- package/dist/animation-editor/effects.d.cts +1 -1
- package/dist/animation-editor/effects.d.ts +1 -1
- package/dist/animation-editor/index.cjs +137 -88
- package/dist/animation-editor/index.js +60 -11
- package/dist/canvas/index.cjs +7 -7
- package/dist/canvas/index.js +1 -1
- package/dist/{chunk-A7PQNIBX.cjs → chunk-7V7QMFTI.cjs} +14 -14
- package/dist/{chunk-JAOSL334.cjs → chunk-GHCSC2WP.cjs} +38 -16
- package/dist/{chunk-JWCKJNDR.cjs → chunk-K5DS2JTA.cjs} +6 -6
- package/dist/{chunk-WDDUPWVG.js → chunk-L3JFOTBQ.js} +1 -1
- package/dist/{chunk-SIK3LT6M.js → chunk-OFH6Q67U.js} +28 -6
- package/dist/{chunk-G3RUKJ5C.js → chunk-RXZL4NG5.js} +1 -1
- package/dist/{chunk-TEFQISBY.js → chunk-X3ZINLIP.js} +1 -1
- package/dist/{chunk-ZQIWBJGH.cjs → chunk-XNZFVYC2.cjs} +7 -7
- package/dist/effects/fake.d.cts +4 -0
- package/dist/effects/fake.d.ts +4 -0
- package/dist/effects/helpers.d.cts +6 -0
- package/dist/effects/helpers.d.ts +6 -0
- package/dist/effects/index.cjs +222 -94
- package/dist/effects/index.js +131 -3
- package/dist/effects/types.d.cts +31 -0
- package/dist/effects/types.d.ts +31 -0
- package/dist/image-editor/index.cjs +13 -13
- package/dist/image-editor/index.js +1 -1
- package/dist/paint-editor/index.cjs +7 -7
- package/dist/paint-editor/index.js +2 -2
- package/dist/vector/index.cjs +6 -6
- package/dist/vector/index.js +1 -1
- package/dist/video-editor/controller.d.cts +49 -1
- package/dist/video-editor/controller.d.ts +49 -1
- package/dist/video-editor/effect-path.d.cts +66 -0
- package/dist/video-editor/effect-path.d.ts +66 -0
- package/dist/video-editor/index.cjs +616 -427
- package/dist/video-editor/index.d.cts +1 -0
- package/dist/video-editor/index.d.ts +1 -0
- package/dist/video-editor/index.js +193 -4
- package/package.json +1 -1
- package/skills/pixerate-animation-editor/SKILL.md +1 -1
- package/skills/pixerate-video-editor/SKILL.md +9 -2
|
@@ -16,7 +16,7 @@ import {
|
|
|
16
16
|
effectSubjectKind,
|
|
17
17
|
effectTemplateTools,
|
|
18
18
|
fillEffectTemplate
|
|
19
|
-
} from "../chunk-
|
|
19
|
+
} from "../chunk-RXZL4NG5.js";
|
|
20
20
|
import {
|
|
21
21
|
IDENTITY_MATRIX,
|
|
22
22
|
applyMatrix,
|
|
@@ -66,9 +66,10 @@ import {
|
|
|
66
66
|
cameraViewOfBox,
|
|
67
67
|
createEffectToolProvider,
|
|
68
68
|
isEffectError,
|
|
69
|
+
pathControlsOf,
|
|
69
70
|
resolveCameraMoveSpec,
|
|
70
71
|
resolveEffectValues
|
|
71
|
-
} from "../chunk-
|
|
72
|
+
} from "../chunk-OFH6Q67U.js";
|
|
72
73
|
import "../chunk-V2ZCJRDM.js";
|
|
73
74
|
import "../chunk-2PBZBRP5.js";
|
|
74
75
|
import "../chunk-ODBT55Q7.js";
|
|
@@ -4606,9 +4607,19 @@ function motionPartsFromVectorEffect(artwork, motion, source, box2 = { x: 0, y:
|
|
|
4606
4607
|
const art = imported.scene.artwork;
|
|
4607
4608
|
const prefix = `${source.instance}-`;
|
|
4608
4609
|
const { nodes, shots } = prefixed(art.nodes, imported.scene.shots, prefix);
|
|
4609
|
-
|
|
4610
|
-
|
|
4611
|
-
|
|
4610
|
+
let k;
|
|
4611
|
+
let dx;
|
|
4612
|
+
let dy;
|
|
4613
|
+
if (box2 === "viewBox") {
|
|
4614
|
+
const vb = /viewBox\s*=\s*["']([^"']+)["']/.exec(motion.svg)?.[1].trim().split(/[\s,]+/).map(Number) ?? [];
|
|
4615
|
+
k = 1;
|
|
4616
|
+
dx = vb.length === 4 && Number.isFinite(vb[0]) ? vb[0] : 0;
|
|
4617
|
+
dy = vb.length === 4 && Number.isFinite(vb[1]) ? vb[1] : 0;
|
|
4618
|
+
} else {
|
|
4619
|
+
k = Math.min(box2.width / art.width, box2.height / art.height);
|
|
4620
|
+
dx = box2.x + (box2.width - art.width * k) / 2;
|
|
4621
|
+
dy = box2.y + (box2.height - art.height * k) / 2;
|
|
4622
|
+
}
|
|
4612
4623
|
const group = { type: "group", id: source.instance, name: source.name ?? source.effect, children: nodes, transform: [k, 0, 0, k, dx, dy] };
|
|
4613
4624
|
const tagged = shots.map((s, i) => ({ ...s, id: i === 0 ? source.instance + "-shot" : `${source.instance}-shot-${i + 1}`, run: tag(s.run, source) }));
|
|
4614
4625
|
return { group, shots: tagged.map((s) => s.hold === 0 ? (({ hold: _h, ...rest }) => rest)(s) : s), notes: imported.notes };
|
|
@@ -4891,11 +4902,34 @@ var AnimationEditorController = class {
|
|
|
4891
4902
|
addVectorEffect(effect, options = {}) {
|
|
4892
4903
|
return this.effectEdit(async () => {
|
|
4893
4904
|
const d = await this.describeFor(effect, ["vector"]);
|
|
4894
|
-
|
|
4905
|
+
let given = options.values;
|
|
4906
|
+
let place = options.box;
|
|
4907
|
+
if (options.path) {
|
|
4908
|
+
const controls = pathControlsOf(d);
|
|
4909
|
+
if (!controls.length) throw new Error(`"${d.id}" does not draw along a path. Find one that does with search_effects accepts ["path"] (kind vector).`);
|
|
4910
|
+
const control = options.control ?? (controls.length === 1 ? controls[0] : void 0);
|
|
4911
|
+
if (!control) throw new Error(`"${d.id}" has several path controls (${controls.join(", ")}); pass control.`);
|
|
4912
|
+
if (!controls.includes(control)) throw new Error(`"${control}" is not a path control of "${d.id}"; its path controls are ${controls.join(", ")}.`);
|
|
4913
|
+
let value;
|
|
4914
|
+
if ("node" in options.path) {
|
|
4915
|
+
const entry = indexMotionNodes(this.scene.artwork.nodes).get(options.path.node);
|
|
4916
|
+
if (!entry) throw new Error(`There is no node "${options.path.node}"; find_animation_elements lists the artwork's nodes.`);
|
|
4917
|
+
if (entry.node.type !== "path") throw new Error(`Node "${options.path.node}" is a ${entry.node.type}, not a path; pass a path node (find_animation_elements shows each node's type).`);
|
|
4918
|
+
value = bezierPathToSvg(vectorPathWorldBezier(entry.node, entry.parentWorld, { effects: false }));
|
|
4919
|
+
place ??= "viewBox";
|
|
4920
|
+
} else {
|
|
4921
|
+
const provider = this.effects.require();
|
|
4922
|
+
if (!provider.pathFromText) throw new Error(`The effects provider "${provider.id}" cannot write words as a path; pass a path node instead.`);
|
|
4923
|
+
if (typeof options.path.text !== "string" || !options.path.text.trim()) throw new Error("text is empty; pass the words to write.");
|
|
4924
|
+
value = await provider.pathFromText(options.path.text, { ...options.path.font ? { font: options.path.font } : {}, ...options.path.align ? { align: options.path.align } : {} });
|
|
4925
|
+
}
|
|
4926
|
+
given = { ...given ?? {}, [control]: value };
|
|
4927
|
+
}
|
|
4928
|
+
const values = resolveEffectValues(d, given);
|
|
4895
4929
|
const instance = options.instance ?? nextMotionEffectInstance(this.scene);
|
|
4896
4930
|
const motion = await this.effects.require().applyVectorEffect(d.id, motionArtworkSvg(this.scene.artwork), values);
|
|
4897
4931
|
const source = { instance, effect: d.id, provider: this.effects.require().id, name: d.name, values };
|
|
4898
|
-
const { group, shots, notes } = motionPartsFromVectorEffect(this.scene.artwork, motion, source,
|
|
4932
|
+
const { group, shots, notes } = motionPartsFromVectorEffect(this.scene.artwork, motion, source, place);
|
|
4899
4933
|
return { scene: { ...this.scene, artwork: { ...this.scene.artwork, nodes: [...this.scene.artwork.nodes, group] }, shots: [...this.scene.shots, ...shots] }, label: `Add ${d.name}`, instance, notes };
|
|
4900
4934
|
});
|
|
4901
4935
|
}
|
|
@@ -7198,7 +7232,7 @@ function createAnimationEffectsToolProvider(controller, options = {}) {
|
|
|
7198
7232
|
{
|
|
7199
7233
|
name: "add_vector_effect",
|
|
7200
7234
|
title: "Add a vector effect",
|
|
7201
|
-
description:
|
|
7235
|
+
description: 'Adds a provider vector effect that brings its own picture (search_effects with kind vector: lower thirds, loaders, stickers): its SVG becomes an artwork group, fitted into box (default the whole artwork), and its motion becomes shots after the last one. For an effect that draws along a path (search_effects accepts ["path"]: a signature, a route), pass path: { node } to run it along a path node (its outline with every transform applied; the picture then lands on the node at scale 1 unless box is given) or { text } for words the provider writes. Use it instead of follow or draw-on when the effect itself should trace the path. The path is copied when added; add it again after reshaping the node. Change it with update_effect (target and effect: the returned instance) or remove it with remove_effect. One undo step. Returns the instance, the group id and any notes on what could not be carried over.',
|
|
7202
7236
|
inputSchema: {
|
|
7203
7237
|
type: "object",
|
|
7204
7238
|
properties: {
|
|
@@ -7214,8 +7248,21 @@ function createAnimationEffectsToolProvider(controller, options = {}) {
|
|
|
7214
7248
|
},
|
|
7215
7249
|
required: ["x", "y", "width", "height"],
|
|
7216
7250
|
additionalProperties: false,
|
|
7217
|
-
description: "Where the picture goes in the artwork, fitted keeping its proportions (default the whole artwork)."
|
|
7218
|
-
}
|
|
7251
|
+
description: "Where the picture goes in the artwork, fitted keeping its proportions (default the whole artwork; with path { node }, on the node at scale 1)."
|
|
7252
|
+
},
|
|
7253
|
+
path: {
|
|
7254
|
+
type: "object",
|
|
7255
|
+
description: "The path the effect draws along (only for effects with a path control): { node } or { text }.",
|
|
7256
|
+
properties: {
|
|
7257
|
+
node: { type: "string", minLength: 1, description: "A path node id (find_animation_elements)." },
|
|
7258
|
+
text: { type: "string", minLength: 1, description: "Words the provider writes as a single-stroke path." },
|
|
7259
|
+
font: { type: "string", minLength: 1, description: "With text: a single-stroke font the provider offers (default its first)." },
|
|
7260
|
+
align: { type: "string", enum: ["start", "middle", "end"], description: "With text: where the line sits (default start)." }
|
|
7261
|
+
},
|
|
7262
|
+
oneOf: [{ required: ["node"] }, { required: ["text"] }],
|
|
7263
|
+
additionalProperties: false
|
|
7264
|
+
},
|
|
7265
|
+
control: { type: "string", minLength: 1, description: "Which path control takes the path (default the effect's only one; describe_effect pathControls lists them)." }
|
|
7219
7266
|
},
|
|
7220
7267
|
required: ["effect"],
|
|
7221
7268
|
additionalProperties: false
|
|
@@ -7223,7 +7270,9 @@ function createAnimationEffectsToolProvider(controller, options = {}) {
|
|
|
7223
7270
|
annotations: { title: "Add a vector effect", ...ctx.annotations.write },
|
|
7224
7271
|
handler: async (args) => {
|
|
7225
7272
|
ctx.requireProvider();
|
|
7226
|
-
const
|
|
7273
|
+
const path = args.path ? args.path.node !== void 0 ? { node: args.path.node } : { text: args.path.text, font: args.path.font, align: args.path.align } : void 0;
|
|
7274
|
+
if (args.path && args.path.node !== void 0 && args.path.text !== void 0) throw new Error("Pass path as { node } or { text }, not both.");
|
|
7275
|
+
const r = await controller.addVectorEffect(args.effect, { values: args.values, box: args.box, path, control: args.control });
|
|
7227
7276
|
if (!r.ok) throw new Error(r.error);
|
|
7228
7277
|
return { instance: r.effect.instance, group: r.effect.group, effect: info(r.effect), ...r.notes.length ? { notes: r.notes } : {} };
|
|
7229
7278
|
}
|
package/dist/canvas/index.cjs
CHANGED
|
@@ -220,7 +220,7 @@ var _chunkPY4GWSE2cjs = require('../chunk-PY4GWSE2.cjs');
|
|
|
220
220
|
|
|
221
221
|
|
|
222
222
|
|
|
223
|
-
var
|
|
223
|
+
var _chunkGHCSC2WPcjs = require('../chunk-GHCSC2WP.cjs');
|
|
224
224
|
require('../chunk-WOFXZ3XU.cjs');
|
|
225
225
|
|
|
226
226
|
|
|
@@ -11426,14 +11426,14 @@ function canvasEffectErrorReason(effect, error) {
|
|
|
11426
11426
|
}
|
|
11427
11427
|
var CANVAS_EFFECT_MISSING_PATTERN = "repeating-linear-gradient(45deg, rgba(128,128,128,0.28) 0 2px, transparent 2px 8px)";
|
|
11428
11428
|
function createCanvasEffectsController(options) {
|
|
11429
|
-
const slot = new (0,
|
|
11429
|
+
const slot = new (0, _chunkGHCSC2WPcjs.EffectsSlot)();
|
|
11430
11430
|
if (options.effects) slot.set(options.effects);
|
|
11431
11431
|
const describe = async (effect, place2) => {
|
|
11432
11432
|
const d = await slot.require().describe(effect);
|
|
11433
11433
|
const allowed = CANVAS_EFFECT_PLACES[place2];
|
|
11434
11434
|
if (d.kind === "motion" || !d.uses.some((u) => allowed.includes(u))) {
|
|
11435
11435
|
const where = place2 === "transition" ? "a scene transition" : place2 === "fill" ? "a fill" : "an overlay";
|
|
11436
|
-
throw new (0,
|
|
11436
|
+
throw new (0, _chunkGHCSC2WPcjs.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);
|
|
11437
11437
|
}
|
|
11438
11438
|
return d;
|
|
11439
11439
|
};
|
|
@@ -11464,7 +11464,7 @@ function createCanvasEffectsController(options) {
|
|
|
11464
11464
|
else if (k in d.controls.properties) base[k] = d.defaults[k];
|
|
11465
11465
|
else throw new Error(`"${k}" is not a control of "${d.id}"; its controls are ${Object.keys(d.controls.properties).join(", ")}.`);
|
|
11466
11466
|
}
|
|
11467
|
-
return { ...base, ...
|
|
11467
|
+
return { ...base, ..._chunkGHCSC2WPcjs.resolveEffectValues.call(void 0, { ...d, defaults: base }, given) };
|
|
11468
11468
|
};
|
|
11469
11469
|
return {
|
|
11470
11470
|
/** The provider slot. */
|
|
@@ -11498,7 +11498,7 @@ function createCanvasEffectsController(options) {
|
|
|
11498
11498
|
const mode = _nullishCoalesce(opts.mode, () => ( "fill"));
|
|
11499
11499
|
const d = await describe(effect, mode);
|
|
11500
11500
|
if (opts.opacity !== void 0 && !(opts.opacity >= 0 && opts.opacity <= 1)) throw new Error("opacity must be from 0 to 1.");
|
|
11501
|
-
const next = { effect: d.id, provider: slot.require().id, name: d.name, values:
|
|
11501
|
+
const next = { effect: d.id, provider: slot.require().id, name: d.name, values: _chunkGHCSC2WPcjs.resolveEffectValues.call(void 0, d, opts.values), mode, ...opts.opacity !== void 0 && opts.opacity !== 1 ? { opacity: opts.opacity } : {} };
|
|
11502
11502
|
commit(`Add effect ${d.name}`, withEffect(nodeId, next));
|
|
11503
11503
|
return next;
|
|
11504
11504
|
},
|
|
@@ -11555,7 +11555,7 @@ function createCanvasEffectsController(options) {
|
|
|
11555
11555
|
const d = await describe(effect, "transition");
|
|
11556
11556
|
const duration = _nullishCoalesce(_nullishCoalesce(opts.duration, () => ( d.duration)), () => ( 600));
|
|
11557
11557
|
if (!(duration > 0)) throw new Error("duration must be a number of ms above 0.");
|
|
11558
|
-
const transition = { effect: d.id, provider: slot.require().id, name: d.name, values:
|
|
11558
|
+
const transition = { effect: d.id, provider: slot.require().id, name: d.name, values: _chunkGHCSC2WPcjs.resolveEffectValues.call(void 0, d, opts.values), duration };
|
|
11559
11559
|
scenes2.update(sceneId, { transition });
|
|
11560
11560
|
return transition;
|
|
11561
11561
|
}
|
|
@@ -11699,7 +11699,7 @@ function createCanvasEffectsToolProvider(controller, options = {}) {
|
|
|
11699
11699
|
const once = (instance) => {
|
|
11700
11700
|
if (instance !== "effect") throw new Error('A shape node has one effect, "effect".');
|
|
11701
11701
|
};
|
|
11702
|
-
return
|
|
11702
|
+
return _chunkGHCSC2WPcjs.createEffectToolProvider.call(void 0, {
|
|
11703
11703
|
id: _nullishCoalesce(options.id, () => ( "canvas-effects")),
|
|
11704
11704
|
namespace: options.namespace,
|
|
11705
11705
|
title: "Canvas effects",
|
package/dist/canvas/index.js
CHANGED
|
@@ -3,7 +3,7 @@
|
|
|
3
3
|
|
|
4
4
|
|
|
5
5
|
|
|
6
|
-
var
|
|
6
|
+
var _chunkGHCSC2WPcjs = require('./chunk-GHCSC2WP.cjs');
|
|
7
7
|
|
|
8
8
|
|
|
9
9
|
var _chunkFJ2MSI43cjs = require('./chunk-FJ2MSI43.cjs');
|
|
@@ -26,7 +26,7 @@ function documentErrors(doc, description) {
|
|
|
26
26
|
const isRecord = (v) => typeof v === "object" && v !== null && !Array.isArray(v);
|
|
27
27
|
if (doc.values !== void 0) {
|
|
28
28
|
if (!isRecord(doc.values)) errors.push({ path: "/values", message: "must be an object of control values" });
|
|
29
|
-
else errors.push(...
|
|
29
|
+
else errors.push(..._chunkGHCSC2WPcjs.effectValueErrors.call(void 0, description, doc.values).map((e) => ({ ...e, path: `/values${e.path}` })));
|
|
30
30
|
}
|
|
31
31
|
if (doc.tracks !== void 0) {
|
|
32
32
|
if (!isRecord(doc.tracks)) errors.push({ path: "/tracks", message: "must be an object of keyframe lists" });
|
|
@@ -39,7 +39,7 @@ function documentErrors(doc, description) {
|
|
|
39
39
|
kfs.forEach((kf, i) => {
|
|
40
40
|
if (!isRecord(kf)) return errors.push({ path: `${at}/${i}`, message: "must be a keyframe object" });
|
|
41
41
|
if (typeof kf.time !== "number" || !Number.isFinite(kf.time) || kf.time < 0) errors.push({ path: `${at}/${i}/time`, message: "must be a finite number of ms >= 0" });
|
|
42
|
-
errors.push(...
|
|
42
|
+
errors.push(..._chunkGHCSC2WPcjs.effectValueErrors.call(void 0, description, { [control]: kf.value }).map((e) => ({ ...e, path: `${at}/${i}/value` })));
|
|
43
43
|
if (kf.easing !== void 0 && !_chunkDWJJI6RTcjs.isAnimationEasing.call(void 0, kf.easing)) errors.push({ path: `${at}/${i}/easing`, message: "is not a valid easing" });
|
|
44
44
|
});
|
|
45
45
|
}
|
|
@@ -49,7 +49,7 @@ function documentErrors(doc, description) {
|
|
|
49
49
|
else
|
|
50
50
|
doc.presets.forEach((p, i) => {
|
|
51
51
|
if (!isRecord(p) || typeof p.name !== "string" || !p.name) return errors.push({ path: `/presets/${i}`, message: "must have a name" });
|
|
52
|
-
errors.push(...
|
|
52
|
+
errors.push(..._chunkGHCSC2WPcjs.effectValueErrors.call(void 0, description, p.values).map((e) => ({ ...e, path: `/presets/${i}/values${e.path}` })));
|
|
53
53
|
});
|
|
54
54
|
}
|
|
55
55
|
if (doc.duration !== void 0 && (typeof doc.duration !== "number" || !Number.isFinite(doc.duration) || !(doc.duration > 0))) {
|
|
@@ -170,7 +170,7 @@ async function createEffectAuthoringSession(source, options) {
|
|
|
170
170
|
if (isDocumentSource(source)) {
|
|
171
171
|
const doc = source.document;
|
|
172
172
|
if (doc.version !== 1 || !doc.effectId) {
|
|
173
|
-
throw new (0,
|
|
173
|
+
throw new (0, _chunkGHCSC2WPcjs.EffectUnsupportedError)("Invalid effect authoring document: version 1 required");
|
|
174
174
|
}
|
|
175
175
|
effectId = doc.effectId;
|
|
176
176
|
initialDoc = doc;
|
|
@@ -178,15 +178,15 @@ async function createEffectAuthoringSession(source, options) {
|
|
|
178
178
|
effectId = source.effectId;
|
|
179
179
|
requestedPreset = source.preset;
|
|
180
180
|
} else {
|
|
181
|
-
throw new (0,
|
|
181
|
+
throw new (0, _chunkGHCSC2WPcjs.EffectUnsupportedError)("Invalid effect authoring source");
|
|
182
182
|
}
|
|
183
183
|
const description = await provider.describe(effectId);
|
|
184
184
|
if (initialDoc) {
|
|
185
185
|
const problems = documentErrors(initialDoc, description);
|
|
186
|
-
if (problems.length) throw new (0,
|
|
186
|
+
if (problems.length) throw new (0, _chunkGHCSC2WPcjs.EffectControlError)(effectId, problems);
|
|
187
187
|
}
|
|
188
188
|
if (description.kind === "motion") {
|
|
189
|
-
throw new (0,
|
|
189
|
+
throw new (0, _chunkGHCSC2WPcjs.EffectUnsupportedError)("Motion effects cannot be authored as pixel effects", effectId);
|
|
190
190
|
}
|
|
191
191
|
let disposed = false;
|
|
192
192
|
let playing = false;
|
|
@@ -207,7 +207,7 @@ async function createEffectAuthoringSession(source, options) {
|
|
|
207
207
|
const found = description.presets.find((p) => p.name === requestedPreset);
|
|
208
208
|
if (!found) {
|
|
209
209
|
const validNames = description.presets.map((p) => `"${p.name}"`).join(", ");
|
|
210
|
-
throw new (0,
|
|
210
|
+
throw new (0, _chunkGHCSC2WPcjs.EffectUnsupportedError)(
|
|
211
211
|
`Unknown preset "${requestedPreset}" for effect "${effectId}". Valid presets are: ${validNames || "none"}`,
|
|
212
212
|
effectId
|
|
213
213
|
);
|
|
@@ -321,7 +321,7 @@ async function createEffectAuthoringSession(source, options) {
|
|
|
321
321
|
if (!validNames.includes(name)) {
|
|
322
322
|
return { ok: false, message: `there is no control "${name}"; the controls are ${validNames.join(", ")}` };
|
|
323
323
|
}
|
|
324
|
-
const errors =
|
|
324
|
+
const errors = _chunkGHCSC2WPcjs.effectValueErrors.call(void 0, description, { [name]: value });
|
|
325
325
|
if (errors.length > 0) {
|
|
326
326
|
return { ok: false, message: valueErrorMessage(errors) };
|
|
327
327
|
}
|
|
@@ -391,7 +391,7 @@ async function createEffectAuthoringSession(source, options) {
|
|
|
391
391
|
if (!validNames.includes(k)) {
|
|
392
392
|
return { ok: false, message: `there is no control "${k}"; the controls are ${validNames.join(", ")}` };
|
|
393
393
|
}
|
|
394
|
-
const errors =
|
|
394
|
+
const errors = _chunkGHCSC2WPcjs.effectValueErrors.call(void 0, description, { [k]: v });
|
|
395
395
|
if (errors.length > 0) {
|
|
396
396
|
return { ok: false, message: valueErrorMessage(errors) };
|
|
397
397
|
}
|
|
@@ -528,7 +528,7 @@ async function createEffectAuthoringSession(source, options) {
|
|
|
528
528
|
return { ok: false, message: `invalid easing: ${String(easing)}` };
|
|
529
529
|
}
|
|
530
530
|
const targetValue = value !== void 0 ? value : valuesAtTime(time, baseValues, tracks)[name];
|
|
531
|
-
const errors =
|
|
531
|
+
const errors = _chunkGHCSC2WPcjs.effectValueErrors.call(void 0, description, { [name]: targetValue });
|
|
532
532
|
if (errors.length > 0) {
|
|
533
533
|
return { ok: false, message: valueErrorMessage(errors) };
|
|
534
534
|
}
|
|
@@ -705,7 +705,7 @@ async function createEffectAuthoringSession(source, options) {
|
|
|
705
705
|
},
|
|
706
706
|
async createPreview(opts) {
|
|
707
707
|
if (disposed) {
|
|
708
|
-
throw new (0,
|
|
708
|
+
throw new (0, _chunkGHCSC2WPcjs.EffectDisposedError)(effectId);
|
|
709
709
|
}
|
|
710
710
|
let renderer;
|
|
711
711
|
try {
|
|
@@ -730,7 +730,7 @@ async function createEffectAuthoringSession(source, options) {
|
|
|
730
730
|
height: renderer.height,
|
|
731
731
|
depth: renderer.depth,
|
|
732
732
|
render: async (req) => {
|
|
733
|
-
if (disposed) throw new (0,
|
|
733
|
+
if (disposed) throw new (0, _chunkGHCSC2WPcjs.EffectDisposedError)(effectId);
|
|
734
734
|
const effectiveValues = _nullishCoalesce(req.values, () => ( valuesAtTime(req.time, baseValues, tracks)));
|
|
735
735
|
const pixels = await renderer.render({
|
|
736
736
|
...req,
|
|
@@ -6,6 +6,8 @@ var _chunkWOFXZ3XUcjs = require('./chunk-WOFXZ3XU.cjs');
|
|
|
6
6
|
var EFFECT_KINDS = ["shader", "vector", "motion", "camera"];
|
|
7
7
|
var EFFECT_USES = ["filter", "transition", "generator", "overlay", "title", "motion", "interactive", "camera"];
|
|
8
8
|
var CAMERA_MOVE_ROLES = ["transition", "hold"];
|
|
9
|
+
var EFFECT_ACCEPTS = ["path"];
|
|
10
|
+
var EFFECT_PATH_FORMAT = "svg-path";
|
|
9
11
|
var EFFECT_SUBJECT_KINDS = ["image", "video", "svg", "text", "slot"];
|
|
10
12
|
var EFFECT_LAYER_ROLES = ["background", "subject", "overlay"];
|
|
11
13
|
var EFFECT_TRANSITION_MS = 800;
|
|
@@ -104,7 +106,11 @@ function countEffectTags(effects) {
|
|
|
104
106
|
for (const e of effects) for (const tag of new Set((_nullishCoalesce(e.tags, () => ( []))).map(normaliseEffectTag))) if (tag) counts.set(tag, (_nullishCoalesce(counts.get(tag), () => ( 0))) + 1);
|
|
105
107
|
return [...counts].map(([tag, count]) => ({ tag, count })).sort((a, b) => b.count - a.count || a.tag.localeCompare(b.tag));
|
|
106
108
|
}
|
|
107
|
-
var matchesFilters = (e, o) => (!o.kind || e.kind === o.kind) && (!o.category || e.category === o.category) && (!o.use || e.uses.includes(o.use)) && hasEffectTags(e, o.tags);
|
|
109
|
+
var matchesFilters = (e, o) => (!o.kind || e.kind === o.kind) && (!o.category || e.category === o.category) && (!o.use || e.uses.includes(o.use)) && hasEffectTags(e, o.tags) && (!_optionalChain([o, 'access', _2 => _2.accepts, 'optionalAccess', _3 => _3.length]) || o.accepts.every((a) => _optionalChain([e, 'access', _4 => _4.accepts, 'optionalAccess', _5 => _5.includes, 'call', _6 => _6(a)])));
|
|
110
|
+
function pathControlsOf(d) {
|
|
111
|
+
const isPath = (s) => !!s && (s.format === EFFECT_PATH_FORMAT || [..._nullishCoalesce(s.anyOf, () => ( [])), ..._nullishCoalesce(s.oneOf, () => ( []))].some((x) => isPath(x)));
|
|
112
|
+
return Object.entries(_nullishCoalesce(_optionalChain([d, 'access', _7 => _7.controls, 'optionalAccess', _8 => _8.properties]), () => ( {}))).filter(([, s]) => isPath(s)).map(([name]) => name);
|
|
113
|
+
}
|
|
108
114
|
function searchEffectSummaries(catalog, query = "", options = {}) {
|
|
109
115
|
const words = query.toLowerCase().split(/\s+/).filter(Boolean);
|
|
110
116
|
const scored = [];
|
|
@@ -228,7 +234,7 @@ async function sampleEffectCameraMove(provider, id, request) {
|
|
|
228
234
|
}
|
|
229
235
|
var cameraMoveField = (role) => role === "transition" ? "in" : "during";
|
|
230
236
|
function cameraMovesFor(catalog, role) {
|
|
231
|
-
return catalog.filter((e) => e.kind === "camera" && _optionalChain([e, 'access',
|
|
237
|
+
return catalog.filter((e) => e.kind === "camera" && _optionalChain([e, 'access', _9 => _9.camera, 'optionalAccess', _10 => _10.role]) === role);
|
|
232
238
|
}
|
|
233
239
|
function cameraMoveIdList(catalog, role) {
|
|
234
240
|
const ids = cameraMovesFor(catalog, role).map((e) => JSON.stringify(e.id));
|
|
@@ -318,11 +324,12 @@ var summaryOut = (e) => ({
|
|
|
318
324
|
category: e.category,
|
|
319
325
|
uses: e.uses,
|
|
320
326
|
...e.description ? { description: e.description } : {},
|
|
321
|
-
..._optionalChain([e, 'access',
|
|
327
|
+
..._optionalChain([e, 'access', _11 => _11.tags, 'optionalAccess', _12 => _12.length]) ? { tags: e.tags } : {},
|
|
322
328
|
transparent: e.transparent,
|
|
323
329
|
loop: e.loop,
|
|
324
330
|
...e.duration !== void 0 ? { duration: e.duration } : {},
|
|
325
|
-
...e.camera ? { camera: e.camera } : {}
|
|
331
|
+
...e.camera ? { camera: e.camera } : {},
|
|
332
|
+
..._optionalChain([e, 'access', _13 => _13.accepts, 'optionalAccess', _14 => _14.length]) ? { accepts: e.accepts } : {}
|
|
326
333
|
});
|
|
327
334
|
function createEffectToolProvider(host) {
|
|
328
335
|
let catalog = null;
|
|
@@ -330,7 +337,7 @@ function createEffectToolProvider(host) {
|
|
|
330
337
|
const notify = () => listeners.forEach((l) => l());
|
|
331
338
|
const catalogFor = (provider) => {
|
|
332
339
|
if (!provider) return null;
|
|
333
|
-
if (_optionalChain([catalog, 'optionalAccess',
|
|
340
|
+
if (_optionalChain([catalog, 'optionalAccess', _15 => _15.provider]) !== provider) {
|
|
334
341
|
const entry = { provider, effects: null };
|
|
335
342
|
catalog = entry;
|
|
336
343
|
Promise.resolve().then(() => provider.list()).then((effects) => {
|
|
@@ -366,7 +373,7 @@ function createEffectToolProvider(host) {
|
|
|
366
373
|
description: `${what}: control name \u2192 value, as listed by describe_effect (its "controls" schema: types, ranges, units and defaults). Only the given controls change; colours are "#rrggbb".${keyframes ? " A number control may instead be keyframes { keys: [{ t: ms from the start, v, ease }] }." : ""} Unknown controls and out-of-range values are refused with the valid ones.`
|
|
367
374
|
});
|
|
368
375
|
const effectIdSchema = (uses) => {
|
|
369
|
-
const effects = _optionalChain([catalogFor, 'call',
|
|
376
|
+
const effects = _optionalChain([catalogFor, 'call', _16 => _16(host.provider()), 'optionalAccess', _17 => _17.filter, 'call', _18 => _18((e) => effectOffered(e, uses))]);
|
|
370
377
|
const base = `An effect id from search_effects (use ${uses.map((u) => `"${u}"`).join(" or ")}).`;
|
|
371
378
|
return effects && effects.length && effects.length <= ENUM_LIMIT ? { type: "string", enum: effects.map((e) => e.id), description: `${base} One of: ${effects.map((e) => `${e.id} (${e.name})`).join(", ")}.` } : { type: "string", minLength: 1, description: base };
|
|
372
379
|
};
|
|
@@ -380,24 +387,35 @@ function createEffectToolProvider(host) {
|
|
|
380
387
|
{
|
|
381
388
|
name: "search_effects",
|
|
382
389
|
title: "Search effects",
|
|
383
|
-
description: `Searches the effects provider's catalog. Every word of query must appear in an effect's id, name, category, tags or description; filter by kind (shader, vector, motion, camera), category, use (${EFFECT_USES.join(", ")}) or tags (effects carrying every one). For this editor, apply_effect takes uses ${host.applyUses.join(" or ")}. Returns { provider, effects: [{ id, name, kind, category, uses, description, tags, transparent, loop, duration (ms), camera: { role, durationMs, cut } for camera moves }], total, tags: [{ tag, count }] }: tags lists the tags of every match, the most used first, so a broad search (or none) shows the tags to narrow by. A tag the catalog does not have comes back in unknownTags with the nearest ones. With no provider registered it returns available: false and the reason. Call describe_effect next for an effect's controls.`,
|
|
390
|
+
description: `Searches the effects provider's catalog. Every word of query must appear in an effect's id, name, category, tags or description; filter by kind (shader, vector, motion, camera), category, use (${EFFECT_USES.join(", ")}) or tags (effects carrying every one), and accepts ["path"] for effects that draw along a path you give. For this editor, apply_effect takes uses ${host.applyUses.join(" or ")}. Returns { provider, effects: [{ id, name, kind, category, uses, description, tags, transparent, loop, duration (ms), camera: { role, durationMs, cut } for camera moves, accepts }], total, tags: [{ tag, count }] }: tags lists the tags of every match, the most used first, so a broad search (or none) shows the tags to narrow by. A tag the catalog does not have comes back in unknownTags with the nearest ones. With no provider registered it returns available: false and the reason. Call describe_effect next for an effect's controls.`,
|
|
384
391
|
inputSchema: object({
|
|
385
392
|
query: { type: "string", description: "Words to match (default: every effect)." },
|
|
386
393
|
kind: { type: "string", enum: [...EFFECT_KINDS], description: "shader (pixels), vector (animated SVG), motion (animates vector art) or camera (moves a scene's camera; no pixels)." },
|
|
387
394
|
category: { type: "string", minLength: 1, description: `The provider's category, e.g. "gradient", "light", "transition".` },
|
|
388
395
|
use: { type: "string", enum: [...EFFECT_USES], description: "What the effect is for: filter changes a picture, transition blends two, generator draws a background, overlay draws over content, title draws a title, motion animates vector art, interactive reacts to input, camera moves a scene's camera." },
|
|
389
396
|
tags: { type: "array", items: { type: "string", minLength: 1 }, description: "Only effects carrying every one of these tags, as search_effects lists them (any case; spaces count as hyphens)." },
|
|
390
|
-
limit: { type: "integer", minimum: 1, maximum: 200, description: "Most effects to return (default 20)." }
|
|
397
|
+
limit: { type: "integer", minimum: 1, maximum: 200, description: "Most effects to return (default 20)." },
|
|
398
|
+
accepts: { type: "array", items: { type: "string", enum: [...EFFECT_ACCEPTS], description: "path: the effect has a control that draws along a path given at run time." }, description: "Only effects that take every one of these (path: effects that draw along a path you give; describe_effect names the controls in pathControls)." }
|
|
391
399
|
}),
|
|
392
400
|
annotations: { title: "Search effects", ...ANNOTATIONS.read },
|
|
393
401
|
handler: async (args) => {
|
|
394
402
|
const p = host.provider();
|
|
395
403
|
if (!p) return { available: false, reason: NO_PROVIDER, effects: [], total: 0 };
|
|
396
404
|
const tags = args.tags;
|
|
397
|
-
const
|
|
405
|
+
const accepts = args.accepts;
|
|
406
|
+
let found = (await explain(async () => p.search(_nullishCoalesce(args.query, () => ( "")), { kind: args.kind, category: args.category, use: args.use, tags, ..._optionalChain([accepts, 'optionalAccess', _19 => _19.length]) ? { accepts } : {} }))).filter((e) => hasEffectTags(e, tags));
|
|
407
|
+
if (_optionalChain([accepts, 'optionalAccess', _20 => _20.includes, 'call', _21 => _21("path")])) {
|
|
408
|
+
const kept = [];
|
|
409
|
+
for (const e of found) {
|
|
410
|
+
if (e.accepts) {
|
|
411
|
+
if (accepts.every((a) => e.accepts.includes(a))) kept.push(e);
|
|
412
|
+
} else if (kept.length < 200 && pathControlsOf(await explain(async () => p.describe(e.id))).length) kept.push(e);
|
|
413
|
+
}
|
|
414
|
+
found = kept;
|
|
415
|
+
}
|
|
398
416
|
const limit = _nullishCoalesce(args.limit, () => ( 20));
|
|
399
417
|
const out = { available: true, provider: { id: p.id, name: p.name, capabilities: p.capabilities }, effects: found.slice(0, limit).map(summaryOut), total: found.length, tags: countEffectTags(found).slice(0, TAG_LIMIT) };
|
|
400
|
-
if (_optionalChain([tags, 'optionalAccess',
|
|
418
|
+
if (_optionalChain([tags, 'optionalAccess', _22 => _22.length]) && !found.length) {
|
|
401
419
|
const known = countEffectTags(await explain(async () => p.list()));
|
|
402
420
|
const have = new Set(known.map((k) => k.tag));
|
|
403
421
|
const unknown = tags.filter((t) => !have.has(normaliseEffectTag(t)));
|
|
@@ -416,7 +434,8 @@ function createEffectToolProvider(host) {
|
|
|
416
434
|
const p = host.provider();
|
|
417
435
|
if (!p) return { available: false, reason: NO_PROVIDER };
|
|
418
436
|
const d = await explain(async () => p.describe(args.effect));
|
|
419
|
-
|
|
437
|
+
const pathControls = pathControlsOf(d);
|
|
438
|
+
return { available: true, ...summaryOut(d), controls: d.controls, defaults: d.defaults, presets: d.presets, inputs: d.inputs, appliesHere: effectOffered(d, host.applyUses), ...pathControls.length ? { pathControls } : {} };
|
|
420
439
|
}
|
|
421
440
|
},
|
|
422
441
|
{
|
|
@@ -512,16 +531,16 @@ function createEffectToolProvider(host) {
|
|
|
512
531
|
...host.description ? { description: host.description } : {},
|
|
513
532
|
...host.skill ? { skill: host.skill } : {},
|
|
514
533
|
tools: () => {
|
|
515
|
-
const extra = _nullishCoalesce(_optionalChain([host, 'access',
|
|
516
|
-
if (host.readOnlyReason()) return [...readTools(), ...extra.filter((t) => _optionalChain([t, 'access',
|
|
534
|
+
const extra = _nullishCoalesce(_optionalChain([host, 'access', _23 => _23.extraTools, 'optionalCall', _24 => _24(context)]), () => ( []));
|
|
535
|
+
if (host.readOnlyReason()) return [...readTools(), ...extra.filter((t) => _optionalChain([t, 'access', _25 => _25.annotations, 'optionalAccess', _26 => _26.readOnlyHint]))];
|
|
517
536
|
return [...readTools(), ...writeTools(), ...extra];
|
|
518
537
|
},
|
|
519
538
|
subscribe: (l) => {
|
|
520
539
|
listeners.add(l);
|
|
521
|
-
const off = _optionalChain([host, 'access',
|
|
540
|
+
const off = _optionalChain([host, 'access', _27 => _27.subscribe, 'optionalCall', _28 => _28(l)]);
|
|
522
541
|
return () => {
|
|
523
542
|
listeners.delete(l);
|
|
524
|
-
_optionalChain([off, 'optionalCall',
|
|
543
|
+
_optionalChain([off, 'optionalCall', _29 => _29()]);
|
|
525
544
|
};
|
|
526
545
|
}
|
|
527
546
|
};
|
|
@@ -570,4 +589,7 @@ function createEffectToolProvider(host) {
|
|
|
570
589
|
|
|
571
590
|
|
|
572
591
|
|
|
573
|
-
|
|
592
|
+
|
|
593
|
+
|
|
594
|
+
|
|
595
|
+
exports.EFFECT_KINDS = EFFECT_KINDS; exports.EFFECT_USES = EFFECT_USES; exports.CAMERA_MOVE_ROLES = CAMERA_MOVE_ROLES; exports.EFFECT_ACCEPTS = EFFECT_ACCEPTS; exports.EFFECT_PATH_FORMAT = EFFECT_PATH_FORMAT; exports.EFFECT_SUBJECT_KINDS = EFFECT_SUBJECT_KINDS; exports.EFFECT_LAYER_ROLES = EFFECT_LAYER_ROLES; exports.EFFECT_TRANSITION_MS = EFFECT_TRANSITION_MS; exports.EFFECT_TEMPLATE_SLOT_TYPES = EFFECT_TEMPLATE_SLOT_TYPES; exports.EFFECT_ERROR_CODES = EFFECT_ERROR_CODES; exports.EffectError = EffectError; exports.UnknownEffectError = UnknownEffectError; exports.UnknownTemplateError = UnknownTemplateError; exports.EffectControlError = EffectControlError; exports.EffectUnsupportedError = EffectUnsupportedError; exports.EffectDisposedError = EffectDisposedError; exports.NoEffectsProviderError = NoEffectsProviderError; exports.isEffectError = isEffectError; exports.effectValueErrors = effectValueErrors; exports.resolveEffectValues = resolveEffectValues; exports.effectOffered = effectOffered; exports.normaliseEffectTag = normaliseEffectTag; exports.hasEffectTags = hasEffectTags; exports.countEffectTags = countEffectTags; exports.pathControlsOf = pathControlsOf; exports.searchEffectSummaries = searchEffectSummaries; exports.createEffectPixels = createEffectPixels; exports.isEffectPixels = isEffectPixels; exports.srgbToLinear = srgbToLinear; exports.linearToSrgb = linearToSrgb; exports.encodeEffectFloat = encodeEffectFloat; exports.applyEffectMask = applyEffectMask; exports.EffectsSlot = EffectsSlot; exports.isCameraView = isCameraView; exports.sampleCameraViews = sampleCameraViews; exports.sampleEffectCameraMove = sampleEffectCameraMove; exports.cameraMoveField = cameraMoveField; exports.cameraMovesFor = cameraMovesFor; exports.cameraMoveIdList = cameraMoveIdList; exports.cameraMoveParts = cameraMoveParts; exports.CAMERA_MOVE_MAX_MS = CAMERA_MOVE_MAX_MS; exports.resolveCameraMoveSpec = resolveCameraMoveSpec; exports.cameraViewOfBox = cameraViewOfBox; exports.cameraBoxOfView = cameraBoxOfView; exports.createEffectToolProvider = createEffectToolProvider;
|
|
@@ -4,7 +4,7 @@
|
|
|
4
4
|
|
|
5
5
|
|
|
6
6
|
|
|
7
|
-
var
|
|
7
|
+
var _chunkGHCSC2WPcjs = require('./chunk-GHCSC2WP.cjs');
|
|
8
8
|
|
|
9
9
|
// src/effects/templates.ts
|
|
10
10
|
var SLOT_NAME = /^[A-Za-z][\w-]*$/;
|
|
@@ -133,7 +133,7 @@ function searchTemplateSummaries(catalog, options = {}) {
|
|
|
133
133
|
const words = (_nullishCoalesce(options.query, () => ( ""))).toLowerCase().split(/\s+/).filter(Boolean);
|
|
134
134
|
const scored = [];
|
|
135
135
|
for (const t of catalog) {
|
|
136
|
-
if (!
|
|
136
|
+
if (!_chunkGHCSC2WPcjs.hasEffectTags.call(void 0, t, options.tags)) continue;
|
|
137
137
|
let score = 0;
|
|
138
138
|
const name = t.name.toLowerCase();
|
|
139
139
|
const rest = [t.id, ..._nullishCoalesce(t.tags, () => ( [])), t.description].join(" ").toLowerCase();
|
|
@@ -200,7 +200,7 @@ function checkEffectSequence(sequence, catalog) {
|
|
|
200
200
|
const p = effectSubjectProblem(layer.subject);
|
|
201
201
|
if (p) problems.push(`${where}: ${p}.`);
|
|
202
202
|
}
|
|
203
|
-
if (layer.role !== void 0 && !
|
|
203
|
+
if (layer.role !== void 0 && !_chunkGHCSC2WPcjs.EFFECT_LAYER_ROLES.includes(layer.role)) problems.push(`${where}: role is one of ${_chunkGHCSC2WPcjs.EFFECT_LAYER_ROLES.join(", ")}.`);
|
|
204
204
|
if (layer.rect !== void 0 && !isRect(layer.rect)) problems.push(`${where}: rect must be { x, y, width, height } with width and height > 0.`);
|
|
205
205
|
for (const k of ["from", "until"]) if (layer[k] !== void 0 && !isLayerTime(layer[k])) problems.push(`${where}: ${k} is ms \u2265 0 or { shots: n } with n \u2265 1.`);
|
|
206
206
|
if (layer.opacity !== void 0 && !(typeof layer.opacity === "number" && layer.opacity >= 0 && layer.opacity <= 1)) problems.push(`${where}: opacity is 0\u20131.`);
|
|
@@ -235,8 +235,8 @@ function effectSequenceSlots(sequence) {
|
|
|
235
235
|
return [...out];
|
|
236
236
|
}
|
|
237
237
|
async function fillEffectTemplate(provider, id, values = {}, options = {}) {
|
|
238
|
-
if (!provider) throw new (0,
|
|
239
|
-
if (!provider.capabilities.supportsTemplates || !provider.describeTemplate || !provider.instantiateTemplate) throw new (0,
|
|
238
|
+
if (!provider) throw new (0, _chunkGHCSC2WPcjs.NoEffectsProviderError)();
|
|
239
|
+
if (!provider.capabilities.supportsTemplates || !provider.describeTemplate || !provider.instantiateTemplate) throw new (0, _chunkGHCSC2WPcjs.EffectUnsupportedError)(`${provider.name} has no sequence templates (capabilities.supportsTemplates is not set).`);
|
|
240
240
|
const template = await provider.describeTemplate(id);
|
|
241
241
|
const errors = checkTemplateValues(template.slots, values);
|
|
242
242
|
if (errors.length) throw new Error(`${errors.join(" ")} describe_template lists what each slot takes.`);
|
|
@@ -256,7 +256,7 @@ function effectSequenceTiming(sequence) {
|
|
|
256
256
|
const leadIns = [];
|
|
257
257
|
let t = 0;
|
|
258
258
|
sequence.shots.forEach((shot, i) => {
|
|
259
|
-
const transition = i > 0 && shot.transition ? _nullishCoalesce(shot.transition.ms, () => (
|
|
259
|
+
const transition = i > 0 && shot.transition ? _nullishCoalesce(shot.transition.ms, () => ( _chunkGHCSC2WPcjs.EFFECT_TRANSITION_MS)) : 0;
|
|
260
260
|
const move = typeof _optionalChain([shot, 'access', _4 => _4.camera, 'optionalAccess', _5 => _5.in]) === "object" && i > 0 ? _nullishCoalesce(shot.camera.in.ms, () => ( 0)) : 0;
|
|
261
261
|
const lead = Math.max(transition, move);
|
|
262
262
|
starts.push(t);
|
|
@@ -6,6 +6,8 @@ import {
|
|
|
6
6
|
var EFFECT_KINDS = ["shader", "vector", "motion", "camera"];
|
|
7
7
|
var EFFECT_USES = ["filter", "transition", "generator", "overlay", "title", "motion", "interactive", "camera"];
|
|
8
8
|
var CAMERA_MOVE_ROLES = ["transition", "hold"];
|
|
9
|
+
var EFFECT_ACCEPTS = ["path"];
|
|
10
|
+
var EFFECT_PATH_FORMAT = "svg-path";
|
|
9
11
|
var EFFECT_SUBJECT_KINDS = ["image", "video", "svg", "text", "slot"];
|
|
10
12
|
var EFFECT_LAYER_ROLES = ["background", "subject", "overlay"];
|
|
11
13
|
var EFFECT_TRANSITION_MS = 800;
|
|
@@ -104,7 +106,11 @@ function countEffectTags(effects) {
|
|
|
104
106
|
for (const e of effects) for (const tag of new Set((e.tags ?? []).map(normaliseEffectTag))) if (tag) counts.set(tag, (counts.get(tag) ?? 0) + 1);
|
|
105
107
|
return [...counts].map(([tag, count]) => ({ tag, count })).sort((a, b) => b.count - a.count || a.tag.localeCompare(b.tag));
|
|
106
108
|
}
|
|
107
|
-
var matchesFilters = (e, o) => (!o.kind || e.kind === o.kind) && (!o.category || e.category === o.category) && (!o.use || e.uses.includes(o.use)) && hasEffectTags(e, o.tags);
|
|
109
|
+
var matchesFilters = (e, o) => (!o.kind || e.kind === o.kind) && (!o.category || e.category === o.category) && (!o.use || e.uses.includes(o.use)) && hasEffectTags(e, o.tags) && (!o.accepts?.length || o.accepts.every((a) => e.accepts?.includes(a)));
|
|
110
|
+
function pathControlsOf(d) {
|
|
111
|
+
const isPath = (s) => !!s && (s.format === EFFECT_PATH_FORMAT || [...s.anyOf ?? [], ...s.oneOf ?? []].some((x) => isPath(x)));
|
|
112
|
+
return Object.entries(d.controls?.properties ?? {}).filter(([, s]) => isPath(s)).map(([name]) => name);
|
|
113
|
+
}
|
|
108
114
|
function searchEffectSummaries(catalog, query = "", options = {}) {
|
|
109
115
|
const words = query.toLowerCase().split(/\s+/).filter(Boolean);
|
|
110
116
|
const scored = [];
|
|
@@ -322,7 +328,8 @@ var summaryOut = (e) => ({
|
|
|
322
328
|
transparent: e.transparent,
|
|
323
329
|
loop: e.loop,
|
|
324
330
|
...e.duration !== void 0 ? { duration: e.duration } : {},
|
|
325
|
-
...e.camera ? { camera: e.camera } : {}
|
|
331
|
+
...e.camera ? { camera: e.camera } : {},
|
|
332
|
+
...e.accepts?.length ? { accepts: e.accepts } : {}
|
|
326
333
|
});
|
|
327
334
|
function createEffectToolProvider(host) {
|
|
328
335
|
let catalog = null;
|
|
@@ -380,21 +387,32 @@ function createEffectToolProvider(host) {
|
|
|
380
387
|
{
|
|
381
388
|
name: "search_effects",
|
|
382
389
|
title: "Search effects",
|
|
383
|
-
description: `Searches the effects provider's catalog. Every word of query must appear in an effect's id, name, category, tags or description; filter by kind (shader, vector, motion, camera), category, use (${EFFECT_USES.join(", ")}) or tags (effects carrying every one). For this editor, apply_effect takes uses ${host.applyUses.join(" or ")}. Returns { provider, effects: [{ id, name, kind, category, uses, description, tags, transparent, loop, duration (ms), camera: { role, durationMs, cut } for camera moves }], total, tags: [{ tag, count }] }: tags lists the tags of every match, the most used first, so a broad search (or none) shows the tags to narrow by. A tag the catalog does not have comes back in unknownTags with the nearest ones. With no provider registered it returns available: false and the reason. Call describe_effect next for an effect's controls.`,
|
|
390
|
+
description: `Searches the effects provider's catalog. Every word of query must appear in an effect's id, name, category, tags or description; filter by kind (shader, vector, motion, camera), category, use (${EFFECT_USES.join(", ")}) or tags (effects carrying every one), and accepts ["path"] for effects that draw along a path you give. For this editor, apply_effect takes uses ${host.applyUses.join(" or ")}. Returns { provider, effects: [{ id, name, kind, category, uses, description, tags, transparent, loop, duration (ms), camera: { role, durationMs, cut } for camera moves, accepts }], total, tags: [{ tag, count }] }: tags lists the tags of every match, the most used first, so a broad search (or none) shows the tags to narrow by. A tag the catalog does not have comes back in unknownTags with the nearest ones. With no provider registered it returns available: false and the reason. Call describe_effect next for an effect's controls.`,
|
|
384
391
|
inputSchema: object({
|
|
385
392
|
query: { type: "string", description: "Words to match (default: every effect)." },
|
|
386
393
|
kind: { type: "string", enum: [...EFFECT_KINDS], description: "shader (pixels), vector (animated SVG), motion (animates vector art) or camera (moves a scene's camera; no pixels)." },
|
|
387
394
|
category: { type: "string", minLength: 1, description: `The provider's category, e.g. "gradient", "light", "transition".` },
|
|
388
395
|
use: { type: "string", enum: [...EFFECT_USES], description: "What the effect is for: filter changes a picture, transition blends two, generator draws a background, overlay draws over content, title draws a title, motion animates vector art, interactive reacts to input, camera moves a scene's camera." },
|
|
389
396
|
tags: { type: "array", items: { type: "string", minLength: 1 }, description: "Only effects carrying every one of these tags, as search_effects lists them (any case; spaces count as hyphens)." },
|
|
390
|
-
limit: { type: "integer", minimum: 1, maximum: 200, description: "Most effects to return (default 20)." }
|
|
397
|
+
limit: { type: "integer", minimum: 1, maximum: 200, description: "Most effects to return (default 20)." },
|
|
398
|
+
accepts: { type: "array", items: { type: "string", enum: [...EFFECT_ACCEPTS], description: "path: the effect has a control that draws along a path given at run time." }, description: "Only effects that take every one of these (path: effects that draw along a path you give; describe_effect names the controls in pathControls)." }
|
|
391
399
|
}),
|
|
392
400
|
annotations: { title: "Search effects", ...ANNOTATIONS.read },
|
|
393
401
|
handler: async (args) => {
|
|
394
402
|
const p = host.provider();
|
|
395
403
|
if (!p) return { available: false, reason: NO_PROVIDER, effects: [], total: 0 };
|
|
396
404
|
const tags = args.tags;
|
|
397
|
-
const
|
|
405
|
+
const accepts = args.accepts;
|
|
406
|
+
let found = (await explain(async () => p.search(args.query ?? "", { kind: args.kind, category: args.category, use: args.use, tags, ...accepts?.length ? { accepts } : {} }))).filter((e) => hasEffectTags(e, tags));
|
|
407
|
+
if (accepts?.includes("path")) {
|
|
408
|
+
const kept = [];
|
|
409
|
+
for (const e of found) {
|
|
410
|
+
if (e.accepts) {
|
|
411
|
+
if (accepts.every((a) => e.accepts.includes(a))) kept.push(e);
|
|
412
|
+
} else if (kept.length < 200 && pathControlsOf(await explain(async () => p.describe(e.id))).length) kept.push(e);
|
|
413
|
+
}
|
|
414
|
+
found = kept;
|
|
415
|
+
}
|
|
398
416
|
const limit = args.limit ?? 20;
|
|
399
417
|
const out = { available: true, provider: { id: p.id, name: p.name, capabilities: p.capabilities }, effects: found.slice(0, limit).map(summaryOut), total: found.length, tags: countEffectTags(found).slice(0, TAG_LIMIT) };
|
|
400
418
|
if (tags?.length && !found.length) {
|
|
@@ -416,7 +434,8 @@ function createEffectToolProvider(host) {
|
|
|
416
434
|
const p = host.provider();
|
|
417
435
|
if (!p) return { available: false, reason: NO_PROVIDER };
|
|
418
436
|
const d = await explain(async () => p.describe(args.effect));
|
|
419
|
-
|
|
437
|
+
const pathControls = pathControlsOf(d);
|
|
438
|
+
return { available: true, ...summaryOut(d), controls: d.controls, defaults: d.defaults, presets: d.presets, inputs: d.inputs, appliesHere: effectOffered(d, host.applyUses), ...pathControls.length ? { pathControls } : {} };
|
|
420
439
|
}
|
|
421
440
|
},
|
|
422
441
|
{
|
|
@@ -531,6 +550,8 @@ export {
|
|
|
531
550
|
EFFECT_KINDS,
|
|
532
551
|
EFFECT_USES,
|
|
533
552
|
CAMERA_MOVE_ROLES,
|
|
553
|
+
EFFECT_ACCEPTS,
|
|
554
|
+
EFFECT_PATH_FORMAT,
|
|
534
555
|
EFFECT_SUBJECT_KINDS,
|
|
535
556
|
EFFECT_LAYER_ROLES,
|
|
536
557
|
EFFECT_TRANSITION_MS,
|
|
@@ -550,6 +571,7 @@ export {
|
|
|
550
571
|
normaliseEffectTag,
|
|
551
572
|
hasEffectTags,
|
|
552
573
|
countEffectTags,
|
|
574
|
+
pathControlsOf,
|
|
553
575
|
searchEffectSummaries,
|
|
554
576
|
createEffectPixels,
|
|
555
577
|
isEffectPixels,
|