@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.
- package/dist/animation-editor/compile.d.cts +3 -0
- package/dist/animation-editor/compile.d.ts +3 -0
- package/dist/animation-editor/controller.d.cts +67 -2
- package/dist/animation-editor/controller.d.ts +67 -2
- package/dist/animation-editor/edit.d.cts +1 -1
- package/dist/animation-editor/edit.d.ts +1 -1
- package/dist/animation-editor/evaluate.d.cts +8 -1
- package/dist/animation-editor/evaluate.d.ts +8 -1
- package/dist/animation-editor/index.cjs +1202 -371
- package/dist/animation-editor/index.d.cts +3 -0
- package/dist/animation-editor/index.d.ts +3 -0
- package/dist/animation-editor/index.js +1032 -201
- package/dist/animation-editor/issues.d.cts +28 -0
- package/dist/animation-editor/issues.d.ts +28 -0
- package/dist/animation-editor/layer-edit.d.cts +55 -0
- package/dist/animation-editor/layer-edit.d.ts +55 -0
- package/dist/animation-editor/layer-tools.d.cts +11 -0
- package/dist/animation-editor/layer-tools.d.ts +11 -0
- package/dist/animation-editor/layers.d.cts +166 -0
- package/dist/animation-editor/layers.d.ts +166 -0
- package/dist/animation-editor/parse.d.cts +2 -0
- package/dist/animation-editor/parse.d.ts +2 -0
- package/dist/animation-editor/types.d.cts +44 -1
- package/dist/animation-editor/types.d.ts +44 -1
- package/dist/canvas/index.cjs +10 -10
- package/dist/canvas/index.js +4 -4
- package/dist/chunk-6RZVFYGJ.cjs +255 -0
- package/dist/{chunk-C73U5UI3.cjs → chunk-A7PQNIBX.cjs} +14 -14
- package/dist/{chunk-ERJA5CGT.cjs → chunk-DGWOGQJ5.cjs} +8 -1
- package/dist/{chunk-TYCJGFCF.js → chunk-EUKY4KFX.js} +8 -1
- package/dist/chunk-I277KJLQ.js +255 -0
- package/dist/{chunk-OPSK22DL.cjs → chunk-JAOSL334.cjs} +24 -7
- package/dist/{chunk-TCZ2J7PR.js → chunk-SIK3LT6M.js} +26 -9
- package/dist/{chunk-SBPGIZQN.js → chunk-TEFQISBY.js} +1 -1
- package/dist/{chunk-CSM5T6SE.js → chunk-WDDUPWVG.js} +1 -1
- package/dist/{chunk-5T3H7CGG.cjs → chunk-ZQIWBJGH.cjs} +7 -7
- package/dist/effects/conformance.d.cts +4 -1
- package/dist/effects/conformance.d.ts +4 -1
- package/dist/effects/errors.d.cts +6 -1
- package/dist/effects/errors.d.ts +6 -1
- package/dist/effects/fake-templates.d.cts +17 -0
- package/dist/effects/fake-templates.d.ts +17 -0
- package/dist/effects/fake.d.cts +6 -1
- package/dist/effects/fake.d.ts +6 -1
- package/dist/effects/index.cjs +322 -67
- package/dist/effects/index.d.cts +1 -0
- package/dist/effects/index.d.ts +1 -0
- package/dist/effects/index.js +258 -3
- package/dist/effects/templates.d.cts +56 -0
- package/dist/effects/templates.d.ts +56 -0
- package/dist/effects/types.d.cts +212 -0
- package/dist/effects/types.d.ts +212 -0
- package/dist/image-editor/index.cjs +16 -16
- package/dist/image-editor/index.js +4 -4
- package/dist/paint-editor/index.cjs +10 -10
- package/dist/paint-editor/index.js +5 -5
- package/dist/vector/index.cjs +6 -6
- package/dist/vector/index.js +1 -1
- package/dist/video-editor/index.cjs +339 -339
- package/dist/video-editor/index.js +5 -5
- package/package.json +1 -1
- 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';
|
package/dist/canvas/index.cjs
CHANGED
|
@@ -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
|
|
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,
|
|
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,
|
|
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, ...
|
|
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:
|
|
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:
|
|
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
|
|
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",
|
package/dist/canvas/index.js
CHANGED
|
@@ -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-
|
|
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
|
|
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(...
|
|
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(...
|
|
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(...
|
|
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,
|
|
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,
|
|
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,
|
|
186
|
+
if (problems.length) throw new (0, _chunkJAOSL334cjs.EffectControlError)(effectId, problems);
|
|
187
187
|
}
|
|
188
188
|
if (description.kind === "motion") {
|
|
189
|
-
throw new (0,
|
|
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,
|
|
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 =
|
|
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 =
|
|
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 =
|
|
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,
|
|
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,
|
|
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 };
|