@pixerate/editor 0.241.0 → 0.243.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 +195 -146
- package/dist/animation-editor/index.js +63 -14
- package/dist/{brushstrokeRender-MOYGEXOC.js → brushstrokeRender-274HGNZR.js} +3 -3
- package/dist/{brushstrokeRender-UKADOV5D.cjs → brushstrokeRender-C7RJZPMC.cjs} +5 -5
- package/dist/canvas/index.cjs +15 -15
- package/dist/canvas/index.js +2 -2
- package/dist/{chunk-KCMV2534.cjs → chunk-3WOZEBXZ.cjs} +2 -2
- package/dist/{chunk-ZDPLZDTT.js → chunk-4ZHXLFXS.js} +1 -1
- package/dist/{chunk-5BR7LC24.cjs → chunk-5FZS7J7C.cjs} +50 -4
- package/dist/{chunk-ILM6GYAX.js → chunk-6L3WWIDW.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-UDBDYZET.js → chunk-H4Z72Z4Z.js} +1 -1
- package/dist/{chunk-XBZK47LV.js → chunk-HQZHOPNE.js} +49 -3
- package/dist/{chunk-QP2B3MID.js → chunk-IXI5KATU.js} +1 -1
- package/dist/{chunk-JWCKJNDR.cjs → chunk-K5DS2JTA.cjs} +6 -6
- package/dist/{chunk-HGGJ36HR.cjs → chunk-KK2FWMA2.cjs} +1 -1
- package/dist/{chunk-WDDUPWVG.js → chunk-L3JFOTBQ.js} +1 -1
- package/dist/{chunk-SIK3LT6M.js → chunk-OFH6Q67U.js} +28 -6
- package/dist/{chunk-AFLMXITL.cjs → chunk-OONFIFEU.cjs} +23 -23
- package/dist/{chunk-HW72ABGR.cjs → chunk-R2FVAKFH.cjs} +86 -86
- package/dist/{chunk-G3RUKJ5C.js → chunk-RXZL4NG5.js} +1 -1
- package/dist/{chunk-NE6OOKWJ.js → chunk-SRXWDOUI.js} +1 -1
- package/dist/{chunk-7F4G74UQ.cjs → chunk-UV7OOZJQ.cjs} +35 -35
- 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 +788 -788
- package/dist/image-editor/index.js +5 -5
- package/dist/paint-editor/brushes.d.cts +18 -2
- package/dist/paint-editor/brushes.d.ts +18 -2
- package/dist/paint-editor/controller.d.cts +28 -0
- package/dist/paint-editor/controller.d.ts +28 -0
- package/dist/paint-editor/document.d.cts +3 -0
- package/dist/paint-editor/document.d.ts +3 -0
- package/dist/paint-editor/index.cjs +1017 -499
- package/dist/paint-editor/index.js +562 -44
- package/dist/paint-editor/light.d.cts +74 -0
- package/dist/paint-editor/light.d.ts +74 -0
- package/dist/paint-editor/stroke.d.cts +14 -0
- package/dist/paint-editor/stroke.d.ts +14 -0
- package/dist/paint-editor/thick-tools.d.cts +2 -2
- package/dist/paint-editor/thick-tools.d.ts +2 -2
- package/dist/paint-editor/thick.d.cts +5 -1
- package/dist/paint-editor/thick.d.ts +5 -1
- package/dist/paint-editor/ui.d.cts +2 -0
- package/dist/paint-editor/ui.d.ts +2 -0
- package/dist/vector/index.cjs +968 -968
- package/dist/vector/index.js +5 -5
- 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-paint-editor/SKILL.md +9 -9
- package/skills/pixerate-paint-editor/references/canvas-hosted.md +8 -0
- package/skills/pixerate-paint-editor/references/light-paint.md +31 -0
- package/skills/pixerate-paint-oil-impasto/SKILL.md +1 -0
- package/skills/pixerate-video-editor/SKILL.md +9 -2
|
@@ -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;
|
|
@@ -1218,7 +1218,7 @@ var BristleStroke = class extends MarkStroke {
|
|
|
1218
1218
|
|
|
1219
1219
|
// src/paint-editor/brushes.ts
|
|
1220
1220
|
var PAINT_BRUSH_ENGINES = ["dab", "eraser", "mixer", "blender", "smudge", "wet", "impasto", "particle", "bristle", "sticker"];
|
|
1221
|
-
var PAINT_BRUSH_CATEGORIES = ["pencil", "pastel", "charcoal", "airbrush", "marker", "ink", "paint", "eraser", "blender", "smudge", "mixer", "watercolor", "wet-tool", "oil", "impasto", "procedural", "bristle", "sticker", "custom"];
|
|
1221
|
+
var PAINT_BRUSH_CATEGORIES = ["pencil", "pastel", "charcoal", "airbrush", "marker", "ink", "paint", "eraser", "blender", "smudge", "mixer", "watercolor", "wet-tool", "oil", "impasto", "procedural", "bristle", "sticker", "light", "custom"];
|
|
1222
1222
|
var WET_TOOLS = ["paint", "water", "dry", "blow", "blend", "smudge", "blot"];
|
|
1223
1223
|
var WET_MEDIA = ["watercolor", "gouache", "oil"];
|
|
1224
1224
|
var DEFAULT_WET_BRUSH = { tool: "paint", water: 0.5, pigment: 0.5, body: 0, strength: 0.5, rewet: 0, medium: "watercolor", load: 1, thinner: 0 };
|
|
@@ -1236,7 +1236,11 @@ function normalizeWetBrush(raw = {}, base = DEFAULT_WET_BRUSH) {
|
|
|
1236
1236
|
};
|
|
1237
1237
|
}
|
|
1238
1238
|
var IMPASTO_TOOLS = ["brush", "tube", "knife", "roller", "gloop"];
|
|
1239
|
-
var DEFAULT_IMPASTO = { tool: "brush", thickness: 0.6, gloss: 0.4, metallic: 0, load: 0.9, thinner: 0, drag: 0.4 };
|
|
1239
|
+
var DEFAULT_IMPASTO = { tool: "brush", thickness: 0.6, gloss: 0.4, metallic: 0, load: 0.9, thinner: 0, drag: 0.4, rim: 0, clarity: 0 };
|
|
1240
|
+
var DEFAULT_LIGHT = { amount: 0, pressure: false };
|
|
1241
|
+
function normalizeLight(raw = {}, base = DEFAULT_LIGHT) {
|
|
1242
|
+
return { amount: unit4(raw.amount, base.amount), pressure: typeof raw.pressure === "boolean" ? raw.pressure : base.pressure };
|
|
1243
|
+
}
|
|
1240
1244
|
function normalizeImpasto(raw = {}, base = DEFAULT_IMPASTO) {
|
|
1241
1245
|
return {
|
|
1242
1246
|
tool: IMPASTO_TOOLS.includes(raw.tool) ? raw.tool : base.tool,
|
|
@@ -1245,7 +1249,9 @@ function normalizeImpasto(raw = {}, base = DEFAULT_IMPASTO) {
|
|
|
1245
1249
|
metallic: unit4(raw.metallic, base.metallic),
|
|
1246
1250
|
load: unit4(raw.load, base.load),
|
|
1247
1251
|
thinner: unit4(raw.thinner, base.thinner),
|
|
1248
|
-
drag: unit4(raw.drag, base.drag)
|
|
1252
|
+
drag: unit4(raw.drag, base.drag),
|
|
1253
|
+
rim: unit4(raw.rim, base.rim ?? 0),
|
|
1254
|
+
clarity: unit4(raw.clarity, base.clarity ?? 0)
|
|
1249
1255
|
};
|
|
1250
1256
|
}
|
|
1251
1257
|
var MULTI_COLOR_MODES = ["off", "tip", "dab"];
|
|
@@ -1403,6 +1409,7 @@ function normalizePaintBrush(raw = {}, base) {
|
|
|
1403
1409
|
particle: normalizeParticle(raw.particle ?? {}, b.particle ?? DEFAULT_PARTICLE),
|
|
1404
1410
|
bristle: normalizeBristle(raw.bristle ?? {}, b.bristle ?? DEFAULT_BRISTLE),
|
|
1405
1411
|
sticker: normalizeStickerSettings(raw.sticker ?? {}, b.sticker ?? DEFAULT_STICKER),
|
|
1412
|
+
light: normalizeLight(raw.light ?? {}, b.light ?? DEFAULT_LIGHT),
|
|
1406
1413
|
paint,
|
|
1407
1414
|
...raw.builtin ? { builtin: true } : {}
|
|
1408
1415
|
};
|
|
@@ -1427,6 +1434,7 @@ var BASE_BRUSH = {
|
|
|
1427
1434
|
particle: { ...DEFAULT_PARTICLE },
|
|
1428
1435
|
bristle: { ...DEFAULT_BRISTLE },
|
|
1429
1436
|
sticker: { ...DEFAULT_STICKER, stamps: [...DEFAULT_STICKER.stamps] },
|
|
1437
|
+
light: { ...DEFAULT_LIGHT },
|
|
1430
1438
|
paint: null
|
|
1431
1439
|
};
|
|
1432
1440
|
var builtin = (init) => ({ ...normalizePaintBrush(init), builtin: true });
|
|
@@ -1848,6 +1856,42 @@ var BUILTIN_BRUSHES = [
|
|
|
1848
1856
|
grain: 0.6,
|
|
1849
1857
|
bristle: { count: 48, stiffness: 0.35, splay: 0.7, thickness: 0.05, load: 0.15, jitter: 0.8 }
|
|
1850
1858
|
}),
|
|
1859
|
+
// Light paint: brushes that lay light with their colour (glow and spill; see light.ts).
|
|
1860
|
+
builtin({
|
|
1861
|
+
id: "glow-gel",
|
|
1862
|
+
name: "Glow gel",
|
|
1863
|
+
category: "light",
|
|
1864
|
+
engine: "impasto",
|
|
1865
|
+
tip: { size: 16, hardness: 0.9, spacing: 0.05, opacity: 1, flow: 1, smoothing: 0.5, pressureSize: true, pressureOpacity: false },
|
|
1866
|
+
dynamics: { minSize: 0.6 },
|
|
1867
|
+
impasto: { tool: "gloop", thickness: 1, gloss: 0.95, load: 1, drag: 0, rim: 0.8, clarity: 0.5 },
|
|
1868
|
+
light: { amount: 0.85 }
|
|
1869
|
+
}),
|
|
1870
|
+
builtin({
|
|
1871
|
+
id: "neon-marker",
|
|
1872
|
+
name: "Neon marker",
|
|
1873
|
+
category: "light",
|
|
1874
|
+
tip: { size: 10, hardness: 0.9, spacing: 0.04, opacity: 1, flow: 1, smoothing: 0.45, pressureSize: false, pressureOpacity: false },
|
|
1875
|
+
light: { amount: 1 }
|
|
1876
|
+
}),
|
|
1877
|
+
builtin({
|
|
1878
|
+
id: "light-airbrush",
|
|
1879
|
+
name: "Light airbrush",
|
|
1880
|
+
category: "light",
|
|
1881
|
+
tip: { size: 90, hardness: 0, spacing: 0.04, opacity: 1, flow: 0.06, smoothing: 0.3, pressureSize: false, pressureOpacity: false },
|
|
1882
|
+
pressureSmoothing: 0.3,
|
|
1883
|
+
dynamics: { pressureFlow: 1 },
|
|
1884
|
+
light: { amount: 0.7, pressure: true }
|
|
1885
|
+
}),
|
|
1886
|
+
builtin({
|
|
1887
|
+
id: "spark-trail",
|
|
1888
|
+
name: "Spark trail",
|
|
1889
|
+
category: "light",
|
|
1890
|
+
engine: "particle",
|
|
1891
|
+
tip: { size: 50, hardness: 0.7, opacity: 1, flow: 1, smoothing: 0.1, pressureSize: true, pressureOpacity: false },
|
|
1892
|
+
particle: { kind: "flow", render: "dot", count: 8, size: 0.06, spread: 0.7, speed: 0.9, life: 0.3 },
|
|
1893
|
+
light: { amount: 1 }
|
|
1894
|
+
}),
|
|
1851
1895
|
builtin({
|
|
1852
1896
|
id: "sticker-stars",
|
|
1853
1897
|
name: "Star stickers",
|
|
@@ -2024,6 +2068,8 @@ export {
|
|
|
2024
2068
|
normalizeWetBrush,
|
|
2025
2069
|
IMPASTO_TOOLS,
|
|
2026
2070
|
DEFAULT_IMPASTO,
|
|
2071
|
+
DEFAULT_LIGHT,
|
|
2072
|
+
normalizeLight,
|
|
2027
2073
|
normalizeImpasto,
|
|
2028
2074
|
MULTI_COLOR_MODES,
|
|
2029
2075
|
DEFAULT_MULTI_COLOR,
|
|
@@ -74,7 +74,7 @@ function profileAlongPolyline(points, values, count = 32) {
|
|
|
74
74
|
var round3 = (v) => Math.round(v * 1e3) / 1e3;
|
|
75
75
|
var loading = null;
|
|
76
76
|
function loadCanvasBrushstrokeRenderer() {
|
|
77
|
-
loading ??= import("./brushstrokeRender-
|
|
77
|
+
loading ??= import("./brushstrokeRender-274HGNZR.js").catch((error) => {
|
|
78
78
|
loading = null;
|
|
79
79
|
throw error;
|
|
80
80
|
});
|
|
@@ -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);
|
|
@@ -74,7 +74,7 @@ function profileAlongPolyline(points, values, count = 32) {
|
|
|
74
74
|
var round3 = (v) => Math.round(v * 1e3) / 1e3;
|
|
75
75
|
var loading = null;
|
|
76
76
|
function loadCanvasBrushstrokeRenderer() {
|
|
77
|
-
loading ??= Promise.resolve().then(() => _interopRequireWildcard(require("./brushstrokeRender-
|
|
77
|
+
loading ??= Promise.resolve().then(() => _interopRequireWildcard(require("./brushstrokeRender-C7RJZPMC.cjs"))).catch((error) => {
|
|
78
78
|
loading = null;
|
|
79
79
|
throw error;
|
|
80
80
|
});
|
|
@@ -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,
|