@pixerate/editor 0.124.0 → 0.124.1
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/{chunk-IWSMDQE5.cjs → chunk-4QQZV5Y5.cjs} +18 -29
- package/dist/chunk-4QQZV5Y5.cjs.map +1 -0
- package/dist/{chunk-IOXKXQNW.cjs → chunk-CN4PJHUG.cjs} +59 -3
- package/dist/chunk-CN4PJHUG.cjs.map +1 -0
- package/dist/{chunk-TK53AB4Q.cjs → chunk-FHYDQOPC.cjs} +3 -3
- package/dist/{chunk-TK53AB4Q.cjs.map → chunk-FHYDQOPC.cjs.map} +1 -1
- package/dist/{chunk-VFAKN46Z.js → chunk-JRXJTPES.js} +2 -2
- package/dist/{chunk-YVVSTD3B.js → chunk-ORHMVUR6.js} +8 -19
- package/dist/chunk-ORHMVUR6.js.map +1 -0
- package/dist/{chunk-TGKT5QAB.js → chunk-QEG3GRN2.js} +58 -2
- package/dist/chunk-QEG3GRN2.js.map +1 -0
- package/dist/effects/index.cjs +51 -34
- package/dist/effects/index.cjs.map +1 -1
- package/dist/effects/index.d.cts +35 -3
- package/dist/effects/index.d.ts +35 -3
- package/dist/effects/index.js +20 -3
- package/dist/effects/index.js.map +1 -1
- package/dist/{export-8RnlngAv.d.ts → export-BGtXgMJ3.d.ts} +3 -1
- package/dist/{export-DM3h7QEI.d.ts → export-BdfY_lH0.d.ts} +4 -2
- package/dist/{export-BzSRXyH6.d.cts → export-CbpjwxaI.d.cts} +4 -2
- package/dist/{export-TqXMOlDt.d.cts → export-WmYoWmLM.d.cts} +3 -1
- package/dist/image-editor/index.cjs +110 -121
- package/dist/image-editor/index.cjs.map +1 -1
- package/dist/image-editor/index.d.cts +2 -2
- package/dist/image-editor/index.d.ts +2 -2
- package/dist/image-editor/index.js +7 -18
- package/dist/image-editor/index.js.map +1 -1
- package/dist/paint-editor/index.cjs +3 -3
- package/dist/paint-editor/index.d.cts +2 -2
- package/dist/paint-editor/index.d.ts +2 -2
- package/dist/paint-editor/index.js +2 -2
- package/dist/video-editor/index.cjs +270 -270
- package/dist/video-editor/index.d.cts +2 -2
- package/dist/video-editor/index.d.ts +2 -2
- package/dist/video-editor/index.js +3 -3
- package/package.json +1 -1
- package/dist/chunk-IOXKXQNW.cjs.map +0 -1
- package/dist/chunk-IWSMDQE5.cjs.map +0 -1
- package/dist/chunk-TGKT5QAB.js.map +0 -1
- package/dist/chunk-YVVSTD3B.js.map +0 -1
- /package/dist/{chunk-VFAKN46Z.js.map → chunk-JRXJTPES.js.map} +0 -0
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
"use strict";Object.defineProperty(exports, "__esModule", {value: true}); function _nullishCoalesce(lhs, rhsFn) { if (lhs != null) { return lhs; } else { return rhsFn(); } } var _class;
|
|
1
|
+
"use strict";Object.defineProperty(exports, "__esModule", {value: true}); function _nullishCoalesce(lhs, rhsFn) { if (lhs != null) { return lhs; } else { return rhsFn(); } } function _optionalChain(ops) { let lastAccessLHS = undefined; let value = ops[0]; let i = 1; while (i < ops.length) { const op = ops[i]; const fn = ops[i + 1]; i += 2; if ((op === 'optionalAccess' || op === 'optionalCall') && value == null) { return undefined; } if (op === 'access' || op === 'optionalAccess') { lastAccessLHS = value; value = fn(value); } else if (op === 'call' || op === 'optionalCall') { value = fn((...args) => value.call(lastAccessLHS, ...args)); lastAccessLHS = undefined; } } return value; } var _class; var _class2;
|
|
2
2
|
|
|
3
3
|
var _chunkRIWPYSKUcjs = require('./chunk-RIWPYSKU.cjs');
|
|
4
4
|
|
|
@@ -155,6 +155,62 @@ var EffectsSlot = (_class = class {constructor() { _class.prototype.__init.call(
|
|
|
155
155
|
}
|
|
156
156
|
}, _class);
|
|
157
157
|
|
|
158
|
+
// src/effects/pool.ts
|
|
159
|
+
var EffectRendererPool = (_class2 = class {
|
|
160
|
+
__init3() {this.entries = /* @__PURE__ */ new Map()}
|
|
161
|
+
|
|
162
|
+
constructor(options = {}) {;_class2.prototype.__init3.call(this);
|
|
163
|
+
this.maxRenderers = Math.max(1, _nullishCoalesce(options.maxRenderers, () => ( 4)));
|
|
164
|
+
}
|
|
165
|
+
/** Live renderers now. */
|
|
166
|
+
get size() {
|
|
167
|
+
return this.entries.size;
|
|
168
|
+
}
|
|
169
|
+
/** Renders `request` with the provider's renderer for this effect, size and depth (made on first use). */
|
|
170
|
+
render(provider, id, size, request) {
|
|
171
|
+
const depth = _nullishCoalesce(size.depth, () => ( 8));
|
|
172
|
+
const key = `${provider.id}:${id}@${size.width}x${size.height}x${depth}`;
|
|
173
|
+
let entry = this.entries.get(key);
|
|
174
|
+
if (entry) {
|
|
175
|
+
this.entries.delete(key);
|
|
176
|
+
this.entries.set(key, entry);
|
|
177
|
+
} else {
|
|
178
|
+
const renderer = Promise.resolve().then(() => provider.createRenderer(id, { width: size.width, height: size.height, depth }));
|
|
179
|
+
renderer.catch(() => {
|
|
180
|
+
if (_optionalChain([this, 'access', _ => _.entries, 'access', _2 => _2.get, 'call', _3 => _3(key), 'optionalAccess', _4 => _4.renderer]) === renderer) this.entries.delete(key);
|
|
181
|
+
});
|
|
182
|
+
entry = { renderer, queue: Promise.resolve() };
|
|
183
|
+
this.entries.set(key, entry);
|
|
184
|
+
this.evict();
|
|
185
|
+
}
|
|
186
|
+
const e = entry;
|
|
187
|
+
const next = e.queue.then(() => e.renderer).then((r) => r.render(request));
|
|
188
|
+
e.queue = next.catch(() => void 0);
|
|
189
|
+
return next;
|
|
190
|
+
}
|
|
191
|
+
/** Disposes the least recently used renderers over the limit, after their queued renders. */
|
|
192
|
+
evict() {
|
|
193
|
+
while (this.entries.size > this.maxRenderers) {
|
|
194
|
+
const [key, entry] = this.entries.entries().next().value;
|
|
195
|
+
this.entries.delete(key);
|
|
196
|
+
dispose(entry);
|
|
197
|
+
}
|
|
198
|
+
}
|
|
199
|
+
/** Disposes every renderer (after its queued renders). */
|
|
200
|
+
clear() {
|
|
201
|
+
for (const entry of this.entries.values()) dispose(entry);
|
|
202
|
+
this.entries.clear();
|
|
203
|
+
}
|
|
204
|
+
}, _class2);
|
|
205
|
+
function dispose(entry) {
|
|
206
|
+
entry.queue.then(() => entry.renderer).then(
|
|
207
|
+
(r) => r.dispose(),
|
|
208
|
+
() => {
|
|
209
|
+
}
|
|
210
|
+
);
|
|
211
|
+
}
|
|
212
|
+
|
|
213
|
+
|
|
158
214
|
|
|
159
215
|
|
|
160
216
|
|
|
@@ -174,5 +230,5 @@ var EffectsSlot = (_class = class {constructor() { _class.prototype.__init.call(
|
|
|
174
230
|
|
|
175
231
|
|
|
176
232
|
|
|
177
|
-
exports.EFFECT_ERROR_CODES = EFFECT_ERROR_CODES; exports.EffectError = EffectError; exports.UnknownEffectError = UnknownEffectError; exports.EffectControlError = EffectControlError; exports.EffectUnsupportedError = EffectUnsupportedError; exports.EffectDisposedError = EffectDisposedError; exports.NoEffectsProviderError = NoEffectsProviderError; exports.isEffectError = isEffectError; exports.effectValueErrors = effectValueErrors; exports.resolveEffectValues = resolveEffectValues; exports.searchEffectSummaries = searchEffectSummaries; exports.createEffectPixels = createEffectPixels; exports.isEffectPixels = isEffectPixels; exports.srgbToLinear = srgbToLinear; exports.linearToSrgb = linearToSrgb; exports.encodeEffectFloat = encodeEffectFloat; exports.applyEffectMask = applyEffectMask; exports.EffectsSlot = EffectsSlot;
|
|
178
|
-
//# sourceMappingURL=chunk-
|
|
233
|
+
exports.EFFECT_ERROR_CODES = EFFECT_ERROR_CODES; exports.EffectError = EffectError; exports.UnknownEffectError = UnknownEffectError; exports.EffectControlError = EffectControlError; exports.EffectUnsupportedError = EffectUnsupportedError; exports.EffectDisposedError = EffectDisposedError; exports.NoEffectsProviderError = NoEffectsProviderError; exports.isEffectError = isEffectError; exports.effectValueErrors = effectValueErrors; exports.resolveEffectValues = resolveEffectValues; 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.EffectRendererPool = EffectRendererPool;
|
|
234
|
+
//# sourceMappingURL=chunk-CN4PJHUG.cjs.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"sources":["/home/runner/work/editor/editor/packages/core/dist/chunk-CN4PJHUG.cjs","../src/effects/errors.ts","../src/effects/helpers.ts","../src/effects/pool.ts"],"names":[],"mappings":"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","file":"/home/runner/work/editor/editor/packages/core/dist/chunk-CN4PJHUG.cjs","sourcesContent":[null,"import type { SchemaValidationError } from '../tools/schema.js';\n\nexport type EffectErrorCode = 'unknown_effect' | 'invalid_values' | 'unsupported' | 'disposed' | 'no_provider';\n\nexport const EFFECT_ERROR_CODES: readonly EffectErrorCode[] = ['unknown_effect', 'invalid_values', 'unsupported', 'disposed', 'no_provider'];\n\n/**\n * Base of every effects error. Check `code` (or use `isEffectError`) rather than `instanceof`\n * across packages, since a provider may be bundled with its own copy of this class.\n */\nexport class EffectError extends Error {\n\treadonly code: EffectErrorCode;\n\treadonly effectId?: string;\n\n\tconstructor(code: EffectErrorCode, message: string, effectId?: string) {\n\t\tsuper(message);\n\t\tthis.name = 'EffectError';\n\t\tthis.code = code;\n\t\tthis.effectId = effectId;\n\t}\n}\n\n/** No effect has this id. */\nexport class UnknownEffectError extends EffectError {\n\tconstructor(effectId: string, providerName?: string) {\n\t\tsuper(\n\t\t\t'unknown_effect',\n\t\t\t`Unknown effect \"${effectId}\"${providerName ? ` in ${providerName}` : ''}. Call search_effects or list() for valid ids.`,\n\t\t\teffectId\n\t\t);\n\t\tthis.name = 'UnknownEffectError';\n\t}\n}\n\n/** Control values do not match the effect's control schema (unknown control, wrong type, out of range). */\nexport class EffectControlError extends EffectError {\n\treadonly details: readonly SchemaValidationError[];\n\n\tconstructor(effectId: string, details: readonly SchemaValidationError[]) {\n\t\tconst lines = details.map((d) => `- ${d.path || '/'}: ${d.message}`).join('\\n');\n\t\tsuper('invalid_values', `Invalid values for effect \"${effectId}\":\\n${lines}`, effectId);\n\t\tthis.name = 'EffectControlError';\n\t\tthis.details = details;\n\t}\n}\n\n/** The provider cannot do this (a vector effect asked for pixels, a size above `maxSize`, a missing input...). */\nexport class EffectUnsupportedError extends EffectError {\n\tconstructor(message: string, effectId?: string) {\n\t\tsuper('unsupported', message, effectId);\n\t\tthis.name = 'EffectUnsupportedError';\n\t}\n}\n\n/** The renderer was disposed. Create a new one. */\nexport class EffectDisposedError extends EffectError {\n\tconstructor(effectId: string) {\n\t\tsuper('disposed', `The renderer for \"${effectId}\" was disposed. Create a new renderer.`, effectId);\n\t\tthis.name = 'EffectDisposedError';\n\t}\n}\n\n/** No effects provider is registered with this editor. */\nexport class NoEffectsProviderError extends EffectError {\n\tconstructor() {\n\t\tsuper('no_provider', 'No effects provider is registered with this editor. The host must call setEffectsProvider(provider) first.');\n\t\tthis.name = 'NoEffectsProviderError';\n\t}\n}\n\n/** True for any effects error, including one thrown by another copy of this module. */\nexport function isEffectError(error: unknown): error is EffectError {\n\tif (error instanceof EffectError) return true;\n\tconst code = error instanceof Error ? (error as unknown as { code?: unknown }).code : undefined;\n\treturn typeof code === 'string' && EFFECT_ERROR_CODES.includes(code as EffectErrorCode);\n}\n","/**\n * Helpers for providers and editors: value validation, catalog search, pixel buffers, masking,\n * and a provider slot editors hold.\n */\n\nimport { validateJsonSchema, type SchemaValidationError } from '../tools/schema.js';\nimport { EffectControlError, NoEffectsProviderError } from './errors.js';\nimport type {\n\tEffectDescription,\n\tEffectImageSource,\n\tEffectPixels,\n\tEffectProvider,\n\tEffectSearchOptions,\n\tEffectSummary,\n\tEffectValues\n} from './types.js';\n\n// ---------------------------------------------------------------------------\n// Values\n// ---------------------------------------------------------------------------\n\n/** Problems with `values` against an effect's control schema (unknown controls, types, ranges). Partial values are fine. */\nexport function effectValueErrors(description: Pick<EffectDescription, 'controls'>, values: unknown): SchemaValidationError[] {\n\tif (values === undefined) return [];\n\treturn validateJsonSchema(values, { ...description.controls, required: [] });\n}\n\n/**\n * `values` over the effect's defaults, validated against its control schema. Throws\n * `EffectControlError` naming every bad control. Providers call this in `render`; editors call\n * it before saving values.\n */\nexport function resolveEffectValues(description: Pick<EffectDescription, 'id' | 'controls' | 'defaults'>, values?: EffectValues): Record<string, EffectValues[string]> {\n\tconst errors = effectValueErrors(description, values);\n\tif (errors.length) throw new EffectControlError(description.id, errors);\n\treturn { ...description.defaults, ...(values ?? {}) };\n}\n\n// ---------------------------------------------------------------------------\n// Search\n// ---------------------------------------------------------------------------\n\nconst matchesFilters = (e: EffectSummary, o: EffectSearchOptions) =>\n\t(!o.kind || e.kind === o.kind) && (!o.category || e.category === o.category) && (!o.use || e.uses.includes(o.use));\n\n/**\n * Filters a catalog and ranks it against `query` (every word must appear in the id, name,\n * category, tags or description; name matches rank first). An empty query keeps catalog order.\n * Providers can implement `list` and `search` with it.\n */\nexport function searchEffectSummaries<T extends EffectSummary>(catalog: readonly T[], query = '', options: EffectSearchOptions = {}): T[] {\n\tconst words = query.toLowerCase().split(/\\s+/).filter(Boolean);\n\tconst scored: { e: T; score: number; i: number }[] = [];\n\tcatalog.forEach((e, i) => {\n\t\tif (!matchesFilters(e, options)) return;\n\t\tconst name = e.name.toLowerCase();\n\t\tconst hay = [e.id, e.name, e.category, ...(e.tags ?? []), e.description ?? ''].join(' ').toLowerCase();\n\t\tlet score = 0;\n\t\tfor (const w of words) {\n\t\t\tif (!hay.includes(w)) return;\n\t\t\tscore += name.startsWith(w) ? 3 : name.includes(w) ? 2 : 1;\n\t\t}\n\t\tscored.push({ e, score, i });\n\t});\n\tscored.sort((a, b) => b.score - a.score || a.i - b.i);\n\tconst out = scored.map((s) => s.e);\n\treturn options.limit !== undefined ? out.slice(0, Math.max(0, options.limit)) : out;\n}\n\n// ---------------------------------------------------------------------------\n// Pixels\n// ---------------------------------------------------------------------------\n\n/** A transparent picture, with float samples when `float` is set. */\nexport function createEffectPixels(width: number, height: number, float = false): EffectPixels {\n\tconst n = Math.max(0, width * height * 4);\n\treturn { width, height, data: new Uint8ClampedArray(n), ...(float ? { float: new Float32Array(n) } : {}) };\n}\n\n/** True for an `EffectPixels` (rather than a host image object). */\nexport function isEffectPixels(source: EffectImageSource | unknown): source is EffectPixels {\n\treturn (\n\t\t!!source &&\n\t\ttypeof source === 'object' &&\n\t\t(source as EffectPixels).data instanceof Uint8ClampedArray &&\n\t\ttypeof (source as EffectPixels).width === 'number' &&\n\t\ttypeof (source as EffectPixels).height === 'number'\n\t);\n}\n\n/** sRGB-encoded 0–1 to linear light. */\nexport const srgbToLinear = (v: number) => (v <= 0.04045 ? v / 12.92 : Math.pow((v + 0.055) / 1.055, 2.4));\n/** Linear light to sRGB-encoded (clamped below at 0; may exceed 1). */\nexport const linearToSrgb = (v: number) => (v > 0 ? (v <= 0.0031308 ? v * 12.92 : 1.055 * Math.pow(v, 1 / 2.4) - 0.055) : 0);\n\n/** Fills `pixels.data` from `pixels.float` (linear float to clamped 8-bit sRGB). */\nexport function encodeEffectFloat(pixels: EffectPixels): EffectPixels {\n\tconst f = pixels.float;\n\tif (!f) return pixels;\n\tconst d = pixels.data;\n\tfor (let i = 0; i < d.length; i += 4) {\n\t\td[i] = Math.round(linearToSrgb(f[i]) * 255);\n\t\td[i + 1] = Math.round(linearToSrgb(f[i + 1]) * 255);\n\t\td[i + 2] = Math.round(linearToSrgb(f[i + 2]) * 255);\n\t\td[i + 3] = Math.round(Math.min(1, Math.max(0, f[i + 3])) * 255);\n\t}\n\treturn pixels;\n}\n\n/**\n * Blends an effect `result` back over its `source` by `mask` coverage (alpha channel), in place\n * on `result`: where the mask is 0 the source shows, where it is 255 the result. Without a\n * source (generators), the result's alpha is multiplied by coverage. Editors use it when the\n * provider does not `supportsMask`. All three must have the same size.\n */\nexport function applyEffectMask(result: EffectPixels, mask: EffectPixels, source?: EffectPixels): EffectPixels {\n\tconst { data } = result;\n\tconst m = mask.data;\n\tfor (let i = 0; i < data.length; i += 4) {\n\t\tconst k = m[i + 3] / 255;\n\t\tif (k >= 1) continue;\n\t\tif (source) {\n\t\t\tfor (let c = 0; c < 4; c++) data[i + c] = source.data[i + c] + (data[i + c] - source.data[i + c]) * k;\n\t\t\tif (result.float && source.float) {\n\t\t\t\tfor (let c = 0; c < 4; c++) result.float[i + c] = source.float[i + c] + (result.float[i + c] - source.float[i + c]) * k;\n\t\t\t}\n\t\t} else {\n\t\t\tdata[i + 3] = data[i + 3] * k;\n\t\t\tif (result.float) result.float[i + 3] *= k;\n\t\t}\n\t}\n\treturn result;\n}\n\n// ---------------------------------------------------------------------------\n// The slot an editor keeps\n// ---------------------------------------------------------------------------\n\n/**\n * Holds an editor's effects provider. Controllers expose it as `effectsProvider`,\n * `setEffectsProvider(provider | null)` and `useEffects(provider)`; listeners hear every change.\n */\nexport class EffectsSlot {\n\tprivate current: EffectProvider | null = null;\n\tprivate readonly listeners = new Set<(provider: EffectProvider | null) => void>();\n\n\tget provider(): EffectProvider | null {\n\t\treturn this.current;\n\t}\n\n\tset(provider: EffectProvider | null): void {\n\t\tif (provider === this.current) return;\n\t\tthis.current = provider;\n\t\tfor (const l of [...this.listeners]) l(provider);\n\t}\n\n\t/** Sets `provider` and returns a function that clears it again (if it is still the one set). */\n\tuse(provider: EffectProvider): () => void {\n\t\tthis.set(provider);\n\t\treturn () => {\n\t\t\tif (this.current === provider) this.set(null);\n\t\t};\n\t}\n\n\t/** The provider, or a `NoEffectsProviderError`. */\n\trequire(): EffectProvider {\n\t\tif (!this.current) throw new NoEffectsProviderError();\n\t\treturn this.current;\n\t}\n\n\tsubscribe(listener: (provider: EffectProvider | null) => void): () => void {\n\t\tthis.listeners.add(listener);\n\t\treturn () => this.listeners.delete(listener);\n\t}\n}\n","/**\n * `EffectRendererPool`: keeps a few live renderers, reused per effect, size and depth.\n *\n * GPU providers hold one graphics context per renderer, and browsers allow only about sixteen\n * WebGL contexts at a time (the oldest is lost after that). So the pool keeps at most\n * `maxRenderers` renderers. It disposes the least recently used one once its renders finish, and it\n * runs one render at a time per renderer, as the contract requires.\n */\n\nimport type { EffectPixels, EffectProvider, EffectRenderer, EffectRenderRequest } from './types.js';\n\ninterface PoolEntry {\n\trenderer: Promise<EffectRenderer>;\n\t/** The last render queued on it. */\n\tqueue: Promise<unknown>;\n}\n\nexport interface EffectRendererPoolOptions {\n\t/** Most live renderers (default 4). */\n\tmaxRenderers?: number;\n}\n\nexport class EffectRendererPool {\n\tprivate readonly entries = new Map<string, PoolEntry>();\n\tprivate readonly maxRenderers: number;\n\n\tconstructor(options: EffectRendererPoolOptions = {}) {\n\t\tthis.maxRenderers = Math.max(1, options.maxRenderers ?? 4);\n\t}\n\n\t/** Live renderers now. */\n\tget size(): number {\n\t\treturn this.entries.size;\n\t}\n\n\t/** Renders `request` with the provider's renderer for this effect, size and depth (made on first use). */\n\trender(provider: EffectProvider, id: string, size: { width: number; height: number; depth?: 8 | 16 | 32 }, request: EffectRenderRequest): Promise<EffectPixels> {\n\t\tconst depth = size.depth ?? 8;\n\t\tconst key = `${provider.id}:${id}@${size.width}x${size.height}x${depth}`;\n\t\tlet entry = this.entries.get(key);\n\t\tif (entry) {\n\t\t\tthis.entries.delete(key);\n\t\t\tthis.entries.set(key, entry);\n\t\t} else {\n\t\t\tconst renderer = Promise.resolve().then(() => provider.createRenderer(id, { width: size.width, height: size.height, depth }));\n\t\t\trenderer.catch(() => {\n\t\t\t\tif (this.entries.get(key)?.renderer === renderer) this.entries.delete(key);\n\t\t\t});\n\t\t\tentry = { renderer, queue: Promise.resolve() };\n\t\t\tthis.entries.set(key, entry);\n\t\t\tthis.evict();\n\t\t}\n\t\tconst e = entry;\n\t\tconst next = e.queue.then(() => e.renderer).then((r) => r.render(request));\n\t\te.queue = next.catch(() => undefined);\n\t\treturn next;\n\t}\n\n\t/** Disposes the least recently used renderers over the limit, after their queued renders. */\n\tprivate evict(): void {\n\t\twhile (this.entries.size > this.maxRenderers) {\n\t\t\tconst [key, entry] = this.entries.entries().next().value!;\n\t\t\tthis.entries.delete(key);\n\t\t\tdispose(entry);\n\t\t}\n\t}\n\n\t/** Disposes every renderer (after its queued renders). */\n\tclear(): void {\n\t\tfor (const entry of this.entries.values()) dispose(entry);\n\t\tthis.entries.clear();\n\t}\n}\n\nfunction dispose(entry: PoolEntry) {\n\tentry.queue\n\t\t.then(() => entry.renderer)\n\t\t.then(\n\t\t\t(r) => r.dispose(),\n\t\t\t() => {}\n\t\t);\n}\n"]}
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
"use strict";Object.defineProperty(exports, "__esModule", {value: true}); function _nullishCoalesce(lhs, rhsFn) { if (lhs != null) { return lhs; } else { return rhsFn(); } } function _optionalChain(ops) { let lastAccessLHS = undefined; let value = ops[0]; let i = 1; while (i < ops.length) { const op = ops[i]; const fn = ops[i + 1]; i += 2; if ((op === 'optionalAccess' || op === 'optionalCall') && value == null) { return undefined; } if (op === 'access' || op === 'optionalAccess') { lastAccessLHS = value; value = fn(value); } else if (op === 'call' || op === 'optionalCall') { value = fn((...args) => value.call(lastAccessLHS, ...args)); lastAccessLHS = undefined; } } return value; }
|
|
2
2
|
|
|
3
|
-
var
|
|
3
|
+
var _chunkCN4PJHUGcjs = require('./chunk-CN4PJHUG.cjs');
|
|
4
4
|
|
|
5
5
|
// src/effects/types.ts
|
|
6
6
|
var EFFECT_KINDS = ["shader", "vector", "motion"];
|
|
@@ -65,7 +65,7 @@ function createEffectToolProvider(host) {
|
|
|
65
65
|
try {
|
|
66
66
|
return await run();
|
|
67
67
|
} catch (error) {
|
|
68
|
-
if (
|
|
68
|
+
if (_chunkCN4PJHUGcjs.isEffectError.call(void 0, error)) throw new Error(error.message);
|
|
69
69
|
throw error;
|
|
70
70
|
}
|
|
71
71
|
};
|
|
@@ -212,4 +212,4 @@ function createEffectToolProvider(host) {
|
|
|
212
212
|
|
|
213
213
|
|
|
214
214
|
exports.EFFECT_KINDS = EFFECT_KINDS; exports.EFFECT_USES = EFFECT_USES; exports.createEffectToolProvider = createEffectToolProvider;
|
|
215
|
-
//# sourceMappingURL=chunk-
|
|
215
|
+
//# sourceMappingURL=chunk-FHYDQOPC.cjs.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"sources":["/home/runner/work/editor/editor/packages/core/dist/chunk-TK53AB4Q.cjs","../src/effects/types.ts","../src/effects/tools.ts"],"names":[],"mappings":"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;AACV,MAAA;AACD,IAAA;AACD,EAAA;AAEM,EAAA;AACL,IAAA;AACO,MAAA;AACC,MAAA;AACP,MAAA;AACA,MAAA;AACC,QAAA;AACS,UAAA;AACA,UAAA;AACA,UAAA;AACD,UAAA;AACP,UAAA;AACA,UAAA;AACD,QAAA;AACC,QAAA;AACF,MAAA;AACA,MAAA;AACS,MAAA;AACC,QAAA;AACF,QAAA;AACR,MAAA;AACD,IAAA;AACA,IAAA;AACO,MAAA;AACC,MAAA;AACP,MAAA;AACA,MAAA;AACC,QAAA;AACS,UAAA;AACA,UAAA;AACA,UAAA;AACR,UAAA;AACA,UAAA;AACO,UAAA;AACR,QAAA;AACC,QAAA;AACF,MAAA;AACA,MAAA;AACS,MAAA;AACC,QAAA;AACA,QAAA;AACF,QAAA;AACR,MAAA;AACD,IAAA;AACA,IAAA;AACO,MAAA;AACC,MAAA;AACP,MAAA;AACA,MAAA;AACA,MAAA;AACS,MAAA;AACC,QAAA;AACF,QAAA;AACR,MAAA;AACD,IAAA;AACD,EAAA;AAEO,EAAA;AACG,IAAA;AACA,IAAA;AACI,IAAA;AACN,MAAA;AACG,MAAA;AACE,MAAA;AACZ,IAAA;AACY,IAAA;AACD,MAAA;AACE,MAAA;AACL,MAAA;AACI,QAAA;AACJ,wBAAA;AACP,MAAA;AACD,IAAA;AACD,EAAA;AACD;AFzGkB;AACA;AACA;AACA;AACA;AACA","file":"/home/runner/work/editor/editor/packages/core/dist/chunk-TK53AB4Q.cjs","sourcesContent":[null,"/**\n * The effects contract: how an editor finds, describes and renders effects that live in another\n * package. This library ships no effects of its own here, only the shapes a provider fills in;\n * a host passes a provider to an editor (`setEffectsProvider`) and the editor degrades to its\n * old behaviour without one.\n *\n * Every time is in milliseconds. Pixels are straight-alpha RGBA rows, top to bottom.\n */\n\nimport type { JsonSchema } from '../tools/schema.js';\nimport type { FlairSvgAnimation } from '../animation-editor/flair.js';\n\n/** How an effect is drawn: a pixel shader, an animated vector picture, or motion applied to someone else's vector art. */\nexport type EffectKind = 'shader' | 'vector' | 'motion';\n\n/**\n * What an effect is for. An effect may have several uses.\n *\n * - `filter`: changes an input picture (`inputs.source`).\n * - `transition`: blends `inputs.from` into `inputs.to`; `progress` runs 0 → 1.\n * - `generator`: draws a picture from nothing (a background, a texture).\n * - `overlay`: draws something meant to sit over other content, usually with transparency.\n * - `title`: draws type or a graphic meant as a title.\n * - `motion`: animates vector art (`applyVectorEffect`).\n * - `interactive`: reacts to pointer, scroll or audio input at run time.\n */\nexport type EffectUse = 'filter' | 'transition' | 'generator' | 'overlay' | 'title' | 'motion' | 'interactive';\n\nexport const EFFECT_KINDS: readonly EffectKind[] = ['shader', 'vector', 'motion'];\nexport const EFFECT_USES: readonly EffectUse[] = ['filter', 'transition', 'generator', 'overlay', 'title', 'motion', 'interactive'];\n\n/** One entry of a provider's catalog. */\nexport interface EffectSummary {\n\t/** Stable id, saved in documents. Never reused for a different effect. */\n\tid: string;\n\tname: string;\n\tkind: EffectKind;\n\t/** The provider's own grouping (e.g. `gradient`, `light`, `transition`). */\n\tcategory: string;\n\tuses: readonly EffectUse[];\n\tdescription?: string;\n\ttags?: readonly string[];\n\t/** True when the effect's output has transparent pixels (it can sit over other content). */\n\ttransparent: boolean;\n\t/** True when the effect loops; its `duration` is then one cycle. */\n\tloop: boolean;\n\t/** Length of one play (ms). Absent for effects that do not change over time. */\n\tduration?: number;\n}\n\nexport interface EffectSearchOptions {\n\tkind?: EffectKind;\n\tcategory?: string;\n\tuse?: EffectUse;\n\t/** Most results to return. */\n\tlimit?: number;\n}\n\n/** One control value: a number, a `#rrggbb` colour or other string, a switch, or a vector of numbers. */\nexport type EffectValue = number | string | boolean | readonly number[];\nexport type EffectValues = Readonly<Record<string, EffectValue>>;\n\n/**\n * The controls of an effect as a strict JSON Schema: `type: 'object'`, `additionalProperties:\n * false`, and a `description` on every property (with units and range). `checkEffectProvider`\n * checks this.\n */\nexport type EffectControlSchema = JsonSchema & {\n\ttype: 'object';\n\tproperties: { readonly [control: string]: JsonSchema };\n\tadditionalProperties: false;\n};\n\nexport interface EffectPreset {\n\tname: string;\n\t/** Values to apply over the defaults. */\n\tvalues: EffectValues;\n}\n\n/**\n * A named input the effect reads. Filters read `source`, transitions `from` and `to`; a mask is\n * passed separately (`EffectRenderRequest.mask`), not as an input.\n */\nexport interface EffectInputSpec {\n\tname: string;\n\tkind: 'image' | 'audio';\n\tdescription: string;\n\trequired: boolean;\n}\n\n/** Everything an editor needs to show an effect's controls and render it. */\nexport interface EffectDescription extends EffectSummary {\n\tcontrols: EffectControlSchema;\n\t/** A value for every control. */\n\tdefaults: EffectValues;\n\tpresets: readonly EffectPreset[];\n\tinputs: readonly EffectInputSpec[];\n}\n\n/**\n * Pixels in and out of a renderer, shaped like `ImageData`.\n *\n * `data` is 8-bit sRGB with straight alpha and is always present. `float`, when present, is the\n * same picture as 32-bit floats in **linear light** (sRGB primaries), straight alpha, where colour\n * may exceed 1. Renderers created with `depth` 16 or 32 return `float` when the provider\n * `supportsFloat`; `data` is then its clamped sRGB encoding.\n */\nexport interface EffectPixels {\n\twidth: number;\n\theight: number;\n\tdata: Uint8ClampedArray;\n\tfloat?: Float32Array;\n}\n\n/**\n * An input picture. Always accepted as `EffectPixels`; a provider whose capabilities include\n * `supportsHostImages` also takes the host's own image objects, which saves a read back.\n */\nexport type EffectImageSource = EffectPixels | ImageBitmap | OffscreenCanvas | VideoFrame;\n\nexport interface EffectRendererOptions {\n\t/** Output size in pixels. */\n\twidth: number;\n\theight: number;\n\t/** Bits per channel of the target (default 8). 16 and 32 ask for `float` output. */\n\tdepth?: 8 | 16 | 32;\n}\n\nexport interface EffectRenderRequest {\n\t/** Effect time (ms), from the start of the effect. */\n\ttime: number;\n\t/** Control values; missing controls take their defaults. Invalid values reject with `EffectControlError`. */\n\tvalues?: EffectValues;\n\t/** Pictures for the effect's named inputs (see `EffectDescription.inputs`). */\n\tinputs?: Readonly<Record<string, EffectImageSource>>;\n\t/** Coverage (alpha channel, 0 = untouched) limiting where the effect applies. Only with `supportsMask`. */\n\tmask?: EffectImageSource;\n\t/** Transitions: how far from `from` to `to`, 0–1. Defaults to `time / duration`. */\n\tprogress?: number;\n}\n\n/** A live renderer for one effect at one size. Not safe for concurrent `render` calls. */\nexport interface EffectRenderer {\n\treadonly effectId: string;\n\treadonly width: number;\n\treadonly height: number;\n\treadonly depth: 8 | 16 | 32;\n\trender(request: EffectRenderRequest): Promise<EffectPixels>;\n\t/** Frees GPU or worker resources. Rendering afterwards rejects. */\n\tdispose(): void;\n}\n\n/** One change of an animated SVG (the shape of Flair's `SvgAnimation`). */\nexport type EffectSvgAnimation = FlairSvgAnimation;\n\n/** An animated SVG: markup plus changes to its elements (the shape of Flair's `SvgMotion`). */\nexport interface EffectVectorMotion {\n\tsvg: string;\n\tanimate?: readonly EffectSvgAnimation[];\n\t/** Length of one play (ms). */\n\tduration: number;\n\tloop: boolean;\n\t/** Ms held on the last frame before looping. */\n\thold?: number;\n}\n\n/** What a vector or motion effect is applied to: SVG markup, optionally with the ids of the elements to animate (default: all). */\nexport type EffectVectorTarget = string | { svg: string; nodeIds?: readonly string[] };\n\n/** What a provider can do, so editors can degrade cleanly. */\nexport interface EffectProviderCapabilities {\n\t/** Renders off the main thread. */\n\tsupportsWorker: boolean;\n\t/** Returns `float` pixels for depth 16 and 32 renderers. */\n\tsupportsFloat: boolean;\n\t/** Honours `EffectRenderRequest.mask`; otherwise the editor masks the result itself. */\n\tsupportsMask: boolean;\n\t/** Takes `ImageBitmap`, `OffscreenCanvas` or `VideoFrame` inputs, not only `EffectPixels`. */\n\tsupportsHostImages: boolean;\n\t/** Implements `applyVectorEffect`. */\n\tsupportsVector: boolean;\n\t/** Largest width or height a renderer accepts (px). */\n\tmaxSize?: number;\n}\n\ntype MaybePromise<T> = T | Promise<T>;\n\n/**\n * A source of effects. Implementations live outside this library (for example Flair's\n * `flairEffects()`); `createFakeEffectProvider` is a small reference implementation.\n *\n * Unknown ids reject or throw `UnknownEffectError`, invalid values `EffectControlError`, and an\n * operation the provider cannot do `EffectUnsupportedError`.\n */\nexport interface EffectProvider {\n\t/** Stable id of the provider, saved next to effect ids (e.g. `flair`). */\n\treadonly id: string;\n\treadonly name: string;\n\treadonly version?: string;\n\treadonly capabilities: EffectProviderCapabilities;\n\tlist(options?: EffectSearchOptions): MaybePromise<readonly EffectSummary[]>;\n\t/** Effects matching every word of `query` (in name, id, category, tags or description), best first. */\n\tsearch(query: string, options?: EffectSearchOptions): MaybePromise<readonly EffectSummary[]>;\n\tdescribe(id: string): MaybePromise<EffectDescription>;\n\t/** A pixel renderer. A `vector` effect may refuse with `EffectUnsupportedError` where it cannot lay out SVG (a Worker). */\n\tcreateRenderer(id: string, options: EffectRendererOptions): MaybePromise<EffectRenderer>;\n\t/**\n\t * Vector and motion effects as an animated SVG. Present when `capabilities.supportsVector`. A\n\t * `motion` effect animates the target's elements (`nodeIds`, else each top-level element with an\n\t * id); a `vector` effect brings its own picture and ignores the target. Refuse a target the\n\t * effect cannot use with `EffectUnsupportedError`.\n\t */\n\tapplyVectorEffect?(id: string, target: EffectVectorTarget, values?: EffectValues): MaybePromise<EffectVectorMotion>;\n}\n","/**\n * The agent tools for provider effects, shared by every editor that takes an `EffectProvider`:\n * `search_effects`, `describe_effect`, `list_applied_effects`, `apply_effect`, `update_effect` and\n * `remove_effect`. An editor supplies an `EffectToolHost` (what its targets are and how to change\n * them as one undo step each) and may add tools of its own (`extraTools`).\n *\n * Without a provider the read tools say so (and `list_applied_effects` still lists what a\n * document holds), and the write tools refuse with the reason. On a read-only surface the write\n * tools are hidden.\n */\n\nimport type { AgentTool, AgentToolProvider } from '../tools/registry.js';\nimport type { JsonSchema } from '../tools/schema.js';\nimport { isEffectError } from './errors.js';\nimport { EFFECT_KINDS, EFFECT_USES, type EffectDescription, type EffectProvider, type EffectSummary, type EffectUse } from './types.js';\n\n/** One effect applied to a target, as the tools report it. */\nexport interface AppliedEffectInfo {\n\t/** Its id on the target: what `update_effect` and `remove_effect` take. */\n\tid: string;\n\teffect: string;\n\tname?: string;\n\tprovider?: string;\n\t/** What it is on the target (e.g. `clip effect`, `effect clip`, `transition`, `smart filter`, `effect layer`). */\n\trole: string;\n\tenabled: boolean;\n\t/** 0–1, or keyframes, where the role has an opacity. */\n\topacity?: unknown;\n\t/** Its saved control values (numbers may be keyframes where the editor allows them). */\n\tvalues: Record<string, unknown>;\n\t/** Its place in the target's stack (0 = applied first), for stacked roles. */\n\tindex?: number;\n}\n\nexport interface EffectTargetEffects {\n\ttarget: string;\n\t/** A label for the target (a clip or layer name). */\n\tname?: string;\n\teffects: AppliedEffectInfo[];\n}\n\nexport interface EffectChangeResult {\n\ttarget: string;\n\t/** What was added, changed or removed. */\n\teffect: AppliedEffectInfo;\n\t/** The target's effects afterwards. */\n\teffects: AppliedEffectInfo[];\n\t/** Other things the change made (a new clip or layer id, ...). */\n\t[extra: string]: unknown;\n}\n\n/** What an editor gives the shared effect tools. */\nexport interface EffectToolHost {\n\t/** Provider id (unique per registry). */\n\tid: string;\n\t/** Always prefix tool names with `<namespace>__`. */\n\tnamespace?: string;\n\t/** Words for the editor's targets, e.g. `{ noun: 'clip', id: 'A clip id from get_video_project (e.g. \"c1\").' }`. */\n\ttarget: { noun: string; id: string };\n\t/** The uses `apply_effect` takes on a target (others go through `extraTools`). */\n\tapplyUses: readonly EffectUse[];\n\t/** Text added to `apply_effect`'s description: where the effect goes and how it is drawn. */\n\tapplyDetails: string;\n\t/** The current provider, or `null`. */\n\tprovider(): EffectProvider | null;\n\t/** Why edits are refused now (read-only), or `null`. */\n\treadOnlyReason(): string | null;\n\t/** Every target with effects, or one target (throws for an unknown id, naming valid ones). */\n\tlist(target?: string): EffectTargetEffects[];\n\tapply(target: string, effect: string, args: { values?: Record<string, unknown>; index?: number; enabled?: boolean; opacity?: number }): Promise<EffectChangeResult>;\n\tupdate(target: string, instance: string, changes: { values?: Record<string, unknown>; index?: number; enabled?: boolean; opacity?: number }): Promise<EffectChangeResult>;\n\tremove(target: string, instance: string): Promise<EffectChangeResult>;\n\t/** Editor-specific effect tools (transitions, effect clips, effect layers, keyframes). */\n\textraTools?: (context: EffectToolContext) => AgentTool[];\n\t/** Lets the tool list follow the provider and read-only changes. */\n\tsubscribe?: (notify: () => void) => () => void;\n}\n\n/** Shared pieces for `extraTools`. */\nexport interface EffectToolContext {\n\t/** The provider, or a thrown error saying none is registered. */\n\trequireProvider(): EffectProvider;\n\t/** The schema of a `values` argument. */\n\tvaluesSchema(what: string, keyframes?: boolean): JsonSchema;\n\t/** The schema of an `effect` argument for effects with one of `uses`. */\n\teffectIdSchema(uses: readonly EffectUse[]): JsonSchema;\n\treadonly annotations: typeof ANNOTATIONS;\n}\n\nconst ANNOTATIONS = {\n\tread: { readOnlyHint: true, destructiveHint: false, idempotentHint: true, openWorldHint: false },\n\twrite: { readOnlyHint: false, destructiveHint: false, idempotentHint: false, openWorldHint: false },\n\twriteIdempotent: { readOnlyHint: false, destructiveHint: false, idempotentHint: true, openWorldHint: false },\n\tdestructive: { readOnlyHint: false, destructiveHint: true, idempotentHint: false, openWorldHint: false }\n} as const;\n\nconst NO_PROVIDER = 'No effects provider is registered with this editor, so there are no effects to search, describe or apply. The host app must register one (setEffectsProvider); effects already in the document are kept and shown as placeholders.';\n\nconst object = (properties: Record<string, JsonSchema>, required: string[] = []): JsonSchema & { type: 'object' } => ({\n\ttype: 'object',\n\tproperties,\n\t...(required.length ? { required } : {}),\n\tadditionalProperties: false\n});\n\n/** The catalog fetched once per provider, so tool schemas can list effect ids. */\ninterface Catalog {\n\tprovider: EffectProvider;\n\teffects: readonly EffectSummary[] | null;\n}\n\n/** Most effect ids listed in an `effect` argument's enum; larger catalogs are searched instead. */\nconst ENUM_LIMIT = 60;\n\nconst summaryOut = (e: EffectSummary) => ({\n\tid: e.id,\n\tname: e.name,\n\tkind: e.kind,\n\tcategory: e.category,\n\tuses: e.uses,\n\t...(e.description ? { description: e.description } : {}),\n\ttransparent: e.transparent,\n\tloop: e.loop,\n\t...(e.duration !== undefined ? { duration: e.duration } : {})\n});\n\n/** The agent tools for provider effects in one editor (see the module comment). */\nexport function createEffectToolProvider(host: EffectToolHost): AgentToolProvider {\n\tlet catalog: Catalog | null = null;\n\tconst listeners = new Set<() => void>();\n\tconst notify = () => listeners.forEach((l) => l());\n\n\t/** Fetches the catalog of a new provider in the background; the tool list changes when it lands. */\n\tconst catalogFor = (provider: EffectProvider | null): readonly EffectSummary[] | null => {\n\t\tif (!provider) return null;\n\t\tif (catalog?.provider !== provider) {\n\t\t\tconst entry: Catalog = { provider, effects: null };\n\t\t\tcatalog = entry;\n\t\t\tPromise.resolve()\n\t\t\t\t.then(() => provider.list())\n\t\t\t\t.then((effects) => {\n\t\t\t\t\tif (catalog === entry) {\n\t\t\t\t\t\tentry.effects = effects;\n\t\t\t\t\t\tnotify();\n\t\t\t\t\t}\n\t\t\t\t})\n\t\t\t\t.catch(() => {});\n\t\t}\n\t\treturn catalog.effects;\n\t};\n\n\tconst requireProvider = (): EffectProvider => {\n\t\tconst p = host.provider();\n\t\tif (!p) throw new Error(NO_PROVIDER);\n\t\treturn p;\n\t};\n\tconst writable = () => {\n\t\tconst reason = host.readOnlyReason();\n\t\tif (reason) throw new Error(reason);\n\t\treturn requireProvider();\n\t};\n\t/** Turns provider errors into messages a model can act on. */\n\tconst explain = async <T>(run: () => Promise<T>): Promise<T> => {\n\t\ttry {\n\t\t\treturn await run();\n\t\t} catch (error) {\n\t\t\tif (isEffectError(error)) throw new Error(error.message);\n\t\t\tthrow error;\n\t\t}\n\t};\n\n\tconst valuesSchema = (what: string, keyframes = false): JsonSchema => ({\n\t\ttype: 'object',\n\t\tdescription: `${what}: control name → 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.`\n\t});\n\tconst effectIdSchema = (uses: readonly EffectUse[]): JsonSchema => {\n\t\tconst effects = catalogFor(host.provider())?.filter((e) => e.kind !== 'motion' && e.uses.some((u) => uses.includes(u)));\n\t\tconst base = `An effect id from search_effects (use ${uses.map((u) => `\"${u}\"`).join(' or ')}).`;\n\t\treturn effects && effects.length && effects.length <= ENUM_LIMIT\n\t\t\t? { type: 'string', enum: effects.map((e) => e.id), description: `${base} One of: ${effects.map((e) => `${e.id} (${e.name})`).join(', ')}.` }\n\t\t\t: { type: 'string', minLength: 1, description: base };\n\t};\n\tconst context: EffectToolContext = { requireProvider: writable, valuesSchema, effectIdSchema, annotations: ANNOTATIONS };\n\n\tconst TARGET = { type: 'string', minLength: 1, description: host.target.id } as const;\n\tconst INSTANCE = { type: 'string', minLength: 1, description: `The effect's id on the ${host.target.noun}, from list_applied_effects or apply_effect.` } as const;\n\tconst INDEX = { type: 'integer', minimum: 0, description: `Place in the ${host.target.noun}'s effect stack: 0 is applied first (default: the end, applied last).` } as const;\n\tconst OPACITY = { type: 'number', minimum: 0, maximum: 1, description: 'How much of the effect shows over its input, 0–1 (default 1).' } as const;\n\tconst ENABLED = { type: 'boolean', description: 'false turns the effect off but keeps it (default true).' } as const;\n\n\tconst readTools = (): AgentTool[] => [\n\t\t{\n\t\t\tname: 'search_effects',\n\t\t\ttitle: 'Search effects',\n\t\t\tdescription: `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), category or use (${EFFECT_USES.join(', ')}). For this editor, apply_effect takes uses ${host.applyUses.join(' or ')}. Returns { provider, effects: [{ id, name, kind, category, uses, description, transparent, loop, duration (ms) }], total }. With no provider registered it returns available: false and the reason. Call describe_effect next for an effect's controls.`,\n\t\t\tinputSchema: object({\n\t\t\t\tquery: { type: 'string', description: 'Words to match (default: every effect).' },\n\t\t\t\tkind: { type: 'string', enum: [...EFFECT_KINDS], description: 'shader (pixels), vector (animated SVG) or motion (animates vector art).' },\n\t\t\t\tcategory: { type: 'string', minLength: 1, description: 'The provider\\'s category, e.g. \"gradient\", \"light\", \"transition\".' },\n\t\t\t\tuse: { 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.' },\n\t\t\t\tlimit: { type: 'integer', minimum: 1, maximum: 200, description: 'Most effects to return (default 20).' }\n\t\t\t}),\n\t\t\tannotations: { title: 'Search effects', ...ANNOTATIONS.read },\n\t\t\thandler: async (args) => {\n\t\t\t\tconst p = host.provider();\n\t\t\t\tif (!p) return { available: false, reason: NO_PROVIDER, effects: [], total: 0 };\n\t\t\t\tconst found = await explain(async () => p.search((args.query as string | undefined) ?? '', { kind: args.kind, category: args.category, use: args.use }));\n\t\t\t\tconst limit = (args.limit as number | undefined) ?? 20;\n\t\t\t\treturn { available: true, provider: { id: p.id, name: p.name, capabilities: p.capabilities }, effects: found.slice(0, limit).map(summaryOut), total: found.length };\n\t\t\t}\n\t\t},\n\t\t{\n\t\t\tname: 'describe_effect',\n\t\t\ttitle: 'Describe an effect',\n\t\t\tdescription: 'Returns one effect\\'s controls as a strict JSON Schema (each control described, with its type, range and units), its defaults, presets (named sets of values) and inputs, plus its summary. Use the control names and ranges for apply_effect and update_effect values. With no provider registered it returns available: false and the reason.',\n\t\t\tinputSchema: object({ effect: { type: 'string', minLength: 1, description: 'An effect id from search_effects or list_applied_effects.' } }, ['effect']),\n\t\t\tannotations: { title: 'Describe an effect', ...ANNOTATIONS.read },\n\t\t\thandler: async (args) => {\n\t\t\t\tconst p = host.provider();\n\t\t\t\tif (!p) return { available: false, reason: NO_PROVIDER };\n\t\t\t\tconst d: EffectDescription = await explain(async () => p.describe(args.effect as string));\n\t\t\t\treturn { available: true, ...summaryOut(d), controls: d.controls, defaults: d.defaults, presets: d.presets, inputs: d.inputs, appliesHere: d.kind !== 'motion' && d.uses.some((u) => host.applyUses.includes(u)) };\n\t\t\t}\n\t\t},\n\t\t{\n\t\t\tname: 'list_applied_effects',\n\t\t\ttitle: 'List applied effects',\n\t\t\tdescription: `Lists the effects in the document: for each ${host.target.noun} that has any (or the one given), its effects in order with id, effect id, name, role, enabled, opacity and saved values. available is false for an effect the current provider does not have (or when none is registered): it is kept and drawn as a placeholder, and can still be removed. Works without a provider.`,\n\t\t\tinputSchema: object({ target: { ...TARGET, description: `${host.target.id} Leave out to list every ${host.target.noun} with effects.` } }),\n\t\t\tannotations: { title: 'List applied effects', ...ANNOTATIONS.read },\n\t\t\thandler: async (args) => {\n\t\t\t\tconst p = host.provider();\n\t\t\t\tconst known = new Set((p ? await explain(async () => p.list()) : []).map((e) => e.id));\n\t\t\t\tconst targets = host.list(args.target as string | undefined).map((t) => ({ ...t, effects: t.effects.map((e) => ({ ...e, available: known.has(e.effect) })) }));\n\t\t\t\treturn { provider: p ? { id: p.id, name: p.name } : null, ...(p ? {} : { reason: NO_PROVIDER }), targets };\n\t\t\t}\n\t\t}\n\t];\n\n\tconst writeTools = (): AgentTool[] => [\n\t\t{\n\t\t\tname: 'apply_effect',\n\t\t\ttitle: 'Apply an effect',\n\t\t\tdescription: `Adds a provider effect to a ${host.target.noun}. ${host.applyDetails} values are checked against describe_effect's controls first; nothing changes if any is wrong. One undo step. Returns the effect with its new id (for update_effect and remove_effect) and the ${host.target.noun}'s effects. Refused when no provider is registered.`,\n\t\t\tinputSchema: object(\n\t\t\t\t{\n\t\t\t\t\ttarget: TARGET,\n\t\t\t\t\teffect: effectIdSchema(host.applyUses),\n\t\t\t\t\tvalues: valuesSchema('Control values over the effect\\'s defaults', true),\n\t\t\t\t\tindex: INDEX,\n\t\t\t\t\tenabled: ENABLED,\n\t\t\t\t\topacity: OPACITY\n\t\t\t\t},\n\t\t\t\t['target', 'effect']\n\t\t\t),\n\t\t\tannotations: { title: 'Apply an effect', ...ANNOTATIONS.write },\n\t\t\thandler: async (args) => {\n\t\t\t\twritable();\n\t\t\t\treturn explain(() => host.apply(args.target as string, args.effect as string, { values: args.values, index: args.index, enabled: args.enabled, opacity: args.opacity }));\n\t\t\t}\n\t\t},\n\t\t{\n\t\t\tname: 'update_effect',\n\t\t\ttitle: 'Change an applied effect',\n\t\t\tdescription: `Changes one applied effect on a ${host.target.noun}: its values (merged over the current ones; null resets a control to its default), enabled, opacity or place in the stack. Values are checked against describe_effect's controls first. One undo step. Returns the effect as it is now and the ${host.target.noun}'s effects. Refused when no provider is registered.`,\n\t\t\tinputSchema: object(\n\t\t\t\t{\n\t\t\t\t\ttarget: TARGET,\n\t\t\t\t\teffect: INSTANCE,\n\t\t\t\t\tvalues: { ...valuesSchema('Control values to change', true), description: `${valuesSchema('Control values to change', true).description} null resets a control to its default.` },\n\t\t\t\t\tenabled: ENABLED,\n\t\t\t\t\topacity: OPACITY,\n\t\t\t\t\tindex: { ...INDEX, description: `Move it to this place in the ${host.target.noun}'s stack (0 = applied first).` }\n\t\t\t\t},\n\t\t\t\t['target', 'effect']\n\t\t\t),\n\t\t\tannotations: { title: 'Change an applied effect', ...ANNOTATIONS.writeIdempotent },\n\t\t\thandler: async (args) => {\n\t\t\t\twritable();\n\t\t\t\tif (args.values === undefined && args.enabled === undefined && args.opacity === undefined && args.index === undefined) throw new Error('Give at least one of values, enabled, opacity or index to change.');\n\t\t\t\treturn explain(() => host.update(args.target as string, args.effect as string, { values: args.values, enabled: args.enabled, opacity: args.opacity, index: args.index }));\n\t\t\t}\n\t\t},\n\t\t{\n\t\t\tname: 'remove_effect',\n\t\t\ttitle: 'Remove an applied effect',\n\t\t\tdescription: `Removes one applied effect from a ${host.target.noun} (an effect the provider no longer has included). One undo step. Returns what was removed and the ${host.target.noun}'s remaining effects. Refused when no provider is registered.`,\n\t\t\tinputSchema: object({ target: TARGET, effect: INSTANCE }, ['target', 'effect']),\n\t\t\tannotations: { title: 'Remove an applied effect', ...ANNOTATIONS.destructive },\n\t\t\thandler: async (args) => {\n\t\t\t\twritable();\n\t\t\t\treturn explain(() => host.remove(args.target as string, args.effect as string));\n\t\t\t}\n\t\t}\n\t];\n\n\treturn {\n\t\tid: host.id,\n\t\t...(host.namespace ? { namespace: host.namespace } : {}),\n\t\ttools: () => {\n\t\t\tconst extra = host.extraTools?.(context) ?? [];\n\t\t\tif (host.readOnlyReason()) return [...readTools(), ...extra.filter((t) => t.annotations?.readOnlyHint)];\n\t\t\treturn [...readTools(), ...writeTools(), ...extra];\n\t\t},\n\t\tsubscribe: (l) => {\n\t\t\tlisteners.add(l);\n\t\t\tconst off = host.subscribe?.(l);\n\t\t\treturn () => {\n\t\t\t\tlisteners.delete(l);\n\t\t\t\toff?.();\n\t\t\t};\n\t\t}\n\t};\n}\n"]}
|
|
1
|
+
{"version":3,"sources":["/home/runner/work/editor/editor/packages/core/dist/chunk-FHYDQOPC.cjs","../src/effects/types.ts","../src/effects/tools.ts"],"names":[],"mappings":"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;AACV,MAAA;AACD,IAAA;AACD,EAAA;AAEM,EAAA;AACL,IAAA;AACO,MAAA;AACC,MAAA;AACP,MAAA;AACA,MAAA;AACC,QAAA;AACS,UAAA;AACA,UAAA;AACA,UAAA;AACD,UAAA;AACP,UAAA;AACA,UAAA;AACD,QAAA;AACC,QAAA;AACF,MAAA;AACA,MAAA;AACS,MAAA;AACC,QAAA;AACF,QAAA;AACR,MAAA;AACD,IAAA;AACA,IAAA;AACO,MAAA;AACC,MAAA;AACP,MAAA;AACA,MAAA;AACC,QAAA;AACS,UAAA;AACA,UAAA;AACA,UAAA;AACR,UAAA;AACA,UAAA;AACO,UAAA;AACR,QAAA;AACC,QAAA;AACF,MAAA;AACA,MAAA;AACS,MAAA;AACC,QAAA;AACA,QAAA;AACF,QAAA;AACR,MAAA;AACD,IAAA;AACA,IAAA;AACO,MAAA;AACC,MAAA;AACP,MAAA;AACA,MAAA;AACA,MAAA;AACS,MAAA;AACC,QAAA;AACF,QAAA;AACR,MAAA;AACD,IAAA;AACD,EAAA;AAEO,EAAA;AACG,IAAA;AACA,IAAA;AACI,IAAA;AACN,MAAA;AACG,MAAA;AACE,MAAA;AACZ,IAAA;AACY,IAAA;AACD,MAAA;AACE,MAAA;AACL,MAAA;AACI,QAAA;AACJ,wBAAA;AACP,MAAA;AACD,IAAA;AACD,EAAA;AACD;AFzGkB;AACA;AACA;AACA;AACA;AACA","file":"/home/runner/work/editor/editor/packages/core/dist/chunk-FHYDQOPC.cjs","sourcesContent":[null,"/**\n * The effects contract: how an editor finds, describes and renders effects that live in another\n * package. This library ships no effects of its own here, only the shapes a provider fills in;\n * a host passes a provider to an editor (`setEffectsProvider`) and the editor degrades to its\n * old behaviour without one.\n *\n * Every time is in milliseconds. Pixels are straight-alpha RGBA rows, top to bottom.\n */\n\nimport type { JsonSchema } from '../tools/schema.js';\nimport type { FlairSvgAnimation } from '../animation-editor/flair.js';\n\n/** How an effect is drawn: a pixel shader, an animated vector picture, or motion applied to someone else's vector art. */\nexport type EffectKind = 'shader' | 'vector' | 'motion';\n\n/**\n * What an effect is for. An effect may have several uses.\n *\n * - `filter`: changes an input picture (`inputs.source`).\n * - `transition`: blends `inputs.from` into `inputs.to`; `progress` runs 0 → 1.\n * - `generator`: draws a picture from nothing (a background, a texture).\n * - `overlay`: draws something meant to sit over other content, usually with transparency.\n * - `title`: draws type or a graphic meant as a title.\n * - `motion`: animates vector art (`applyVectorEffect`).\n * - `interactive`: reacts to pointer, scroll or audio input at run time.\n */\nexport type EffectUse = 'filter' | 'transition' | 'generator' | 'overlay' | 'title' | 'motion' | 'interactive';\n\nexport const EFFECT_KINDS: readonly EffectKind[] = ['shader', 'vector', 'motion'];\nexport const EFFECT_USES: readonly EffectUse[] = ['filter', 'transition', 'generator', 'overlay', 'title', 'motion', 'interactive'];\n\n/** One entry of a provider's catalog. */\nexport interface EffectSummary {\n\t/** Stable id, saved in documents. Never reused for a different effect. */\n\tid: string;\n\tname: string;\n\tkind: EffectKind;\n\t/** The provider's own grouping (e.g. `gradient`, `light`, `transition`). */\n\tcategory: string;\n\tuses: readonly EffectUse[];\n\tdescription?: string;\n\ttags?: readonly string[];\n\t/** True when the effect's output has transparent pixels (it can sit over other content). */\n\ttransparent: boolean;\n\t/** True when the effect loops; its `duration` is then one cycle. */\n\tloop: boolean;\n\t/** Length of one play (ms). Absent for effects that do not change over time. */\n\tduration?: number;\n}\n\nexport interface EffectSearchOptions {\n\tkind?: EffectKind;\n\tcategory?: string;\n\tuse?: EffectUse;\n\t/** Most results to return. */\n\tlimit?: number;\n}\n\n/** One control value: a number, a `#rrggbb` colour or other string, a switch, or a vector of numbers. */\nexport type EffectValue = number | string | boolean | readonly number[];\nexport type EffectValues = Readonly<Record<string, EffectValue>>;\n\n/**\n * The controls of an effect as a strict JSON Schema: `type: 'object'`, `additionalProperties:\n * false`, and a `description` on every property (with units and range). `checkEffectProvider`\n * checks this.\n */\nexport type EffectControlSchema = JsonSchema & {\n\ttype: 'object';\n\tproperties: { readonly [control: string]: JsonSchema };\n\tadditionalProperties: false;\n};\n\nexport interface EffectPreset {\n\tname: string;\n\t/** Values to apply over the defaults. */\n\tvalues: EffectValues;\n}\n\n/**\n * A named input the effect reads. Filters read `source`, transitions `from` and `to`; a mask is\n * passed separately (`EffectRenderRequest.mask`), not as an input.\n */\nexport interface EffectInputSpec {\n\tname: string;\n\tkind: 'image' | 'audio';\n\tdescription: string;\n\trequired: boolean;\n}\n\n/** Everything an editor needs to show an effect's controls and render it. */\nexport interface EffectDescription extends EffectSummary {\n\tcontrols: EffectControlSchema;\n\t/** A value for every control. */\n\tdefaults: EffectValues;\n\tpresets: readonly EffectPreset[];\n\tinputs: readonly EffectInputSpec[];\n}\n\n/**\n * Pixels in and out of a renderer, shaped like `ImageData`.\n *\n * `data` is 8-bit sRGB with straight alpha and is always present. `float`, when present, is the\n * same picture as 32-bit floats in **linear light** (sRGB primaries), straight alpha, where colour\n * may exceed 1. Renderers created with `depth` 16 or 32 return `float` when the provider\n * `supportsFloat`; `data` is then its clamped sRGB encoding.\n */\nexport interface EffectPixels {\n\twidth: number;\n\theight: number;\n\tdata: Uint8ClampedArray;\n\tfloat?: Float32Array;\n}\n\n/**\n * An input picture. Always accepted as `EffectPixels`; a provider whose capabilities include\n * `supportsHostImages` also takes the host's own image objects, which saves a read back.\n */\nexport type EffectImageSource = EffectPixels | ImageBitmap | OffscreenCanvas | VideoFrame;\n\nexport interface EffectRendererOptions {\n\t/** Output size in pixels. */\n\twidth: number;\n\theight: number;\n\t/** Bits per channel of the target (default 8). 16 and 32 ask for `float` output. */\n\tdepth?: 8 | 16 | 32;\n}\n\nexport interface EffectRenderRequest {\n\t/** Effect time (ms), from the start of the effect. */\n\ttime: number;\n\t/** Control values; missing controls take their defaults. Invalid values reject with `EffectControlError`. */\n\tvalues?: EffectValues;\n\t/** Pictures for the effect's named inputs (see `EffectDescription.inputs`). */\n\tinputs?: Readonly<Record<string, EffectImageSource>>;\n\t/** Coverage (alpha channel, 0 = untouched) limiting where the effect applies. Only with `supportsMask`. */\n\tmask?: EffectImageSource;\n\t/** Transitions: how far from `from` to `to`, 0–1. Defaults to `time / duration`. */\n\tprogress?: number;\n}\n\n/** A live renderer for one effect at one size. Not safe for concurrent `render` calls. */\nexport interface EffectRenderer {\n\treadonly effectId: string;\n\treadonly width: number;\n\treadonly height: number;\n\treadonly depth: 8 | 16 | 32;\n\trender(request: EffectRenderRequest): Promise<EffectPixels>;\n\t/** Frees GPU or worker resources. Rendering afterwards rejects. */\n\tdispose(): void;\n}\n\n/** One change of an animated SVG (the shape of Flair's `SvgAnimation`). */\nexport type EffectSvgAnimation = FlairSvgAnimation;\n\n/** An animated SVG: markup plus changes to its elements (the shape of Flair's `SvgMotion`). */\nexport interface EffectVectorMotion {\n\tsvg: string;\n\tanimate?: readonly EffectSvgAnimation[];\n\t/** Length of one play (ms). */\n\tduration: number;\n\tloop: boolean;\n\t/** Ms held on the last frame before looping. */\n\thold?: number;\n}\n\n/** What a vector or motion effect is applied to: SVG markup, optionally with the ids of the elements to animate (default: all). */\nexport type EffectVectorTarget = string | { svg: string; nodeIds?: readonly string[] };\n\n/** What a provider can do, so editors can degrade cleanly. */\nexport interface EffectProviderCapabilities {\n\t/** Renders off the main thread. */\n\tsupportsWorker: boolean;\n\t/** Returns `float` pixels for depth 16 and 32 renderers. */\n\tsupportsFloat: boolean;\n\t/** Honours `EffectRenderRequest.mask`; otherwise the editor masks the result itself. */\n\tsupportsMask: boolean;\n\t/** Takes `ImageBitmap`, `OffscreenCanvas` or `VideoFrame` inputs, not only `EffectPixels`. */\n\tsupportsHostImages: boolean;\n\t/** Implements `applyVectorEffect`. */\n\tsupportsVector: boolean;\n\t/** Largest width or height a renderer accepts (px). */\n\tmaxSize?: number;\n}\n\ntype MaybePromise<T> = T | Promise<T>;\n\n/**\n * A source of effects. Implementations live outside this library (for example Flair's\n * `flairEffects()`); `createFakeEffectProvider` is a small reference implementation.\n *\n * Unknown ids reject or throw `UnknownEffectError`, invalid values `EffectControlError`, and an\n * operation the provider cannot do `EffectUnsupportedError`.\n */\nexport interface EffectProvider {\n\t/** Stable id of the provider, saved next to effect ids (e.g. `flair`). */\n\treadonly id: string;\n\treadonly name: string;\n\treadonly version?: string;\n\treadonly capabilities: EffectProviderCapabilities;\n\tlist(options?: EffectSearchOptions): MaybePromise<readonly EffectSummary[]>;\n\t/** Effects matching every word of `query` (in name, id, category, tags or description), best first. */\n\tsearch(query: string, options?: EffectSearchOptions): MaybePromise<readonly EffectSummary[]>;\n\tdescribe(id: string): MaybePromise<EffectDescription>;\n\t/** A pixel renderer. A `vector` effect may refuse with `EffectUnsupportedError` where it cannot lay out SVG (a Worker). */\n\tcreateRenderer(id: string, options: EffectRendererOptions): MaybePromise<EffectRenderer>;\n\t/**\n\t * Vector and motion effects as an animated SVG. Present when `capabilities.supportsVector`. A\n\t * `motion` effect animates the target's elements (`nodeIds`, else each top-level element with an\n\t * id); a `vector` effect brings its own picture and ignores the target. Refuse a target the\n\t * effect cannot use with `EffectUnsupportedError`.\n\t */\n\tapplyVectorEffect?(id: string, target: EffectVectorTarget, values?: EffectValues): MaybePromise<EffectVectorMotion>;\n}\n","/**\n * The agent tools for provider effects, shared by every editor that takes an `EffectProvider`:\n * `search_effects`, `describe_effect`, `list_applied_effects`, `apply_effect`, `update_effect` and\n * `remove_effect`. An editor supplies an `EffectToolHost` (what its targets are and how to change\n * them as one undo step each) and may add tools of its own (`extraTools`).\n *\n * Without a provider the read tools say so (and `list_applied_effects` still lists what a\n * document holds), and the write tools refuse with the reason. On a read-only surface the write\n * tools are hidden.\n */\n\nimport type { AgentTool, AgentToolProvider } from '../tools/registry.js';\nimport type { JsonSchema } from '../tools/schema.js';\nimport { isEffectError } from './errors.js';\nimport { EFFECT_KINDS, EFFECT_USES, type EffectDescription, type EffectProvider, type EffectSummary, type EffectUse } from './types.js';\n\n/** One effect applied to a target, as the tools report it. */\nexport interface AppliedEffectInfo {\n\t/** Its id on the target: what `update_effect` and `remove_effect` take. */\n\tid: string;\n\teffect: string;\n\tname?: string;\n\tprovider?: string;\n\t/** What it is on the target (e.g. `clip effect`, `effect clip`, `transition`, `smart filter`, `effect layer`). */\n\trole: string;\n\tenabled: boolean;\n\t/** 0–1, or keyframes, where the role has an opacity. */\n\topacity?: unknown;\n\t/** Its saved control values (numbers may be keyframes where the editor allows them). */\n\tvalues: Record<string, unknown>;\n\t/** Its place in the target's stack (0 = applied first), for stacked roles. */\n\tindex?: number;\n}\n\nexport interface EffectTargetEffects {\n\ttarget: string;\n\t/** A label for the target (a clip or layer name). */\n\tname?: string;\n\teffects: AppliedEffectInfo[];\n}\n\nexport interface EffectChangeResult {\n\ttarget: string;\n\t/** What was added, changed or removed. */\n\teffect: AppliedEffectInfo;\n\t/** The target's effects afterwards. */\n\teffects: AppliedEffectInfo[];\n\t/** Other things the change made (a new clip or layer id, ...). */\n\t[extra: string]: unknown;\n}\n\n/** What an editor gives the shared effect tools. */\nexport interface EffectToolHost {\n\t/** Provider id (unique per registry). */\n\tid: string;\n\t/** Always prefix tool names with `<namespace>__`. */\n\tnamespace?: string;\n\t/** Words for the editor's targets, e.g. `{ noun: 'clip', id: 'A clip id from get_video_project (e.g. \"c1\").' }`. */\n\ttarget: { noun: string; id: string };\n\t/** The uses `apply_effect` takes on a target (others go through `extraTools`). */\n\tapplyUses: readonly EffectUse[];\n\t/** Text added to `apply_effect`'s description: where the effect goes and how it is drawn. */\n\tapplyDetails: string;\n\t/** The current provider, or `null`. */\n\tprovider(): EffectProvider | null;\n\t/** Why edits are refused now (read-only), or `null`. */\n\treadOnlyReason(): string | null;\n\t/** Every target with effects, or one target (throws for an unknown id, naming valid ones). */\n\tlist(target?: string): EffectTargetEffects[];\n\tapply(target: string, effect: string, args: { values?: Record<string, unknown>; index?: number; enabled?: boolean; opacity?: number }): Promise<EffectChangeResult>;\n\tupdate(target: string, instance: string, changes: { values?: Record<string, unknown>; index?: number; enabled?: boolean; opacity?: number }): Promise<EffectChangeResult>;\n\tremove(target: string, instance: string): Promise<EffectChangeResult>;\n\t/** Editor-specific effect tools (transitions, effect clips, effect layers, keyframes). */\n\textraTools?: (context: EffectToolContext) => AgentTool[];\n\t/** Lets the tool list follow the provider and read-only changes. */\n\tsubscribe?: (notify: () => void) => () => void;\n}\n\n/** Shared pieces for `extraTools`. */\nexport interface EffectToolContext {\n\t/** The provider, or a thrown error saying none is registered. */\n\trequireProvider(): EffectProvider;\n\t/** The schema of a `values` argument. */\n\tvaluesSchema(what: string, keyframes?: boolean): JsonSchema;\n\t/** The schema of an `effect` argument for effects with one of `uses`. */\n\teffectIdSchema(uses: readonly EffectUse[]): JsonSchema;\n\treadonly annotations: typeof ANNOTATIONS;\n}\n\nconst ANNOTATIONS = {\n\tread: { readOnlyHint: true, destructiveHint: false, idempotentHint: true, openWorldHint: false },\n\twrite: { readOnlyHint: false, destructiveHint: false, idempotentHint: false, openWorldHint: false },\n\twriteIdempotent: { readOnlyHint: false, destructiveHint: false, idempotentHint: true, openWorldHint: false },\n\tdestructive: { readOnlyHint: false, destructiveHint: true, idempotentHint: false, openWorldHint: false }\n} as const;\n\nconst NO_PROVIDER = 'No effects provider is registered with this editor, so there are no effects to search, describe or apply. The host app must register one (setEffectsProvider); effects already in the document are kept and shown as placeholders.';\n\nconst object = (properties: Record<string, JsonSchema>, required: string[] = []): JsonSchema & { type: 'object' } => ({\n\ttype: 'object',\n\tproperties,\n\t...(required.length ? { required } : {}),\n\tadditionalProperties: false\n});\n\n/** The catalog fetched once per provider, so tool schemas can list effect ids. */\ninterface Catalog {\n\tprovider: EffectProvider;\n\teffects: readonly EffectSummary[] | null;\n}\n\n/** Most effect ids listed in an `effect` argument's enum; larger catalogs are searched instead. */\nconst ENUM_LIMIT = 60;\n\nconst summaryOut = (e: EffectSummary) => ({\n\tid: e.id,\n\tname: e.name,\n\tkind: e.kind,\n\tcategory: e.category,\n\tuses: e.uses,\n\t...(e.description ? { description: e.description } : {}),\n\ttransparent: e.transparent,\n\tloop: e.loop,\n\t...(e.duration !== undefined ? { duration: e.duration } : {})\n});\n\n/** The agent tools for provider effects in one editor (see the module comment). */\nexport function createEffectToolProvider(host: EffectToolHost): AgentToolProvider {\n\tlet catalog: Catalog | null = null;\n\tconst listeners = new Set<() => void>();\n\tconst notify = () => listeners.forEach((l) => l());\n\n\t/** Fetches the catalog of a new provider in the background; the tool list changes when it lands. */\n\tconst catalogFor = (provider: EffectProvider | null): readonly EffectSummary[] | null => {\n\t\tif (!provider) return null;\n\t\tif (catalog?.provider !== provider) {\n\t\t\tconst entry: Catalog = { provider, effects: null };\n\t\t\tcatalog = entry;\n\t\t\tPromise.resolve()\n\t\t\t\t.then(() => provider.list())\n\t\t\t\t.then((effects) => {\n\t\t\t\t\tif (catalog === entry) {\n\t\t\t\t\t\tentry.effects = effects;\n\t\t\t\t\t\tnotify();\n\t\t\t\t\t}\n\t\t\t\t})\n\t\t\t\t.catch(() => {});\n\t\t}\n\t\treturn catalog.effects;\n\t};\n\n\tconst requireProvider = (): EffectProvider => {\n\t\tconst p = host.provider();\n\t\tif (!p) throw new Error(NO_PROVIDER);\n\t\treturn p;\n\t};\n\tconst writable = () => {\n\t\tconst reason = host.readOnlyReason();\n\t\tif (reason) throw new Error(reason);\n\t\treturn requireProvider();\n\t};\n\t/** Turns provider errors into messages a model can act on. */\n\tconst explain = async <T>(run: () => Promise<T>): Promise<T> => {\n\t\ttry {\n\t\t\treturn await run();\n\t\t} catch (error) {\n\t\t\tif (isEffectError(error)) throw new Error(error.message);\n\t\t\tthrow error;\n\t\t}\n\t};\n\n\tconst valuesSchema = (what: string, keyframes = false): JsonSchema => ({\n\t\ttype: 'object',\n\t\tdescription: `${what}: control name → 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.`\n\t});\n\tconst effectIdSchema = (uses: readonly EffectUse[]): JsonSchema => {\n\t\tconst effects = catalogFor(host.provider())?.filter((e) => e.kind !== 'motion' && e.uses.some((u) => uses.includes(u)));\n\t\tconst base = `An effect id from search_effects (use ${uses.map((u) => `\"${u}\"`).join(' or ')}).`;\n\t\treturn effects && effects.length && effects.length <= ENUM_LIMIT\n\t\t\t? { type: 'string', enum: effects.map((e) => e.id), description: `${base} One of: ${effects.map((e) => `${e.id} (${e.name})`).join(', ')}.` }\n\t\t\t: { type: 'string', minLength: 1, description: base };\n\t};\n\tconst context: EffectToolContext = { requireProvider: writable, valuesSchema, effectIdSchema, annotations: ANNOTATIONS };\n\n\tconst TARGET = { type: 'string', minLength: 1, description: host.target.id } as const;\n\tconst INSTANCE = { type: 'string', minLength: 1, description: `The effect's id on the ${host.target.noun}, from list_applied_effects or apply_effect.` } as const;\n\tconst INDEX = { type: 'integer', minimum: 0, description: `Place in the ${host.target.noun}'s effect stack: 0 is applied first (default: the end, applied last).` } as const;\n\tconst OPACITY = { type: 'number', minimum: 0, maximum: 1, description: 'How much of the effect shows over its input, 0–1 (default 1).' } as const;\n\tconst ENABLED = { type: 'boolean', description: 'false turns the effect off but keeps it (default true).' } as const;\n\n\tconst readTools = (): AgentTool[] => [\n\t\t{\n\t\t\tname: 'search_effects',\n\t\t\ttitle: 'Search effects',\n\t\t\tdescription: `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), category or use (${EFFECT_USES.join(', ')}). For this editor, apply_effect takes uses ${host.applyUses.join(' or ')}. Returns { provider, effects: [{ id, name, kind, category, uses, description, transparent, loop, duration (ms) }], total }. With no provider registered it returns available: false and the reason. Call describe_effect next for an effect's controls.`,\n\t\t\tinputSchema: object({\n\t\t\t\tquery: { type: 'string', description: 'Words to match (default: every effect).' },\n\t\t\t\tkind: { type: 'string', enum: [...EFFECT_KINDS], description: 'shader (pixels), vector (animated SVG) or motion (animates vector art).' },\n\t\t\t\tcategory: { type: 'string', minLength: 1, description: 'The provider\\'s category, e.g. \"gradient\", \"light\", \"transition\".' },\n\t\t\t\tuse: { 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.' },\n\t\t\t\tlimit: { type: 'integer', minimum: 1, maximum: 200, description: 'Most effects to return (default 20).' }\n\t\t\t}),\n\t\t\tannotations: { title: 'Search effects', ...ANNOTATIONS.read },\n\t\t\thandler: async (args) => {\n\t\t\t\tconst p = host.provider();\n\t\t\t\tif (!p) return { available: false, reason: NO_PROVIDER, effects: [], total: 0 };\n\t\t\t\tconst found = await explain(async () => p.search((args.query as string | undefined) ?? '', { kind: args.kind, category: args.category, use: args.use }));\n\t\t\t\tconst limit = (args.limit as number | undefined) ?? 20;\n\t\t\t\treturn { available: true, provider: { id: p.id, name: p.name, capabilities: p.capabilities }, effects: found.slice(0, limit).map(summaryOut), total: found.length };\n\t\t\t}\n\t\t},\n\t\t{\n\t\t\tname: 'describe_effect',\n\t\t\ttitle: 'Describe an effect',\n\t\t\tdescription: 'Returns one effect\\'s controls as a strict JSON Schema (each control described, with its type, range and units), its defaults, presets (named sets of values) and inputs, plus its summary. Use the control names and ranges for apply_effect and update_effect values. With no provider registered it returns available: false and the reason.',\n\t\t\tinputSchema: object({ effect: { type: 'string', minLength: 1, description: 'An effect id from search_effects or list_applied_effects.' } }, ['effect']),\n\t\t\tannotations: { title: 'Describe an effect', ...ANNOTATIONS.read },\n\t\t\thandler: async (args) => {\n\t\t\t\tconst p = host.provider();\n\t\t\t\tif (!p) return { available: false, reason: NO_PROVIDER };\n\t\t\t\tconst d: EffectDescription = await explain(async () => p.describe(args.effect as string));\n\t\t\t\treturn { available: true, ...summaryOut(d), controls: d.controls, defaults: d.defaults, presets: d.presets, inputs: d.inputs, appliesHere: d.kind !== 'motion' && d.uses.some((u) => host.applyUses.includes(u)) };\n\t\t\t}\n\t\t},\n\t\t{\n\t\t\tname: 'list_applied_effects',\n\t\t\ttitle: 'List applied effects',\n\t\t\tdescription: `Lists the effects in the document: for each ${host.target.noun} that has any (or the one given), its effects in order with id, effect id, name, role, enabled, opacity and saved values. available is false for an effect the current provider does not have (or when none is registered): it is kept and drawn as a placeholder, and can still be removed. Works without a provider.`,\n\t\t\tinputSchema: object({ target: { ...TARGET, description: `${host.target.id} Leave out to list every ${host.target.noun} with effects.` } }),\n\t\t\tannotations: { title: 'List applied effects', ...ANNOTATIONS.read },\n\t\t\thandler: async (args) => {\n\t\t\t\tconst p = host.provider();\n\t\t\t\tconst known = new Set((p ? await explain(async () => p.list()) : []).map((e) => e.id));\n\t\t\t\tconst targets = host.list(args.target as string | undefined).map((t) => ({ ...t, effects: t.effects.map((e) => ({ ...e, available: known.has(e.effect) })) }));\n\t\t\t\treturn { provider: p ? { id: p.id, name: p.name } : null, ...(p ? {} : { reason: NO_PROVIDER }), targets };\n\t\t\t}\n\t\t}\n\t];\n\n\tconst writeTools = (): AgentTool[] => [\n\t\t{\n\t\t\tname: 'apply_effect',\n\t\t\ttitle: 'Apply an effect',\n\t\t\tdescription: `Adds a provider effect to a ${host.target.noun}. ${host.applyDetails} values are checked against describe_effect's controls first; nothing changes if any is wrong. One undo step. Returns the effect with its new id (for update_effect and remove_effect) and the ${host.target.noun}'s effects. Refused when no provider is registered.`,\n\t\t\tinputSchema: object(\n\t\t\t\t{\n\t\t\t\t\ttarget: TARGET,\n\t\t\t\t\teffect: effectIdSchema(host.applyUses),\n\t\t\t\t\tvalues: valuesSchema('Control values over the effect\\'s defaults', true),\n\t\t\t\t\tindex: INDEX,\n\t\t\t\t\tenabled: ENABLED,\n\t\t\t\t\topacity: OPACITY\n\t\t\t\t},\n\t\t\t\t['target', 'effect']\n\t\t\t),\n\t\t\tannotations: { title: 'Apply an effect', ...ANNOTATIONS.write },\n\t\t\thandler: async (args) => {\n\t\t\t\twritable();\n\t\t\t\treturn explain(() => host.apply(args.target as string, args.effect as string, { values: args.values, index: args.index, enabled: args.enabled, opacity: args.opacity }));\n\t\t\t}\n\t\t},\n\t\t{\n\t\t\tname: 'update_effect',\n\t\t\ttitle: 'Change an applied effect',\n\t\t\tdescription: `Changes one applied effect on a ${host.target.noun}: its values (merged over the current ones; null resets a control to its default), enabled, opacity or place in the stack. Values are checked against describe_effect's controls first. One undo step. Returns the effect as it is now and the ${host.target.noun}'s effects. Refused when no provider is registered.`,\n\t\t\tinputSchema: object(\n\t\t\t\t{\n\t\t\t\t\ttarget: TARGET,\n\t\t\t\t\teffect: INSTANCE,\n\t\t\t\t\tvalues: { ...valuesSchema('Control values to change', true), description: `${valuesSchema('Control values to change', true).description} null resets a control to its default.` },\n\t\t\t\t\tenabled: ENABLED,\n\t\t\t\t\topacity: OPACITY,\n\t\t\t\t\tindex: { ...INDEX, description: `Move it to this place in the ${host.target.noun}'s stack (0 = applied first).` }\n\t\t\t\t},\n\t\t\t\t['target', 'effect']\n\t\t\t),\n\t\t\tannotations: { title: 'Change an applied effect', ...ANNOTATIONS.writeIdempotent },\n\t\t\thandler: async (args) => {\n\t\t\t\twritable();\n\t\t\t\tif (args.values === undefined && args.enabled === undefined && args.opacity === undefined && args.index === undefined) throw new Error('Give at least one of values, enabled, opacity or index to change.');\n\t\t\t\treturn explain(() => host.update(args.target as string, args.effect as string, { values: args.values, enabled: args.enabled, opacity: args.opacity, index: args.index }));\n\t\t\t}\n\t\t},\n\t\t{\n\t\t\tname: 'remove_effect',\n\t\t\ttitle: 'Remove an applied effect',\n\t\t\tdescription: `Removes one applied effect from a ${host.target.noun} (an effect the provider no longer has included). One undo step. Returns what was removed and the ${host.target.noun}'s remaining effects. Refused when no provider is registered.`,\n\t\t\tinputSchema: object({ target: TARGET, effect: INSTANCE }, ['target', 'effect']),\n\t\t\tannotations: { title: 'Remove an applied effect', ...ANNOTATIONS.destructive },\n\t\t\thandler: async (args) => {\n\t\t\t\twritable();\n\t\t\t\treturn explain(() => host.remove(args.target as string, args.effect as string));\n\t\t\t}\n\t\t}\n\t];\n\n\treturn {\n\t\tid: host.id,\n\t\t...(host.namespace ? { namespace: host.namespace } : {}),\n\t\ttools: () => {\n\t\t\tconst extra = host.extraTools?.(context) ?? [];\n\t\t\tif (host.readOnlyReason()) return [...readTools(), ...extra.filter((t) => t.annotations?.readOnlyHint)];\n\t\t\treturn [...readTools(), ...writeTools(), ...extra];\n\t\t},\n\t\tsubscribe: (l) => {\n\t\t\tlisteners.add(l);\n\t\t\tconst off = host.subscribe?.(l);\n\t\t\treturn () => {\n\t\t\t\tlisteners.delete(l);\n\t\t\t\toff?.();\n\t\t\t};\n\t\t}\n\t};\n}\n"]}
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
import {
|
|
2
2
|
isEffectError
|
|
3
|
-
} from "./chunk-
|
|
3
|
+
} from "./chunk-QEG3GRN2.js";
|
|
4
4
|
|
|
5
5
|
// src/effects/types.ts
|
|
6
6
|
var EFFECT_KINDS = ["shader", "vector", "motion"];
|
|
@@ -212,4 +212,4 @@ export {
|
|
|
212
212
|
EFFECT_USES,
|
|
213
213
|
createEffectToolProvider
|
|
214
214
|
};
|
|
215
|
-
//# sourceMappingURL=chunk-
|
|
215
|
+
//# sourceMappingURL=chunk-JRXJTPES.js.map
|
|
@@ -1,10 +1,11 @@
|
|
|
1
1
|
import {
|
|
2
2
|
EffectControlError,
|
|
3
|
+
EffectRendererPool,
|
|
3
4
|
EffectUnsupportedError,
|
|
4
5
|
EffectsSlot,
|
|
5
6
|
createEffectPixels,
|
|
6
7
|
effectValueErrors
|
|
7
|
-
} from "./chunk-
|
|
8
|
+
} from "./chunk-QEG3GRN2.js";
|
|
8
9
|
import {
|
|
9
10
|
validateJsonSchema
|
|
10
11
|
} from "./chunk-4FX5ACVG.js";
|
|
@@ -1213,7 +1214,7 @@ var VideoEffectsEngine = class {
|
|
|
1213
1214
|
cache = /* @__PURE__ */ new Map();
|
|
1214
1215
|
pending = /* @__PURE__ */ new Map();
|
|
1215
1216
|
failures = /* @__PURE__ */ new Map();
|
|
1216
|
-
|
|
1217
|
+
pool;
|
|
1217
1218
|
descriptions = /* @__PURE__ */ new Map();
|
|
1218
1219
|
listeners = /* @__PURE__ */ new Set();
|
|
1219
1220
|
maxEntries;
|
|
@@ -1224,6 +1225,7 @@ var VideoEffectsEngine = class {
|
|
|
1224
1225
|
this.slot = options.slot ?? new EffectsSlot();
|
|
1225
1226
|
this.maxEntries = options.maxEntries ?? 120;
|
|
1226
1227
|
this.toImage = options.toImage ?? defaultToImage;
|
|
1228
|
+
this.pool = new EffectRendererPool({ maxRenderers: options.maxRenderers });
|
|
1227
1229
|
this.detach = this.slot.subscribe(() => {
|
|
1228
1230
|
this.reset();
|
|
1229
1231
|
this.notify();
|
|
@@ -1324,20 +1326,9 @@ var VideoEffectsEngine = class {
|
|
|
1324
1326
|
}
|
|
1325
1327
|
return d;
|
|
1326
1328
|
}
|
|
1327
|
-
/** Renders
|
|
1329
|
+
/** Renders through the pool: a few live renderers, one render at a time on each. */
|
|
1328
1330
|
render(provider, id, width, height, request) {
|
|
1329
|
-
|
|
1330
|
-
let r = this.renderers.get(k);
|
|
1331
|
-
if (!r) {
|
|
1332
|
-
const renderer = Promise.resolve().then(() => provider.createRenderer(id, { width, height }));
|
|
1333
|
-
renderer.catch(() => this.renderers.delete(k));
|
|
1334
|
-
r = { renderer, queue: Promise.resolve() };
|
|
1335
|
-
this.renderers.set(k, r);
|
|
1336
|
-
}
|
|
1337
|
-
const entry = r;
|
|
1338
|
-
const next = entry.queue.then(() => entry.renderer).then((renderer) => renderer.render(request));
|
|
1339
|
-
entry.queue = next.catch(() => void 0);
|
|
1340
|
-
return next;
|
|
1331
|
+
return this.pool.render(provider, id, { width, height }, request);
|
|
1341
1332
|
}
|
|
1342
1333
|
/** Forgets every picture and failure and disposes the renderers (e.g. after the provider changes). */
|
|
1343
1334
|
reset() {
|
|
@@ -1346,9 +1337,7 @@ var VideoEffectsEngine = class {
|
|
|
1346
1337
|
this.pending.clear();
|
|
1347
1338
|
this.failures.clear();
|
|
1348
1339
|
this.descriptions.clear();
|
|
1349
|
-
|
|
1350
|
-
});
|
|
1351
|
-
this.renderers.clear();
|
|
1340
|
+
this.pool.clear();
|
|
1352
1341
|
}
|
|
1353
1342
|
subscribe(listener) {
|
|
1354
1343
|
this.listeners.add(listener);
|
|
@@ -2389,4 +2378,4 @@ export {
|
|
|
2389
2378
|
toVideoFfmpeg,
|
|
2390
2379
|
toVideoLoudnessMeasure
|
|
2391
2380
|
};
|
|
2392
|
-
//# sourceMappingURL=chunk-
|
|
2381
|
+
//# sourceMappingURL=chunk-ORHMVUR6.js.map
|