@pixerate/editor 0.231.1 → 0.233.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (62) hide show
  1. package/dist/animation-editor/compile.d.cts +3 -0
  2. package/dist/animation-editor/compile.d.ts +3 -0
  3. package/dist/animation-editor/controller.d.cts +67 -2
  4. package/dist/animation-editor/controller.d.ts +67 -2
  5. package/dist/animation-editor/edit.d.cts +1 -1
  6. package/dist/animation-editor/edit.d.ts +1 -1
  7. package/dist/animation-editor/evaluate.d.cts +8 -1
  8. package/dist/animation-editor/evaluate.d.ts +8 -1
  9. package/dist/animation-editor/index.cjs +1202 -371
  10. package/dist/animation-editor/index.d.cts +3 -0
  11. package/dist/animation-editor/index.d.ts +3 -0
  12. package/dist/animation-editor/index.js +1032 -201
  13. package/dist/animation-editor/issues.d.cts +28 -0
  14. package/dist/animation-editor/issues.d.ts +28 -0
  15. package/dist/animation-editor/layer-edit.d.cts +55 -0
  16. package/dist/animation-editor/layer-edit.d.ts +55 -0
  17. package/dist/animation-editor/layer-tools.d.cts +11 -0
  18. package/dist/animation-editor/layer-tools.d.ts +11 -0
  19. package/dist/animation-editor/layers.d.cts +166 -0
  20. package/dist/animation-editor/layers.d.ts +166 -0
  21. package/dist/animation-editor/parse.d.cts +2 -0
  22. package/dist/animation-editor/parse.d.ts +2 -0
  23. package/dist/animation-editor/types.d.cts +44 -1
  24. package/dist/animation-editor/types.d.ts +44 -1
  25. package/dist/canvas/index.cjs +10 -10
  26. package/dist/canvas/index.js +4 -4
  27. package/dist/chunk-6RZVFYGJ.cjs +255 -0
  28. package/dist/{chunk-C73U5UI3.cjs → chunk-A7PQNIBX.cjs} +14 -14
  29. package/dist/{chunk-ERJA5CGT.cjs → chunk-DGWOGQJ5.cjs} +8 -1
  30. package/dist/{chunk-TYCJGFCF.js → chunk-EUKY4KFX.js} +8 -1
  31. package/dist/chunk-I277KJLQ.js +255 -0
  32. package/dist/{chunk-OPSK22DL.cjs → chunk-JAOSL334.cjs} +24 -7
  33. package/dist/{chunk-TCZ2J7PR.js → chunk-SIK3LT6M.js} +26 -9
  34. package/dist/{chunk-SBPGIZQN.js → chunk-TEFQISBY.js} +1 -1
  35. package/dist/{chunk-CSM5T6SE.js → chunk-WDDUPWVG.js} +1 -1
  36. package/dist/{chunk-5T3H7CGG.cjs → chunk-ZQIWBJGH.cjs} +7 -7
  37. package/dist/effects/conformance.d.cts +4 -1
  38. package/dist/effects/conformance.d.ts +4 -1
  39. package/dist/effects/errors.d.cts +6 -1
  40. package/dist/effects/errors.d.ts +6 -1
  41. package/dist/effects/fake-templates.d.cts +17 -0
  42. package/dist/effects/fake-templates.d.ts +17 -0
  43. package/dist/effects/fake.d.cts +6 -1
  44. package/dist/effects/fake.d.ts +6 -1
  45. package/dist/effects/index.cjs +322 -67
  46. package/dist/effects/index.d.cts +1 -0
  47. package/dist/effects/index.d.ts +1 -0
  48. package/dist/effects/index.js +258 -3
  49. package/dist/effects/templates.d.cts +56 -0
  50. package/dist/effects/templates.d.ts +56 -0
  51. package/dist/effects/types.d.cts +212 -0
  52. package/dist/effects/types.d.ts +212 -0
  53. package/dist/image-editor/index.cjs +16 -16
  54. package/dist/image-editor/index.js +4 -4
  55. package/dist/paint-editor/index.cjs +10 -10
  56. package/dist/paint-editor/index.js +5 -5
  57. package/dist/vector/index.cjs +6 -6
  58. package/dist/vector/index.js +1 -1
  59. package/dist/video-editor/index.cjs +339 -339
  60. package/dist/video-editor/index.js +5 -5
  61. package/package.json +1 -1
  62. package/skills/pixerate-animation-editor/SKILL.md +37 -0
@@ -1,4 +1,4 @@
1
- import type { CameraMoveSpec, EffectValue } from '../effects/types.js';
1
+ import type { CameraMoveSpec, EffectLayer, EffectShotTransition, EffectValue } from '../effects/types.js';
2
2
  import type { AnimationEasing } from '../animation/easing.js';
3
3
  import type { AnimationLoopMode } from '../animation/evaluate.js';
4
4
  import type { VectorDocument } from '../vector/types.js';
@@ -153,6 +153,49 @@ export interface MotionShot {
153
153
  * durations, staggers and hold all scale by 1 / speed. `fit_animation` sets it to fit a length.
154
154
  */
155
155
  speed?: number;
156
+ /**
157
+ * Provider effects and pictures shown with the shot, bottom to top (see `MotionShotLayer`):
158
+ * `background` layers under the artwork, `subject` and `overlay` layers over it.
159
+ */
160
+ layers?: MotionShotLayer[];
161
+ /**
162
+ * How the shot before gives way to this one: a provider transition effect blends the last
163
+ * frame of the shot before into this shot over `ms`. With no camera `in`, the camera cuts.
164
+ */
165
+ transition?: MotionShotTransition;
166
+ }
167
+ /** What a shot layer shows or feeds its effect: a picture or SVG (URL, `data:` URL or markup), words, or a `slot` the host fills. */
168
+ export type MotionLayerSubject = {
169
+ image: string;
170
+ } | {
171
+ svg: string;
172
+ } | {
173
+ text: string;
174
+ font?: string;
175
+ weight?: number | string;
176
+ } | {
177
+ slot: string;
178
+ };
179
+ /**
180
+ * A layer of a shot (the shape of Flair's `SceneLayer` and the effects contract's
181
+ * `EffectLayer`): a provider effect (a generator, filter, overlay or title), optionally applied
182
+ * to a `subject`, or a bare subject. It lasts from `from` to `until` (default: from the shot's
183
+ * start until the next shot starts), placed at `rect` in artwork units (moving with the camera)
184
+ * or filling the screen.
185
+ */
186
+ export interface MotionShotLayer extends Omit<EffectLayer, 'id' | 'subject'> {
187
+ /** Unique in the scene (e.g. `layer1`). */
188
+ id: string;
189
+ subject?: MotionLayerSubject;
190
+ /** The provider it was added with (its `id`). */
191
+ provider?: string;
192
+ /** The effect's name when it was added, shown where no provider can draw it. */
193
+ name?: string;
194
+ }
195
+ /** A provider transition into a shot (the effects contract's `EffectShotTransition`). */
196
+ export interface MotionShotTransition extends EffectShotTransition {
197
+ provider?: string;
198
+ name?: string;
156
199
  }
157
200
  /** How `split` splits outlined text. */
158
201
  export type MotionTextUnit = 'letter' | 'word' | 'line';
@@ -117,9 +117,6 @@ var _chunkT7QSEKDYcjs = require('../chunk-T7QSEKDY.cjs');
117
117
  var _chunkSVFEVKLFcjs = require('../chunk-SVFEVKLF.cjs');
118
118
 
119
119
 
120
- var _chunkPY4GWSE2cjs = require('../chunk-PY4GWSE2.cjs');
121
-
122
-
123
120
 
124
121
  var _chunkN2FGYZ4Qcjs = require('../chunk-N2FGYZ4Q.cjs');
125
122
 
@@ -217,10 +214,13 @@ var _chunkGF2RBSDMcjs = require('../chunk-GF2RBSDM.cjs');
217
214
  var _chunkNM3G4VQTcjs = require('../chunk-NM3G4VQT.cjs');
218
215
 
219
216
 
217
+ var _chunkPY4GWSE2cjs = require('../chunk-PY4GWSE2.cjs');
218
+
219
+
220
220
 
221
221
 
222
222
 
223
- var _chunkOPSK22DLcjs = require('../chunk-OPSK22DL.cjs');
223
+ var _chunkJAOSL334cjs = require('../chunk-JAOSL334.cjs');
224
224
  require('../chunk-WOFXZ3XU.cjs');
225
225
 
226
226
 
@@ -11425,14 +11425,14 @@ function canvasEffectErrorReason(effect, error) {
11425
11425
  }
11426
11426
  var CANVAS_EFFECT_MISSING_PATTERN = "repeating-linear-gradient(45deg, rgba(128,128,128,0.28) 0 2px, transparent 2px 8px)";
11427
11427
  function createCanvasEffectsController(options) {
11428
- const slot = new (0, _chunkOPSK22DLcjs.EffectsSlot)();
11428
+ const slot = new (0, _chunkJAOSL334cjs.EffectsSlot)();
11429
11429
  if (options.effects) slot.set(options.effects);
11430
11430
  const describe = async (effect, place2) => {
11431
11431
  const d = await slot.require().describe(effect);
11432
11432
  const allowed = CANVAS_EFFECT_PLACES[place2];
11433
11433
  if (d.kind === "motion" || !d.uses.some((u) => allowed.includes(u))) {
11434
11434
  const where = place2 === "transition" ? "a scene transition" : place2 === "fill" ? "a fill" : "an overlay";
11435
- throw new (0, _chunkOPSK22DLcjs.EffectUnsupportedError)(`"${d.id}" (${d.name}) is a ${d.uses.join(" / ")} effect and cannot be ${where}, which takes ${allowed.join(" or ")} effects; search_effects with use "${allowed[0]}" lists ones that fit.`, d.id);
11435
+ throw new (0, _chunkJAOSL334cjs.EffectUnsupportedError)(`"${d.id}" (${d.name}) is a ${d.uses.join(" / ")} effect and cannot be ${where}, which takes ${allowed.join(" or ")} effects; search_effects with use "${allowed[0]}" lists ones that fit.`, d.id);
11436
11436
  }
11437
11437
  return d;
11438
11438
  };
@@ -11463,7 +11463,7 @@ function createCanvasEffectsController(options) {
11463
11463
  else if (k in d.controls.properties) base[k] = d.defaults[k];
11464
11464
  else throw new Error(`"${k}" is not a control of "${d.id}"; its controls are ${Object.keys(d.controls.properties).join(", ")}.`);
11465
11465
  }
11466
- return { ...base, ..._chunkOPSK22DLcjs.resolveEffectValues.call(void 0, { ...d, defaults: base }, given) };
11466
+ return { ...base, ..._chunkJAOSL334cjs.resolveEffectValues.call(void 0, { ...d, defaults: base }, given) };
11467
11467
  };
11468
11468
  return {
11469
11469
  /** The provider slot. */
@@ -11497,7 +11497,7 @@ function createCanvasEffectsController(options) {
11497
11497
  const mode = _nullishCoalesce(opts.mode, () => ( "fill"));
11498
11498
  const d = await describe(effect, mode);
11499
11499
  if (opts.opacity !== void 0 && !(opts.opacity >= 0 && opts.opacity <= 1)) throw new Error("opacity must be from 0 to 1.");
11500
- const next = { effect: d.id, provider: slot.require().id, name: d.name, values: _chunkOPSK22DLcjs.resolveEffectValues.call(void 0, d, opts.values), mode, ...opts.opacity !== void 0 && opts.opacity !== 1 ? { opacity: opts.opacity } : {} };
11500
+ const next = { effect: d.id, provider: slot.require().id, name: d.name, values: _chunkJAOSL334cjs.resolveEffectValues.call(void 0, d, opts.values), mode, ...opts.opacity !== void 0 && opts.opacity !== 1 ? { opacity: opts.opacity } : {} };
11501
11501
  commit(`Add effect ${d.name}`, withEffect(nodeId, next));
11502
11502
  return next;
11503
11503
  },
@@ -11554,7 +11554,7 @@ function createCanvasEffectsController(options) {
11554
11554
  const d = await describe(effect, "transition");
11555
11555
  const duration = _nullishCoalesce(_nullishCoalesce(opts.duration, () => ( d.duration)), () => ( 600));
11556
11556
  if (!(duration > 0)) throw new Error("duration must be a number of ms above 0.");
11557
- const transition = { effect: d.id, provider: slot.require().id, name: d.name, values: _chunkOPSK22DLcjs.resolveEffectValues.call(void 0, d, opts.values), duration };
11557
+ const transition = { effect: d.id, provider: slot.require().id, name: d.name, values: _chunkJAOSL334cjs.resolveEffectValues.call(void 0, d, opts.values), duration };
11558
11558
  scenes2.update(sceneId, { transition });
11559
11559
  return transition;
11560
11560
  }
@@ -11698,7 +11698,7 @@ function createCanvasEffectsToolProvider(controller, options = {}) {
11698
11698
  const once = (instance) => {
11699
11699
  if (instance !== "effect") throw new Error('A shape node has one effect, "effect".');
11700
11700
  };
11701
- return _chunkOPSK22DLcjs.createEffectToolProvider.call(void 0, {
11701
+ return _chunkJAOSL334cjs.createEffectToolProvider.call(void 0, {
11702
11702
  id: _nullishCoalesce(options.id, () => ( "canvas-effects")),
11703
11703
  namespace: options.namespace,
11704
11704
  title: "Canvas effects",
@@ -115,9 +115,6 @@ import {
115
115
  CAPTURE_OVERLAY_SELECTORS,
116
116
  rasterizeElement
117
117
  } from "../chunk-XCK7QK5D.js";
118
- import {
119
- EffectRendererPool
120
- } from "../chunk-EHLOCVWB.js";
121
118
  import {
122
119
  createCaptureToolProvider,
123
120
  unionCaptureRects
@@ -215,12 +212,15 @@ import {
215
212
  shapeToPolygon,
216
213
  simplifyRing
217
214
  } from "../chunk-PWBZH3FK.js";
215
+ import {
216
+ EffectRendererPool
217
+ } from "../chunk-EHLOCVWB.js";
218
218
  import {
219
219
  EffectUnsupportedError,
220
220
  EffectsSlot,
221
221
  createEffectToolProvider,
222
222
  resolveEffectValues
223
- } from "../chunk-TCZ2J7PR.js";
223
+ } from "../chunk-SIK3LT6M.js";
224
224
  import "../chunk-V2ZCJRDM.js";
225
225
  import {
226
226
  brushstrokeScaleBucket
@@ -0,0 +1,255 @@
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
+
3
+
4
+ var _chunkJAOSL334cjs = require('./chunk-JAOSL334.cjs');
5
+
6
+ // src/effects/templates.ts
7
+ var SLOT_NAME = /^[A-Za-z][\w-]*$/;
8
+ var COLOR = /^#[0-9a-fA-F]{6}$/;
9
+ var EFFECT_SEQUENCE_NAME = /^[a-z0-9]+(?:-[a-z0-9]+)*$/;
10
+ var isObject = (v) => !!v && typeof v === "object" && !Array.isArray(v);
11
+ var isSubjectTemplateSlot = (slot) => slot.type === "image" || slot.type === "video" || slot.type === "svg";
12
+ function effectSubjectKind(subject) {
13
+ if (!isObject(subject)) return void 0;
14
+ for (const k of ["slot", "image", "video", "svg", "text"]) if (typeof subject[k] === "string") return k;
15
+ return void 0;
16
+ }
17
+ var typeName = (value) => value === null ? "null" : Array.isArray(value) ? "a list" : typeof value === "object" ? "an object" : typeof value === "string" ? `the words ${JSON.stringify(value.length > 40 ? `${value.slice(0, 40)}\u2026` : value)}` : `${typeof value === "number" ? "the number" : "the value"} ${String(value)}`;
18
+ function range(slot) {
19
+ const whole = slot.integer ? "whole number" : "number";
20
+ if (slot.min !== void 0 && slot.max !== void 0) return `a ${whole} from ${slot.min} to ${slot.max}`;
21
+ if (slot.min !== void 0) return `a ${whole} of at least ${slot.min}`;
22
+ if (slot.max !== void 0) return `a ${whole} of at most ${slot.max}`;
23
+ return `a ${whole}`;
24
+ }
25
+ function templateSlotTakes(slot) {
26
+ switch (slot.type) {
27
+ case "text":
28
+ return `words${slot.maxLength !== void 0 ? ` (up to ${slot.maxLength} characters)` : ""}`;
29
+ case "color":
30
+ return `a colour written #RRGGBB, like "${slot.default}"`;
31
+ case "number":
32
+ return range(slot);
33
+ case "video":
34
+ return "a video URL";
35
+ case "svg":
36
+ return "SVG markup or an SVG URL";
37
+ default:
38
+ return "a picture: a URL, a data: URL or <svg> markup";
39
+ }
40
+ }
41
+ function templateValueProblem(slot, value) {
42
+ const no = (extra = "") => `Slot "${slot.name}" takes ${templateSlotTakes(slot)}${extra}, not ${typeName(value)}.`;
43
+ switch (slot.type) {
44
+ case "text":
45
+ if (typeof value !== "string") return no();
46
+ if (slot.maxLength !== void 0 && value.length > slot.maxLength) return `Slot "${slot.name}" takes up to ${slot.maxLength} characters; the value has ${value.length}. Shorten it.`;
47
+ return void 0;
48
+ case "color":
49
+ return typeof value === "string" && COLOR.test(value) ? void 0 : no();
50
+ case "number":
51
+ if (typeof value !== "number" || !Number.isFinite(value)) return no();
52
+ if (slot.integer && !Number.isInteger(value)) return `Slot "${slot.name}" takes ${range(slot)}; ${value} is not whole.`;
53
+ if (slot.min !== void 0 && value < slot.min || slot.max !== void 0 && value > slot.max) return `Slot "${slot.name}" takes ${range(slot)}; ${value} is out of range.`;
54
+ return void 0;
55
+ default: {
56
+ const host = ` (or { "slot": "${slot.name}" } to leave it for the host)`;
57
+ if (typeof value === "string") return value.trim() ? void 0 : no(host);
58
+ const kind = effectSubjectKind(value);
59
+ if (!kind) return no(host);
60
+ if (kind === "slot") return SLOT_NAME.test(value.slot) ? void 0 : `Slot "${slot.name}" left for the host is { "slot": name }, the name letters, digits, "-" and "_", starting with a letter.`;
61
+ const fits = kind === slot.type || slot.type === "image" && kind === "svg";
62
+ if (!fits) return `Slot "${slot.name}" takes { "${slot.type}": \u2026 }${slot.type === "image" ? ' or { "svg": \u2026 }' : ""}, not { "${kind}": \u2026 }.`;
63
+ return value[kind].trim() ? void 0 : `Slot "${slot.name}"'s "${kind}" is a non-empty string.`;
64
+ }
65
+ }
66
+ }
67
+ var slotList = (slots) => slots.length ? `the slots are ${slots.map((s) => `${s.name} (${s.type})`).join(", ")}` : "the template has no slots";
68
+ function checkTemplateValues(slots, values) {
69
+ if (values === void 0) return [];
70
+ if (!isObject(values)) return [`Values are an object of slot names to values; ${slotList(slots)}.`];
71
+ const byName = new Map(slots.map((s) => [s.name, s]));
72
+ const errors = [];
73
+ for (const [name, value] of Object.entries(values)) {
74
+ const slot = byName.get(name);
75
+ if (!slot) errors.push(`There is no slot "${name}"; ${slotList(slots)}.`);
76
+ else if (value !== void 0) {
77
+ const problem = templateValueProblem(slot, value);
78
+ if (problem) errors.push(problem);
79
+ }
80
+ }
81
+ return errors;
82
+ }
83
+ function templateSlotSchema(slot) {
84
+ const about = `${slot.label}${slot.description ? `: ${slot.description}` : ""}.`;
85
+ switch (slot.type) {
86
+ case "text":
87
+ return { type: "string", ...slot.maxLength !== void 0 ? { maxLength: slot.maxLength } : {}, description: `${about} Words${slot.maxLength !== void 0 ? `, up to ${slot.maxLength} characters` : ""}. Default ${JSON.stringify(slot.default)}.` };
88
+ case "color":
89
+ return { type: "string", pattern: COLOR.source, description: `${about} A colour as #RRGGBB. Default ${slot.default}.` };
90
+ case "number":
91
+ return {
92
+ type: slot.integer ? "integer" : "number",
93
+ ...slot.min !== void 0 ? { minimum: slot.min } : {},
94
+ ...slot.max !== void 0 ? { maximum: slot.max } : {},
95
+ description: `${about} ${range(slot).replace(/^a /, "A ")}. Default ${slot.default}.`
96
+ };
97
+ default: {
98
+ const key = slot.type;
99
+ const object = (k, what) => ({ type: "object", properties: { [k]: { type: "string", description: what } }, required: [k], additionalProperties: false, description: `{ "${k}": \u2026 }` });
100
+ return {
101
+ anyOf: [
102
+ { type: "string", minLength: 1, description: key === "video" ? "A video URL." : key === "svg" ? "SVG markup or an SVG URL." : "A picture URL, a data: URL or <svg> markup." },
103
+ object(key, key === "video" ? "A video URL." : key === "svg" ? "SVG markup or an SVG URL." : "A picture URL or a data: URL."),
104
+ ...key === "image" ? [object("svg", "SVG markup.")] : [],
105
+ object("slot", "A slot name, to leave the subject for the host to fill.")
106
+ ],
107
+ description: `${about} ${templateSlotTakes(slot).replace(/^a /, "A ")}. ${slot.hasDefault ? "Default: a placeholder picture." : "No default: left out, the host fills it."}`
108
+ };
109
+ }
110
+ }
111
+ }
112
+ function templateValuesSchema(slots) {
113
+ return {
114
+ type: "object",
115
+ properties: Object.fromEntries(slots.map((s) => [s.name, templateSlotSchema(s)])),
116
+ additionalProperties: false,
117
+ description: `Slot values by name; slots left out take their defaults. ${slots.length ? `Slots: ${slots.map((s) => s.name).join(", ")}.` : "This template has no slots."}`
118
+ };
119
+ }
120
+ function templateDefaultValues(slots) {
121
+ const out = {};
122
+ for (const s of slots) if (!isSubjectTemplateSlot(s)) out[s.name] = s.default;
123
+ return out;
124
+ }
125
+ function templateSlotSummary(slot) {
126
+ const fallback = isSubjectTemplateSlot(slot) ? slot.hasDefault ? "a placeholder by default" : "the host fills it when none is given" : `default ${JSON.stringify(slot.default)}`;
127
+ return `${slot.name} (${slot.type}): ${slot.label}; takes ${templateSlotTakes(slot)}; ${fallback}.`;
128
+ }
129
+ function searchTemplateSummaries(catalog, options = {}) {
130
+ const words = (_nullishCoalesce(options.query, () => ( ""))).toLowerCase().split(/\s+/).filter(Boolean);
131
+ const scored = [];
132
+ for (const t of catalog) {
133
+ if (!_chunkJAOSL334cjs.hasEffectTags.call(void 0, t, options.tags)) continue;
134
+ let score = 0;
135
+ const name = t.name.toLowerCase();
136
+ const rest = [t.id, ..._nullishCoalesce(t.tags, () => ( [])), t.description].join(" ").toLowerCase();
137
+ if (!words.every((w) => name.includes(w) || rest.includes(w))) continue;
138
+ for (const w of words) score += name.includes(w) ? 2 : 1;
139
+ scored.push({ t, score });
140
+ }
141
+ scored.sort((a, b) => b.score - a.score);
142
+ const out = scored.map((s) => s.t);
143
+ return options.limit !== void 0 ? out.slice(0, Math.max(0, options.limit)) : out;
144
+ }
145
+ var effectLayerRole = (layer) => _nullishCoalesce(layer.role, () => ( (layer.rect ? "subject" : "background")));
146
+ function effectLayerInput(description) {
147
+ const images = description.inputs.filter((i) => i.kind === "image");
148
+ return _optionalChain([(_nullishCoalesce(images.find((i) => i.name === "source"), () => ( images.find((i) => i.name !== "from" && i.name !== "to")))), 'optionalAccess', _ => _.name]);
149
+ }
150
+ var EFFECT_LAYER_USES = ["generator", "filter", "overlay", "title"];
151
+ var effectLayerOffered = (e) => (e.kind === "shader" || e.kind === "vector") && e.uses.some((u) => EFFECT_LAYER_USES.includes(u));
152
+ var isRect = (r) => isObject(r) && ["x", "y", "width", "height"].every((k) => Number.isFinite(r[k])) && r.width > 0 && r.height > 0;
153
+ var isLayerTime = (t) => typeof t === "number" && Number.isFinite(t) && t >= 0 || isObject(t) && Number.isInteger(t.shots) && t.shots >= 1;
154
+ function effectSubjectProblem(subject) {
155
+ const kind = effectSubjectKind(subject);
156
+ if (!kind) return `a subject is one of { image }, { video }, { svg }, { text } or { slot }, not ${typeName(subject)}`;
157
+ if (!subject[kind].trim()) return `a subject's "${kind}" is a non-empty string`;
158
+ return void 0;
159
+ }
160
+ function checkEffectSequence(sequence, catalog) {
161
+ const problems = [];
162
+ if (!isObject(sequence)) return ["A sequence is an object with name, stage and shots."];
163
+ if (typeof sequence.name !== "string" || !sequence.name) problems.push("The sequence needs a name.");
164
+ const stage = sequence.stage;
165
+ if (!isObject(stage) || !(Number(stage.width) > 0) || !(Number(stage.height) > 0)) problems.push("The sequence needs a stage { width, height } in pixels, both > 0.");
166
+ if (!Array.isArray(sequence.shots) || !sequence.shots.length) return [...problems, "The sequence needs at least one shot."];
167
+ const byId = catalog ? new Map(catalog.map((e) => [e.id, e])) : void 0;
168
+ const ids = /* @__PURE__ */ new Set();
169
+ sequence.shots.forEach((shot, i) => {
170
+ const at = `shot ${i + 1}`;
171
+ if (!isObject(shot)) return void problems.push(`${at} is not an object.`);
172
+ if (!(Number.isFinite(shot.hold) && shot.hold >= 0)) problems.push(`${at}: hold must be ms \u2265 0.`);
173
+ for (const k of ["title", "caption"]) if (shot[k] !== void 0 && typeof shot[k] !== "string") problems.push(`${at}: ${k} must be words.`);
174
+ if (shot.camera !== void 0) {
175
+ const camera = shot.camera;
176
+ if (!isObject(camera)) problems.push(`${at}: camera must be an object.`);
177
+ else if (camera.framing !== void 0 && !isRect(camera.framing)) problems.push(`${at}: camera.framing must be { x, y, width, height } with width and height > 0.`);
178
+ }
179
+ if (!Array.isArray(shot.layers)) problems.push(`${at}: layers must be a list (it may be empty).`);
180
+ else
181
+ shot.layers.forEach((layer, j) => {
182
+ const where = `${at}, layer ${j + 1}`;
183
+ if (!isObject(layer)) return void problems.push(`${where} is not an object.`);
184
+ if (layer.id !== void 0) {
185
+ if (typeof layer.id !== "string" || !layer.id) problems.push(`${where}: id must be a non-empty string.`);
186
+ else if (ids.has(layer.id)) problems.push(`${where}: id "${layer.id}" is used twice.`);
187
+ else ids.add(layer.id);
188
+ }
189
+ if (layer.effect === void 0 && layer.subject === void 0) problems.push(`${where} has neither an effect nor a subject.`);
190
+ if (layer.effect !== void 0 && (typeof layer.effect !== "string" || !layer.effect)) problems.push(`${where}: effect must be an effect id.`);
191
+ else if (typeof layer.effect === "string" && byId) {
192
+ const e = byId.get(layer.effect);
193
+ if (!e) problems.push(`${where}: the provider has no effect "${layer.effect}".`);
194
+ else if (!effectLayerOffered(e)) problems.push(`${where}: "${layer.effect}" (${e.kind}, ${e.uses.join("/")}) cannot be a layer; a layer's effect is a generator, filter, overlay or title.`);
195
+ }
196
+ if (layer.subject !== void 0) {
197
+ const p = effectSubjectProblem(layer.subject);
198
+ if (p) problems.push(`${where}: ${p}.`);
199
+ }
200
+ if (layer.role !== void 0 && !_chunkJAOSL334cjs.EFFECT_LAYER_ROLES.includes(layer.role)) problems.push(`${where}: role is one of ${_chunkJAOSL334cjs.EFFECT_LAYER_ROLES.join(", ")}.`);
201
+ if (layer.rect !== void 0 && !isRect(layer.rect)) problems.push(`${where}: rect must be { x, y, width, height } with width and height > 0.`);
202
+ 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.`);
203
+ if (layer.opacity !== void 0 && !(typeof layer.opacity === "number" && layer.opacity >= 0 && layer.opacity <= 1)) problems.push(`${where}: opacity is 0\u20131.`);
204
+ if (layer.fade !== void 0 && !(typeof layer.fade === "number" && layer.fade >= 0)) problems.push(`${where}: fade is ms \u2265 0.`);
205
+ if (layer.values !== void 0 && !isObject(layer.values)) problems.push(`${where}: values must be an object of control values.`);
206
+ });
207
+ if (shot.transition !== void 0) {
208
+ const t = shot.transition;
209
+ if (i === 0) problems.push(`${at} has a transition, but there is no shot before it.`);
210
+ if (!isObject(t) || typeof t.effect !== "string" || !t.effect) problems.push(`${at}: transition needs an effect id.`);
211
+ else {
212
+ if (t.ms !== void 0 && !(typeof t.ms === "number" && t.ms > 0)) problems.push(`${at}: transition ms must be > 0.`);
213
+ const e = _optionalChain([byId, 'optionalAccess', _2 => _2.get, 'call', _3 => _3(t.effect)]);
214
+ if (byId && !e) problems.push(`${at}: the provider has no transition "${t.effect}".`);
215
+ else if (e && !e.uses.includes("transition")) problems.push(`${at}: "${t.effect}" is not a transition (its uses are ${e.uses.join(", ")}).`);
216
+ }
217
+ }
218
+ });
219
+ return problems;
220
+ }
221
+ function effectSequenceEffects(sequence) {
222
+ const out = /* @__PURE__ */ new Set();
223
+ for (const shot of sequence.shots) {
224
+ for (const l of shot.layers) if (l.effect) out.add(l.effect);
225
+ if (shot.transition) out.add(shot.transition.effect);
226
+ }
227
+ return [...out];
228
+ }
229
+ function effectSequenceSlots(sequence) {
230
+ const out = /* @__PURE__ */ new Set();
231
+ for (const shot of sequence.shots) for (const l of shot.layers) if (l.subject && "slot" in l.subject) out.add(l.subject.slot);
232
+ return [...out];
233
+ }
234
+
235
+
236
+
237
+
238
+
239
+
240
+
241
+
242
+
243
+
244
+
245
+
246
+
247
+
248
+
249
+
250
+
251
+
252
+
253
+
254
+
255
+ exports.EFFECT_SEQUENCE_NAME = EFFECT_SEQUENCE_NAME; exports.isSubjectTemplateSlot = isSubjectTemplateSlot; exports.effectSubjectKind = effectSubjectKind; exports.templateSlotTakes = templateSlotTakes; exports.templateValueProblem = templateValueProblem; exports.checkTemplateValues = checkTemplateValues; exports.templateSlotSchema = templateSlotSchema; exports.templateValuesSchema = templateValuesSchema; exports.templateDefaultValues = templateDefaultValues; exports.templateSlotSummary = templateSlotSummary; exports.searchTemplateSummaries = searchTemplateSummaries; exports.effectLayerRole = effectLayerRole; exports.effectLayerInput = effectLayerInput; exports.EFFECT_LAYER_USES = EFFECT_LAYER_USES; exports.effectLayerOffered = effectLayerOffered; exports.effectSubjectProblem = effectSubjectProblem; exports.checkEffectSequence = checkEffectSequence; exports.effectSequenceEffects = effectSequenceEffects; exports.effectSequenceSlots = effectSequenceSlots;
@@ -3,7 +3,7 @@
3
3
 
4
4
 
5
5
 
6
- var _chunkOPSK22DLcjs = require('./chunk-OPSK22DL.cjs');
6
+ var _chunkJAOSL334cjs = require('./chunk-JAOSL334.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(..._chunkOPSK22DLcjs.effectValueErrors.call(void 0, description, doc.values).map((e) => ({ ...e, path: `/values${e.path}` })));
29
+ else errors.push(..._chunkJAOSL334cjs.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(..._chunkOPSK22DLcjs.effectValueErrors.call(void 0, description, { [control]: kf.value }).map((e) => ({ ...e, path: `${at}/${i}/value` })));
42
+ errors.push(..._chunkJAOSL334cjs.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(..._chunkOPSK22DLcjs.effectValueErrors.call(void 0, description, p.values).map((e) => ({ ...e, path: `/presets/${i}/values${e.path}` })));
52
+ errors.push(..._chunkJAOSL334cjs.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, _chunkOPSK22DLcjs.EffectUnsupportedError)("Invalid effect authoring document: version 1 required");
173
+ throw new (0, _chunkJAOSL334cjs.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, _chunkOPSK22DLcjs.EffectUnsupportedError)("Invalid effect authoring source");
181
+ throw new (0, _chunkJAOSL334cjs.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, _chunkOPSK22DLcjs.EffectControlError)(effectId, problems);
186
+ if (problems.length) throw new (0, _chunkJAOSL334cjs.EffectControlError)(effectId, problems);
187
187
  }
188
188
  if (description.kind === "motion") {
189
- throw new (0, _chunkOPSK22DLcjs.EffectUnsupportedError)("Motion effects cannot be authored as pixel effects", effectId);
189
+ throw new (0, _chunkJAOSL334cjs.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, _chunkOPSK22DLcjs.EffectUnsupportedError)(
210
+ throw new (0, _chunkJAOSL334cjs.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 = _chunkOPSK22DLcjs.effectValueErrors.call(void 0, description, { [name]: value });
324
+ const errors = _chunkJAOSL334cjs.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 = _chunkOPSK22DLcjs.effectValueErrors.call(void 0, description, { [k]: v });
394
+ const errors = _chunkJAOSL334cjs.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 = _chunkOPSK22DLcjs.effectValueErrors.call(void 0, description, { [name]: targetValue });
531
+ const errors = _chunkJAOSL334cjs.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, _chunkOPSK22DLcjs.EffectDisposedError)(effectId);
708
+ throw new (0, _chunkJAOSL334cjs.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, _chunkOPSK22DLcjs.EffectDisposedError)(effectId);
733
+ if (disposed) throw new (0, _chunkJAOSL334cjs.EffectDisposedError)(effectId);
734
734
  const effectiveValues = _nullishCoalesce(req.values, () => ( valuesAtTime(req.time, baseValues, tracks)));
735
735
  const pixels = await renderer.render({
736
736
  ...req,
@@ -37,7 +37,14 @@ var MOTION_ISSUE_CODES = {
37
37
  "camera-move-first-shot": { severity: "note", wording: "The first shot cuts to its framing, so the move its camera gets in with does not play." },
38
38
  "camera-move-not-played": { severity: "note", wording: "A shot names a camera move, but there is no effects provider with camera moves to play it, so the camera eases instead." },
39
39
  "camera-move-unknown": { severity: "error", wording: "A shot names a camera move the effects provider does not have, or one of the wrong role." },
40
- "camera-move-values": { severity: "error", wording: "A camera move's preset or values do not fit its controls." }
40
+ "camera-move-values": { severity: "error", wording: "A camera move's preset or values do not fit its controls." },
41
+ "layer-effect-unknown": { severity: "error", wording: "A shot layer names an effect the provider does not have, one that cannot be a layer, or a subject its effect cannot take." },
42
+ "layer-effect-values": { severity: "error", wording: "A shot layer's preset or values do not fit its effect's controls." },
43
+ "layer-not-drawn": { severity: "note", wording: "Shot layers name provider effects, but there is no effects provider to draw them, so they are left out of frames." },
44
+ "layer-slot-empty": { severity: "warning", wording: "A shot layer's subject is a slot nobody has filled, so it draws nothing." },
45
+ "transition-unknown": { severity: "error", wording: "A shot names a transition the provider does not have, or an effect that is not a transition." },
46
+ "transition-first-shot": { severity: "note", wording: "The first shot has a transition, but there is no shot before it, so it does not play." },
47
+ "transition-not-played": { severity: "note", wording: "Shots name transitions, but there is no effects provider to play them, so they crossfade." }
41
48
  };
42
49
  function motionIssue(code, message, extra = {}) {
43
50
  return { code, severity: MOTION_ISSUE_CODES[code].severity, message, ...extra };
@@ -37,7 +37,14 @@ var MOTION_ISSUE_CODES = {
37
37
  "camera-move-first-shot": { severity: "note", wording: "The first shot cuts to its framing, so the move its camera gets in with does not play." },
38
38
  "camera-move-not-played": { severity: "note", wording: "A shot names a camera move, but there is no effects provider with camera moves to play it, so the camera eases instead." },
39
39
  "camera-move-unknown": { severity: "error", wording: "A shot names a camera move the effects provider does not have, or one of the wrong role." },
40
- "camera-move-values": { severity: "error", wording: "A camera move's preset or values do not fit its controls." }
40
+ "camera-move-values": { severity: "error", wording: "A camera move's preset or values do not fit its controls." },
41
+ "layer-effect-unknown": { severity: "error", wording: "A shot layer names an effect the provider does not have, one that cannot be a layer, or a subject its effect cannot take." },
42
+ "layer-effect-values": { severity: "error", wording: "A shot layer's preset or values do not fit its effect's controls." },
43
+ "layer-not-drawn": { severity: "note", wording: "Shot layers name provider effects, but there is no effects provider to draw them, so they are left out of frames." },
44
+ "layer-slot-empty": { severity: "warning", wording: "A shot layer's subject is a slot nobody has filled, so it draws nothing." },
45
+ "transition-unknown": { severity: "error", wording: "A shot names a transition the provider does not have, or an effect that is not a transition." },
46
+ "transition-first-shot": { severity: "note", wording: "The first shot has a transition, but there is no shot before it, so it does not play." },
47
+ "transition-not-played": { severity: "note", wording: "Shots name transitions, but there is no effects provider to play them, so they crossfade." }
41
48
  };
42
49
  function motionIssue(code, message, extra = {}) {
43
50
  return { code, severity: MOTION_ISSUE_CODES[code].severity, message, ...extra };