@pixerate/editor 0.229.0 → 0.231.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 (73) hide show
  1. package/dist/animation-editor/camera-tools.d.cts +10 -0
  2. package/dist/animation-editor/camera-tools.d.ts +10 -0
  3. package/dist/animation-editor/camera.d.cts +67 -0
  4. package/dist/animation-editor/camera.d.ts +67 -0
  5. package/dist/animation-editor/compile.d.cts +4 -0
  6. package/dist/animation-editor/compile.d.ts +4 -0
  7. package/dist/animation-editor/controller.d.cts +34 -2
  8. package/dist/animation-editor/controller.d.ts +34 -2
  9. package/dist/animation-editor/effect-tools.d.cts +2 -1
  10. package/dist/animation-editor/effect-tools.d.ts +2 -1
  11. package/dist/animation-editor/evaluate.d.cts +9 -0
  12. package/dist/animation-editor/evaluate.d.ts +9 -0
  13. package/dist/animation-editor/flair.d.cts +8 -0
  14. package/dist/animation-editor/flair.d.ts +8 -0
  15. package/dist/animation-editor/index.cjs +603 -217
  16. package/dist/animation-editor/index.d.cts +2 -0
  17. package/dist/animation-editor/index.d.ts +2 -0
  18. package/dist/animation-editor/index.js +454 -68
  19. package/dist/animation-editor/issues.d.cts +16 -0
  20. package/dist/animation-editor/issues.d.ts +16 -0
  21. package/dist/animation-editor/tools.d.cts +1 -1
  22. package/dist/animation-editor/tools.d.ts +1 -1
  23. package/dist/animation-editor/types.d.cts +21 -3
  24. package/dist/animation-editor/types.d.ts +21 -3
  25. package/dist/canvas/index.cjs +7 -7
  26. package/dist/canvas/index.js +1 -1
  27. package/dist/{chunk-IGPEBXUD.cjs → chunk-5T3H7CGG.cjs} +21 -11
  28. package/dist/{chunk-K5GXW5TR.cjs → chunk-C73U5UI3.cjs} +14 -14
  29. package/dist/{chunk-LPTFDCJO.js → chunk-CSM5T6SE.js} +14 -4
  30. package/dist/{chunk-EBDZWIOP.cjs → chunk-ERJA5CGT.cjs} +5 -1
  31. package/dist/{chunk-5MQOBTNP.cjs → chunk-OPSK22DL.cjs} +80 -9
  32. package/dist/{chunk-PB6JV62G.js → chunk-SBPGIZQN.js} +1 -1
  33. package/dist/{chunk-UGYUGZE2.js → chunk-TCZ2J7PR.js} +71 -0
  34. package/dist/{chunk-W4KFNAWC.js → chunk-TYCJGFCF.js} +5 -1
  35. package/dist/effects/helpers.d.cts +35 -1
  36. package/dist/effects/helpers.d.ts +35 -1
  37. package/dist/effects/index.cjs +60 -44
  38. package/dist/effects/index.js +18 -2
  39. package/dist/effects/types.d.cts +15 -0
  40. package/dist/effects/types.d.ts +15 -0
  41. package/dist/image-editor/index.cjs +13 -13
  42. package/dist/image-editor/index.js +1 -1
  43. package/dist/paint-editor/index.cjs +7 -7
  44. package/dist/paint-editor/index.js +2 -2
  45. package/dist/vector/index.cjs +6 -6
  46. package/dist/vector/index.js +1 -1
  47. package/dist/video-editor/camera-tools.d.cts +10 -0
  48. package/dist/video-editor/camera-tools.d.ts +10 -0
  49. package/dist/video-editor/camera.d.cts +47 -0
  50. package/dist/video-editor/camera.d.ts +47 -0
  51. package/dist/video-editor/check.d.cts +1 -1
  52. package/dist/video-editor/check.d.ts +1 -1
  53. package/dist/video-editor/controller.d.cts +37 -2
  54. package/dist/video-editor/controller.d.ts +37 -2
  55. package/dist/video-editor/effect-tools.d.cts +2 -1
  56. package/dist/video-editor/effect-tools.d.ts +2 -1
  57. package/dist/video-editor/index.cjs +705 -390
  58. package/dist/video-editor/index.d.cts +2 -0
  59. package/dist/video-editor/index.d.ts +2 -0
  60. package/dist/video-editor/index.js +334 -19
  61. package/dist/video-editor/keyframes.d.cts +10 -3
  62. package/dist/video-editor/keyframes.d.ts +10 -3
  63. package/dist/video-editor/parse.d.cts +5 -3
  64. package/dist/video-editor/parse.d.ts +5 -3
  65. package/dist/video-editor/tools.d.cts +18 -0
  66. package/dist/video-editor/tools.d.ts +18 -0
  67. package/dist/video-editor/types.d.cts +37 -1
  68. package/dist/video-editor/types.d.ts +37 -1
  69. package/package.json +1 -1
  70. package/skills/pixerate-animation-editor/SKILL.md +28 -1
  71. package/skills/pixerate-animation-motion-graphics/SKILL.md +3 -0
  72. package/skills/pixerate-video-editor/SKILL.md +16 -1
  73. package/skills/pixerate-video-montage/SKILL.md +2 -1
@@ -143,6 +143,22 @@ export declare const MOTION_ISSUE_CODES: {
143
143
  readonly severity: "note";
144
144
  readonly wording: "A camera rectangle has a different shape from the artwork, so it is letterboxed.";
145
145
  };
146
+ readonly 'camera-move-first-shot': {
147
+ readonly severity: "note";
148
+ readonly wording: "The first shot cuts to its framing, so the move its camera gets in with does not play.";
149
+ };
150
+ readonly 'camera-move-not-played': {
151
+ readonly severity: "note";
152
+ readonly wording: "A shot names a camera move, but there is no effects provider with camera moves to play it, so the camera eases instead.";
153
+ };
154
+ readonly 'camera-move-unknown': {
155
+ readonly severity: "error";
156
+ readonly wording: "A shot names a camera move the effects provider does not have, or one of the wrong role.";
157
+ };
158
+ readonly 'camera-move-values': {
159
+ readonly severity: "error";
160
+ readonly wording: "A camera move's preset or values do not fit its controls.";
161
+ };
146
162
  };
147
163
  export type MotionIssueCode = keyof typeof MOTION_ISSUE_CODES;
148
164
  export interface MotionIssue {
@@ -143,6 +143,22 @@ export declare const MOTION_ISSUE_CODES: {
143
143
  readonly severity: "note";
144
144
  readonly wording: "A camera rectangle has a different shape from the artwork, so it is letterboxed.";
145
145
  };
146
+ readonly 'camera-move-first-shot': {
147
+ readonly severity: "note";
148
+ readonly wording: "The first shot cuts to its framing, so the move its camera gets in with does not play.";
149
+ };
150
+ readonly 'camera-move-not-played': {
151
+ readonly severity: "note";
152
+ readonly wording: "A shot names a camera move, but there is no effects provider with camera moves to play it, so the camera eases instead.";
153
+ };
154
+ readonly 'camera-move-unknown': {
155
+ readonly severity: "error";
156
+ readonly wording: "A shot names a camera move the effects provider does not have, or one of the wrong role.";
157
+ };
158
+ readonly 'camera-move-values': {
159
+ readonly severity: "error";
160
+ readonly wording: "A camera move's preset or values do not fit its controls.";
161
+ };
146
162
  };
147
163
  export type MotionIssueCode = keyof typeof MOTION_ISSUE_CODES;
148
164
  export interface MotionIssue {
@@ -43,7 +43,7 @@ export declare function describeMotionElements(scene: MotionScene): {
43
43
  } | undefined;
44
44
  effects?: {
45
45
  id: string;
46
- type: "trim" | "merge" | "bevel" | "offset" | "twist" | "roundCorners" | "puckerBloat" | "zigzag" | "wiggle" | "repeater" | "envelope" | "contour" | "blockShadow" | "extrude";
46
+ type: "trim" | "merge" | "bevel" | "offset" | "contour" | "twist" | "roundCorners" | "puckerBloat" | "zigzag" | "wiggle" | "repeater" | "envelope" | "blockShadow" | "extrude";
47
47
  numericParams: {
48
48
  [k: string]: unknown;
49
49
  };
@@ -43,7 +43,7 @@ export declare function describeMotionElements(scene: MotionScene): {
43
43
  } | undefined;
44
44
  effects?: {
45
45
  id: string;
46
- type: "trim" | "merge" | "bevel" | "offset" | "twist" | "roundCorners" | "puckerBloat" | "zigzag" | "wiggle" | "repeater" | "envelope" | "contour" | "blockShadow" | "extrude";
46
+ type: "trim" | "merge" | "bevel" | "offset" | "contour" | "twist" | "roundCorners" | "puckerBloat" | "zigzag" | "wiggle" | "repeater" | "envelope" | "blockShadow" | "extrude";
47
47
  numericParams: {
48
48
  [k: string]: unknown;
49
49
  };
@@ -1,4 +1,4 @@
1
- import type { EffectValue } from '../effects/types.cjs';
1
+ import type { CameraMoveSpec, EffectValue } from '../effects/types.cjs';
2
2
  import type { AnimationEasing } from '../animation/easing.cjs';
3
3
  import type { AnimationLoopMode } from '../animation/evaluate.cjs';
4
4
  import type { VectorDocument } from '../vector/types.cjs';
@@ -76,17 +76,35 @@ export interface MotionRect {
76
76
  }
77
77
  /**
78
78
  * Where the camera looks: a rectangle, or the id of a node to frame (with `padding` around
79
- * its bounds, default 24). The camera eases there over `ms` (default 1000; the first shot cuts).
79
+ * its bounds, default 24), or neither to keep the framing it has (to name only a move). The
80
+ * camera eases there over `ms` (default 1000; the first shot cuts).
81
+ *
82
+ * With an effects provider that has camera moves (`capabilities.supportsCamera`), the camera can
83
+ * play a move from its catalog (as Flair's scene fields of the same names):
84
+ * - `in`: how it gets into this shot, a move whose role is `transition` (a glide, a whip pan, a
85
+ * cut). Its `ms` (else `ms` here, else 1000) sets how long it takes.
86
+ * - `during`: how it moves while the shot holds, a move whose role is `hold` (a Ken Burns drift,
87
+ * a crash zoom), played across the shot's hold. The next shot's move starts from where it ends.
88
+ * Without such a provider they are kept but not played: the camera eases as it would without them.
80
89
  */
81
90
  export type MotionCamera = (MotionRect | {
82
91
  frame: string;
83
92
  padding?: number;
84
- }) & {
93
+ } | MotionCameraNoFraming) & {
85
94
  ease?: AnimationEasing;
86
95
  ms?: number;
87
96
  /** Zoom at a steady rate (the size changes by the same factor each moment) instead of linearly. */
88
97
  geometric?: boolean;
98
+ in?: CameraMoveSpec;
99
+ during?: CameraMoveSpec;
89
100
  };
101
+ /** A camera that keeps the framing it has (it names only a move). */
102
+ export interface MotionCameraNoFraming {
103
+ x?: undefined;
104
+ frame?: undefined;
105
+ }
106
+ /** True when a shot's camera gives a framing (a rectangle or a node), not only a move. */
107
+ export declare const motionCameraFramed: (camera: MotionCamera) => boolean;
90
108
  /** A steady beat: `bpm` beats a minute, the first at `offset` ms, `meter` beats a bar. */
91
109
  export interface MotionBeatGrid {
92
110
  bpm: number;
@@ -1,4 +1,4 @@
1
- import type { EffectValue } from '../effects/types.js';
1
+ import type { CameraMoveSpec, 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';
@@ -76,17 +76,35 @@ export interface MotionRect {
76
76
  }
77
77
  /**
78
78
  * Where the camera looks: a rectangle, or the id of a node to frame (with `padding` around
79
- * its bounds, default 24). The camera eases there over `ms` (default 1000; the first shot cuts).
79
+ * its bounds, default 24), or neither to keep the framing it has (to name only a move). The
80
+ * camera eases there over `ms` (default 1000; the first shot cuts).
81
+ *
82
+ * With an effects provider that has camera moves (`capabilities.supportsCamera`), the camera can
83
+ * play a move from its catalog (as Flair's scene fields of the same names):
84
+ * - `in`: how it gets into this shot, a move whose role is `transition` (a glide, a whip pan, a
85
+ * cut). Its `ms` (else `ms` here, else 1000) sets how long it takes.
86
+ * - `during`: how it moves while the shot holds, a move whose role is `hold` (a Ken Burns drift,
87
+ * a crash zoom), played across the shot's hold. The next shot's move starts from where it ends.
88
+ * Without such a provider they are kept but not played: the camera eases as it would without them.
80
89
  */
81
90
  export type MotionCamera = (MotionRect | {
82
91
  frame: string;
83
92
  padding?: number;
84
- }) & {
93
+ } | MotionCameraNoFraming) & {
85
94
  ease?: AnimationEasing;
86
95
  ms?: number;
87
96
  /** Zoom at a steady rate (the size changes by the same factor each moment) instead of linearly. */
88
97
  geometric?: boolean;
98
+ in?: CameraMoveSpec;
99
+ during?: CameraMoveSpec;
89
100
  };
101
+ /** A camera that keeps the framing it has (it names only a move). */
102
+ export interface MotionCameraNoFraming {
103
+ x?: undefined;
104
+ frame?: undefined;
105
+ }
106
+ /** True when a shot's camera gives a framing (a rectangle or a node), not only a move. */
107
+ export declare const motionCameraFramed: (camera: MotionCamera) => boolean;
90
108
  /** A steady beat: `bpm` beats a minute, the first at `offset` ms, `meter` beats a bar. */
91
109
  export interface MotionBeatGrid {
92
110
  bpm: number;
@@ -220,7 +220,7 @@ var _chunkNM3G4VQTcjs = require('../chunk-NM3G4VQT.cjs');
220
220
 
221
221
 
222
222
 
223
- var _chunk5MQOBTNPcjs = require('../chunk-5MQOBTNP.cjs');
223
+ var _chunkOPSK22DLcjs = require('../chunk-OPSK22DL.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, _chunk5MQOBTNPcjs.EffectsSlot)();
11428
+ const slot = new (0, _chunkOPSK22DLcjs.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, _chunk5MQOBTNPcjs.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, _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);
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, ..._chunk5MQOBTNPcjs.resolveEffectValues.call(void 0, { ...d, defaults: base }, given) };
11466
+ return { ...base, ..._chunkOPSK22DLcjs.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: _chunk5MQOBTNPcjs.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: _chunkOPSK22DLcjs.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: _chunk5MQOBTNPcjs.resolveEffectValues.call(void 0, d, opts.values), duration };
11557
+ const transition = { effect: d.id, provider: slot.require().id, name: d.name, values: _chunkOPSK22DLcjs.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 _chunk5MQOBTNPcjs.createEffectToolProvider.call(void 0, {
11701
+ return _chunkOPSK22DLcjs.createEffectToolProvider.call(void 0, {
11702
11702
  id: _nullishCoalesce(options.id, () => ( "canvas-effects")),
11703
11703
  namespace: options.namespace,
11704
11704
  title: "Canvas effects",
@@ -220,7 +220,7 @@ import {
220
220
  EffectsSlot,
221
221
  createEffectToolProvider,
222
222
  resolveEffectValues
223
- } from "../chunk-UGYUGZE2.js";
223
+ } from "../chunk-TCZ2J7PR.js";
224
224
  import "../chunk-V2ZCJRDM.js";
225
225
  import {
226
226
  brushstrokeScaleBucket
@@ -7,7 +7,7 @@ var _chunkPY4GWSE2cjs = require('./chunk-PY4GWSE2.cjs');
7
7
 
8
8
 
9
9
 
10
- var _chunk5MQOBTNPcjs = require('./chunk-5MQOBTNP.cjs');
10
+ var _chunkOPSK22DLcjs = require('./chunk-OPSK22DL.cjs');
11
11
 
12
12
 
13
13
  var _chunkWOFXZ3XUcjs = require('./chunk-WOFXZ3XU.cjs');
@@ -155,11 +155,20 @@ var withEase = (key, ease) => {
155
155
  const { ease: _old, ...rest } = key;
156
156
  return ease === void 0 || ease === "linear" ? rest : { ...rest, ease };
157
157
  };
158
- function sortVideoKeys(keys) {
158
+ function sortVideoKeys(keys, options = {}) {
159
159
  const sorted = [...keys].sort((a, b) => a.t - b.t);
160
- for (let i = 1; i < sorted.length; i++) if (Math.abs(sorted[i].t - sorted[i - 1].t) < 1e-6) throw new Error(`Two keys are at ${sorted[i].t} ms; give each key its own time.`);
160
+ const problem = videoKeyTimesProblem(sorted, options.jumps !== false);
161
+ if (problem) throw new Error(problem);
161
162
  return sorted;
162
163
  }
164
+ function videoKeyTimesProblem(keys, jumps = true) {
165
+ for (let i = 1; i < keys.length; i++) {
166
+ if (Math.abs(keys[i].t - keys[i - 1].t) >= 1e-6) continue;
167
+ if (!jumps) return `Two keys are at ${keys[i].t} ms; give each key its own time.`;
168
+ if (i >= 2 && Math.abs(keys[i].t - keys[i - 2].t) < 1e-6) return `Three keys are at ${keys[i].t} ms; a time takes one key, or two for a jump (the value just before, then the value after).`;
169
+ }
170
+ return null;
171
+ }
163
172
  function setVideoKey(prop, key, fallback) {
164
173
  const keys = isVideoKeyframes(prop) ? prop.keys.filter((k) => Math.abs(k.t - key.t) >= 1e-6) : [];
165
174
  if (!isVideoKeyframes(prop)) {
@@ -207,7 +216,7 @@ function videoKeyframeFfmpegExpr(prop, time, fallback, step) {
207
216
  for (let i = points.length - 2; i >= 0; i--) {
208
217
  const [t0, v0] = points[i];
209
218
  const [t1, v1] = points[i + 1];
210
- const seg = holds.has(i) || v0 === v1 ? fmt(v0) : `${fmt(v0)}${signed((v1 - v0) / (t1 - t0))}*(${time}${signed(-t0)})`;
219
+ const seg = holds.has(i) || v0 === v1 || t1 <= t0 ? fmt(v0) : `${fmt(v0)}${signed((v1 - v0) / (t1 - t0))}*(${time}${signed(-t0)})`;
211
220
  expr = `if(lt(${time},${fmt(t1)}),${seg},${expr})`;
212
221
  }
213
222
  return `if(lt(${time},${fmt(points[0][0])}),${fmt(points[0][1])},${expr})`;
@@ -1307,12 +1316,12 @@ function videoEffectLayerKey(layer, width, height) {
1307
1316
  }
1308
1317
  function mixEffectPixels(a, b, k) {
1309
1318
  if (k >= 1) return b;
1310
- const out = _chunk5MQOBTNPcjs.createEffectPixels.call(void 0, a.width, a.height);
1319
+ const out = _chunkOPSK22DLcjs.createEffectPixels.call(void 0, a.width, a.height);
1311
1320
  for (let i = 0; i < out.data.length; i++) out.data[i] = a.data[i] + (b.data[i] - a.data[i]) * k;
1312
1321
  return out;
1313
1322
  }
1314
1323
  function overEffectPixels(base, top, opacity = 1) {
1315
- const out = _chunk5MQOBTNPcjs.createEffectPixels.call(void 0, base.width, base.height);
1324
+ const out = _chunkOPSK22DLcjs.createEffectPixels.call(void 0, base.width, base.height);
1316
1325
  const o = out.data;
1317
1326
  const b = base.data;
1318
1327
  const t = top.data;
@@ -1357,7 +1366,7 @@ var VideoEffectsEngine = (_class2 = class {
1357
1366
  __init10() {this.generation = 0}
1358
1367
 
1359
1368
  constructor(options = {}) {;_class2.prototype.__init5.call(this);_class2.prototype.__init6.call(this);_class2.prototype.__init7.call(this);_class2.prototype.__init8.call(this);_class2.prototype.__init9.call(this);_class2.prototype.__init10.call(this);
1360
- this.slot = _nullishCoalesce(options.slot, () => ( new (0, _chunk5MQOBTNPcjs.EffectsSlot)()));
1369
+ this.slot = _nullishCoalesce(options.slot, () => ( new (0, _chunkOPSK22DLcjs.EffectsSlot)()));
1361
1370
  this.maxEntries = _nullishCoalesce(options.maxEntries, () => ( 120));
1362
1371
  this.toImage = _nullishCoalesce(options.toImage, () => ( defaultToImage));
1363
1372
  this.pool = new (0, _chunkPY4GWSE2cjs.EffectRendererPool)({ maxRenderers: options.maxRenderers });
@@ -1499,7 +1508,7 @@ function checkVideoEffectPlace(d, place) {
1499
1508
  if (d.kind === "motion" || !d.uses.some((u) => allowed.includes(u))) {
1500
1509
  const where = place === "clip" ? "on a clip" : place === "transition" ? "as a transition" : "as an effect clip";
1501
1510
  const instead = d.uses.includes("transition") ? "set_video_effect_transition" : d.uses.some((u) => VIDEO_EFFECT_PLACES.generator.includes(u)) ? "add_video_effect_clip" : d.uses.some((u) => u === "filter") ? "apply_effect" : null;
1502
- throw new (0, _chunk5MQOBTNPcjs.EffectUnsupportedError)(
1511
+ throw new (0, _chunkOPSK22DLcjs.EffectUnsupportedError)(
1503
1512
  `"${d.id}" (${d.name}) is a ${d.uses.join(" / ")} effect and cannot be used ${where}, which takes ${allowed.join(" or ")} effects${instead ? `; use ${instead} instead` : ""}. search_effects with use "${allowed[0]}" lists ones that fit.`,
1504
1513
  d.id
1505
1514
  );
@@ -1520,12 +1529,12 @@ function checkVideoEffectValues(d, values, options = {}) {
1520
1529
  else if (type !== "number" && type !== "integer") errors.push({ path: `/${k}`, message: "only number controls can be keyframed." });
1521
1530
  else for (const [i, key] of v.keys.entries()) for (const e of _chunkWOFXZ3XUcjs.validateJsonSchema.call(void 0, key.v, schema)) errors.push({ path: `/${k}/keys/${i}/v`, message: e.message });
1522
1531
  }
1523
- for (const e of _chunk5MQOBTNPcjs.effectValueErrors.call(void 0, d, plain)) {
1532
+ for (const e of _chunkOPSK22DLcjs.effectValueErrors.call(void 0, d, plain)) {
1524
1533
  const name = e.path.split("/")[1];
1525
1534
  const about = name && _optionalChain([d, 'access', _46 => _46.controls, 'access', _47 => _47.properties, 'access', _48 => _48[name], 'optionalAccess', _49 => _49.description]);
1526
1535
  errors.push(about ? { path: e.path, message: `${e.message} (${about})` } : e);
1527
1536
  }
1528
- if (errors.length) throw new (0, _chunk5MQOBTNPcjs.EffectControlError)(d.id, errors);
1537
+ if (errors.length) throw new (0, _chunkOPSK22DLcjs.EffectControlError)(d.id, errors);
1529
1538
  return { ..._nullishCoalesce(options.base, () => ( d.defaults)), ..._nullishCoalesce(values, () => ( {})) };
1530
1539
  }
1531
1540
 
@@ -2610,4 +2619,5 @@ function buildFfmpeg(project, options, measure) {
2610
2619
 
2611
2620
 
2612
2621
 
2613
- exports.VIDEO_FITS = VIDEO_FITS; exports.VIDEO_TRANSITION_TYPES = VIDEO_TRANSITION_TYPES; exports.VIDEO_WIPE_DIRECTIONS = VIDEO_WIPE_DIRECTIONS; exports.videoTransitionAudioCurve = videoTransitionAudioCurve; exports.VIDEO_BLEND_MODES = VIDEO_BLEND_MODES; exports.VIDEO_CLIP_ANIMATION_EFFECTS = VIDEO_CLIP_ANIMATION_EFFECTS; exports.VIDEO_MASK_SHAPES = VIDEO_MASK_SHAPES; exports.VIDEO_FADE_CURVES = VIDEO_FADE_CURVES; exports.VIDEO_ADJUSTMENT_SCOPES = VIDEO_ADJUSTMENT_SCOPES; exports.VIDEO_PLACEMENT_MODES = VIDEO_PLACEMENT_MODES; exports.VIDEO_CLIP_TYPES = VIDEO_CLIP_TYPES; exports.VIDEO_EASE_NAMES = VIDEO_EASE_NAMES; exports.VIDEO_KEYFRAME_PROPERTIES = VIDEO_KEYFRAME_PROPERTIES; exports.VIDEO_TRANSFORM_PROPERTIES = VIDEO_TRANSFORM_PROPERTIES; exports.VIDEO_KEYFRAME_LIMITS = VIDEO_KEYFRAME_LIMITS; exports.isVideoKeyframes = isVideoKeyframes; exports.videoEaseBezier = videoEaseBezier; exports.videoEaseProgress = videoEaseProgress; exports.videoValueAt = videoValueAt; exports.videoKeysOf = videoKeysOf; exports.retimeVideoAnimatable = retimeVideoAnimatable; exports.splitVideoAnimatable = splitVideoAnimatable; exports.sortVideoKeys = sortVideoKeys; exports.setVideoKey = setVideoKey; exports.removeVideoKey = removeVideoKey; exports.videoKeyframeBreakpoints = videoKeyframeBreakpoints; exports.videoKeyframeFfmpegExpr = videoKeyframeFfmpegExpr; exports.VIDEO_LUT_MAX_SIZE = VIDEO_LUT_MAX_SIZE; exports.parseVideoCubeLut = parseVideoCubeLut; exports.videoLutFromFunction = videoLutFromFunction; exports.videoLutToCube = videoLutToCube; exports.applyVideoLut = applyVideoLut; exports.videoSkinToneWeight = videoSkinToneWeight; exports.applyVideoLutToPixels = applyVideoLutToPixels; exports.videoLutSvgApproximation = videoLutSvgApproximation; exports.VideoLutCache = VideoLutCache; exports.VIDEO_GRADE_CONTROLS = VIDEO_GRADE_CONTROLS; exports.VIDEO_GRADE_PRESETS = VIDEO_GRADE_PRESETS; exports.VIDEO_GRADE_PRESET_NAMES = VIDEO_GRADE_PRESET_NAMES; exports.normalizeVideoGradeLut = normalizeVideoGradeLut; exports.normalizeVideoGrade = normalizeVideoGrade; exports.videoGradePreset = videoGradePreset; exports.videoGradeLook = videoGradeLook; exports.videoGradeSteps = videoGradeSteps; exports.videoGradeStepMatrix = videoGradeStepMatrix; exports.applyVideoGrade = applyVideoGrade; exports.videoGradeStepSvgValues = videoGradeStepSvgValues; exports.videoLutSvgPrimitives = videoLutSvgPrimitives; exports.videoGradeSvgFilter = videoGradeSvgFilter; exports.videoGradeGainsFilterId = videoGradeGainsFilterId; exports.videoGradeCssFilter = videoGradeCssFilter; exports.videoGradeSvgDefs = videoGradeSvgDefs; exports.videoGradeFfmpegFilters = videoGradeFfmpegFilters; exports.videoClipSourceTime = videoClipSourceTime; exports.videoClipSpeedAt = videoClipSpeedAt; exports.videoSpeedRampKeys = videoSpeedRampKeys; exports.checkVideoTimeRemap = checkVideoTimeRemap; exports.videoTimeRemapSpan = videoTimeRemapSpan; exports.videoTimeRemapBreakpoints = videoTimeRemapBreakpoints; exports.videoSpeedRampEdit = videoSpeedRampEdit; exports.videoFreezeFrameEdit = videoFreezeFrameEdit; exports.videoSlowMotionRamp = videoSlowMotionRamp; exports.VIDEO_TIME_EPSILON = VIDEO_TIME_EPSILON; exports.videoClipEnd = videoClipEnd; exports.videoClipTypeLabel = videoClipTypeLabel; exports.videoTrackSyncLocked = videoTrackSyncLocked; exports.videoClipSpeed = videoClipSpeed; exports.videoClipSourceRange = videoClipSourceRange; exports.videoFrameDuration = videoFrameDuration; exports.snapToVideoFrame = snapToVideoFrame; exports.videoFrames = videoFrames; exports.isVideoDropFrameRate = isVideoDropFrameRate; exports.videoTimecodeFrames = videoTimecodeFrames; exports.formatVideoTimecode = formatVideoTimecode; exports.formatVideoTimecodeFrames = formatVideoTimecodeFrames; exports.videoHourFrames = videoHourFrames; exports.formatVideoSeconds = formatVideoSeconds; exports.videoAssetHasSound = videoAssetHasSound; exports.videoAssetMap = videoAssetMap; exports.videoSequenceMap = videoSequenceMap; exports.videoSequenceDuration = videoSequenceDuration; exports.videoSequenceChildren = videoSequenceChildren; exports.findVideoSequenceCycle = findVideoSequenceCycle; exports.allVideoTracks = allVideoTracks; exports.sortedVideoClips = sortedVideoClips; exports.findVideoClip = findVideoClip; exports.allVideoClipIds = allVideoClipIds; exports.findVideoSequenceClip = findVideoSequenceClip; exports.videoProjectDuration = videoProjectDuration; exports.videoClipBefore = videoClipBefore; exports.videoClipAfter = videoClipAfter; exports.videoClipHandles = videoClipHandles; exports.videoClipOverhang = videoClipOverhang; exports.videoSnapPoints = videoSnapPoints; exports.snapVideoTime = snapVideoTime; exports.findVideoGaps = findVideoGaps; exports.videoOverlap = videoOverlap; exports.nextFreeVideoSlot = nextFreeVideoSlot; exports.videoClipsAt = videoClipsAt; exports.VIDEO_CLIP_ANIMATION_DURATION = VIDEO_CLIP_ANIMATION_DURATION; exports.videoClipAnimationProperties = videoClipAnimationProperties; exports.videoClipAnimationSpans = videoClipAnimationSpans; exports.videoClipAnimated = videoClipAnimated; exports.VIDEO_MASK_DEFAULTS = VIDEO_MASK_DEFAULTS; exports.normalizeVideoClipMask = normalizeVideoClipMask; exports.videoMaskPolygon = videoMaskPolygon; exports.videoLayerMask = videoLayerMask; exports.videoMaskSvgPath = videoMaskSvgPath; exports.videoMaskContains = videoMaskContains; exports.videoEffectValuesAt = videoEffectValuesAt; exports.videoTransformAt = videoTransformAt; exports.placeVideoRect = placeVideoRect; exports.videoTextStyle = videoTextStyle; exports.videoTitleFrame = videoTitleFrame; exports.videoTransitionEase = videoTransitionEase; exports.videoFadeCurveGain = videoFadeCurveGain; exports.videoClipSoundGain = videoClipSoundGain; exports.videoFrameAt = videoFrameAt; exports.videoLayersWithoutTransitionEffects = videoLayersWithoutTransitionEffects; exports.videoFrameLeafLayers = videoFrameLeafLayers; exports.videoProjectEffects = videoProjectEffects; exports.videoClipEffectUses = videoClipEffectUses; exports.videoLayersUseEffects = videoLayersUseEffects; exports.videoEffectFrameTime = videoEffectFrameTime; exports.videoEffectLayerKey = videoEffectLayerKey; exports.mixEffectPixels = mixEffectPixels; exports.overEffectPixels = overEffectPixels; exports.VideoEffectsEngine = VideoEffectsEngine; exports.VIDEO_EFFECT_PLACES = VIDEO_EFFECT_PLACES; exports.checkVideoEffectPlace = checkVideoEffectPlace; exports.checkVideoEffectValues = checkVideoEffectValues; exports.videoIrisRadius = videoIrisRadius; exports.videoWipeRect = videoWipeRect; exports.videoFrameToSvg = videoFrameToSvg; exports.videoProjectSvg = videoProjectSvg; exports.videoContactSheetSvg = videoContactSheetSvg; exports.drawVideoFrame = drawVideoFrame; exports.renderVideoFrameExact = renderVideoFrameExact; exports.toVideoEdl = toVideoEdl; exports.videoFfmpegUnsupportedEffects = videoFfmpegUnsupportedEffects; exports.videoFfmpegUnsupportedFeatures = videoFfmpegUnsupportedFeatures; exports.parseVideoLoudnessMeasurement = parseVideoLoudnessMeasurement; exports.ffmpegFilterValue = ffmpegFilterValue; exports.videoShellQuote = videoShellQuote; exports.toVideoFfmpeg = toVideoFfmpeg; exports.toVideoLoudnessMeasure = toVideoLoudnessMeasure;
2622
+
2623
+ exports.VIDEO_FITS = VIDEO_FITS; exports.VIDEO_TRANSITION_TYPES = VIDEO_TRANSITION_TYPES; exports.VIDEO_WIPE_DIRECTIONS = VIDEO_WIPE_DIRECTIONS; exports.videoTransitionAudioCurve = videoTransitionAudioCurve; exports.VIDEO_BLEND_MODES = VIDEO_BLEND_MODES; exports.VIDEO_CLIP_ANIMATION_EFFECTS = VIDEO_CLIP_ANIMATION_EFFECTS; exports.VIDEO_MASK_SHAPES = VIDEO_MASK_SHAPES; exports.VIDEO_FADE_CURVES = VIDEO_FADE_CURVES; exports.VIDEO_ADJUSTMENT_SCOPES = VIDEO_ADJUSTMENT_SCOPES; exports.VIDEO_PLACEMENT_MODES = VIDEO_PLACEMENT_MODES; exports.VIDEO_CLIP_TYPES = VIDEO_CLIP_TYPES; exports.VIDEO_EASE_NAMES = VIDEO_EASE_NAMES; exports.VIDEO_KEYFRAME_PROPERTIES = VIDEO_KEYFRAME_PROPERTIES; exports.VIDEO_TRANSFORM_PROPERTIES = VIDEO_TRANSFORM_PROPERTIES; exports.VIDEO_KEYFRAME_LIMITS = VIDEO_KEYFRAME_LIMITS; exports.isVideoKeyframes = isVideoKeyframes; exports.videoEaseBezier = videoEaseBezier; exports.videoEaseProgress = videoEaseProgress; exports.videoValueAt = videoValueAt; exports.videoKeysOf = videoKeysOf; exports.retimeVideoAnimatable = retimeVideoAnimatable; exports.splitVideoAnimatable = splitVideoAnimatable; exports.sortVideoKeys = sortVideoKeys; exports.videoKeyTimesProblem = videoKeyTimesProblem; exports.setVideoKey = setVideoKey; exports.removeVideoKey = removeVideoKey; exports.videoKeyframeBreakpoints = videoKeyframeBreakpoints; exports.videoKeyframeFfmpegExpr = videoKeyframeFfmpegExpr; exports.VIDEO_LUT_MAX_SIZE = VIDEO_LUT_MAX_SIZE; exports.parseVideoCubeLut = parseVideoCubeLut; exports.videoLutFromFunction = videoLutFromFunction; exports.videoLutToCube = videoLutToCube; exports.applyVideoLut = applyVideoLut; exports.videoSkinToneWeight = videoSkinToneWeight; exports.applyVideoLutToPixels = applyVideoLutToPixels; exports.videoLutSvgApproximation = videoLutSvgApproximation; exports.VideoLutCache = VideoLutCache; exports.VIDEO_GRADE_CONTROLS = VIDEO_GRADE_CONTROLS; exports.VIDEO_GRADE_PRESETS = VIDEO_GRADE_PRESETS; exports.VIDEO_GRADE_PRESET_NAMES = VIDEO_GRADE_PRESET_NAMES; exports.normalizeVideoGradeLut = normalizeVideoGradeLut; exports.normalizeVideoGrade = normalizeVideoGrade; exports.videoGradePreset = videoGradePreset; exports.videoGradeLook = videoGradeLook; exports.videoGradeSteps = videoGradeSteps; exports.videoGradeStepMatrix = videoGradeStepMatrix; exports.applyVideoGrade = applyVideoGrade; exports.videoGradeStepSvgValues = videoGradeStepSvgValues; exports.videoLutSvgPrimitives = videoLutSvgPrimitives; exports.videoGradeSvgFilter = videoGradeSvgFilter; exports.videoGradeGainsFilterId = videoGradeGainsFilterId; exports.videoGradeCssFilter = videoGradeCssFilter; exports.videoGradeSvgDefs = videoGradeSvgDefs; exports.videoGradeFfmpegFilters = videoGradeFfmpegFilters; exports.videoClipSourceTime = videoClipSourceTime; exports.videoClipSpeedAt = videoClipSpeedAt; exports.videoSpeedRampKeys = videoSpeedRampKeys; exports.checkVideoTimeRemap = checkVideoTimeRemap; exports.videoTimeRemapSpan = videoTimeRemapSpan; exports.videoTimeRemapBreakpoints = videoTimeRemapBreakpoints; exports.videoSpeedRampEdit = videoSpeedRampEdit; exports.videoFreezeFrameEdit = videoFreezeFrameEdit; exports.videoSlowMotionRamp = videoSlowMotionRamp; exports.VIDEO_TIME_EPSILON = VIDEO_TIME_EPSILON; exports.videoClipEnd = videoClipEnd; exports.videoClipTypeLabel = videoClipTypeLabel; exports.videoTrackSyncLocked = videoTrackSyncLocked; exports.videoClipSpeed = videoClipSpeed; exports.videoClipSourceRange = videoClipSourceRange; exports.videoFrameDuration = videoFrameDuration; exports.snapToVideoFrame = snapToVideoFrame; exports.videoFrames = videoFrames; exports.isVideoDropFrameRate = isVideoDropFrameRate; exports.videoTimecodeFrames = videoTimecodeFrames; exports.formatVideoTimecode = formatVideoTimecode; exports.formatVideoTimecodeFrames = formatVideoTimecodeFrames; exports.videoHourFrames = videoHourFrames; exports.formatVideoSeconds = formatVideoSeconds; exports.videoAssetHasSound = videoAssetHasSound; exports.videoAssetMap = videoAssetMap; exports.videoSequenceMap = videoSequenceMap; exports.videoSequenceDuration = videoSequenceDuration; exports.videoSequenceChildren = videoSequenceChildren; exports.findVideoSequenceCycle = findVideoSequenceCycle; exports.allVideoTracks = allVideoTracks; exports.sortedVideoClips = sortedVideoClips; exports.findVideoClip = findVideoClip; exports.allVideoClipIds = allVideoClipIds; exports.findVideoSequenceClip = findVideoSequenceClip; exports.videoProjectDuration = videoProjectDuration; exports.videoClipBefore = videoClipBefore; exports.videoClipAfter = videoClipAfter; exports.videoClipHandles = videoClipHandles; exports.videoClipOverhang = videoClipOverhang; exports.videoSnapPoints = videoSnapPoints; exports.snapVideoTime = snapVideoTime; exports.findVideoGaps = findVideoGaps; exports.videoOverlap = videoOverlap; exports.nextFreeVideoSlot = nextFreeVideoSlot; exports.videoClipsAt = videoClipsAt; exports.VIDEO_CLIP_ANIMATION_DURATION = VIDEO_CLIP_ANIMATION_DURATION; exports.videoClipAnimationProperties = videoClipAnimationProperties; exports.videoClipAnimationSpans = videoClipAnimationSpans; exports.videoClipAnimated = videoClipAnimated; exports.VIDEO_MASK_DEFAULTS = VIDEO_MASK_DEFAULTS; exports.normalizeVideoClipMask = normalizeVideoClipMask; exports.videoMaskPolygon = videoMaskPolygon; exports.videoLayerMask = videoLayerMask; exports.videoMaskSvgPath = videoMaskSvgPath; exports.videoMaskContains = videoMaskContains; exports.videoEffectValuesAt = videoEffectValuesAt; exports.videoTransformAt = videoTransformAt; exports.placeVideoRect = placeVideoRect; exports.videoTextStyle = videoTextStyle; exports.videoTitleFrame = videoTitleFrame; exports.videoTransitionEase = videoTransitionEase; exports.videoFadeCurveGain = videoFadeCurveGain; exports.videoClipSoundGain = videoClipSoundGain; exports.videoFrameAt = videoFrameAt; exports.videoLayersWithoutTransitionEffects = videoLayersWithoutTransitionEffects; exports.videoFrameLeafLayers = videoFrameLeafLayers; exports.videoProjectEffects = videoProjectEffects; exports.videoClipEffectUses = videoClipEffectUses; exports.videoLayersUseEffects = videoLayersUseEffects; exports.videoEffectFrameTime = videoEffectFrameTime; exports.videoEffectLayerKey = videoEffectLayerKey; exports.mixEffectPixels = mixEffectPixels; exports.overEffectPixels = overEffectPixels; exports.VideoEffectsEngine = VideoEffectsEngine; exports.VIDEO_EFFECT_PLACES = VIDEO_EFFECT_PLACES; exports.checkVideoEffectPlace = checkVideoEffectPlace; exports.checkVideoEffectValues = checkVideoEffectValues; exports.videoIrisRadius = videoIrisRadius; exports.videoWipeRect = videoWipeRect; exports.videoFrameToSvg = videoFrameToSvg; exports.videoProjectSvg = videoProjectSvg; exports.videoContactSheetSvg = videoContactSheetSvg; exports.drawVideoFrame = drawVideoFrame; exports.renderVideoFrameExact = renderVideoFrameExact; exports.toVideoEdl = toVideoEdl; exports.videoFfmpegUnsupportedEffects = videoFfmpegUnsupportedEffects; exports.videoFfmpegUnsupportedFeatures = videoFfmpegUnsupportedFeatures; exports.parseVideoLoudnessMeasurement = parseVideoLoudnessMeasurement; exports.ffmpegFilterValue = ffmpegFilterValue; exports.videoShellQuote = videoShellQuote; exports.toVideoFfmpeg = toVideoFfmpeg; exports.toVideoLoudnessMeasure = toVideoLoudnessMeasure;
@@ -3,7 +3,7 @@
3
3
 
4
4
 
5
5
 
6
- var _chunk5MQOBTNPcjs = require('./chunk-5MQOBTNP.cjs');
6
+ var _chunkOPSK22DLcjs = require('./chunk-OPSK22DL.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(..._chunk5MQOBTNPcjs.effectValueErrors.call(void 0, description, doc.values).map((e) => ({ ...e, path: `/values${e.path}` })));
29
+ else errors.push(..._chunkOPSK22DLcjs.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(..._chunk5MQOBTNPcjs.effectValueErrors.call(void 0, description, { [control]: kf.value }).map((e) => ({ ...e, path: `${at}/${i}/value` })));
42
+ errors.push(..._chunkOPSK22DLcjs.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(..._chunk5MQOBTNPcjs.effectValueErrors.call(void 0, description, p.values).map((e) => ({ ...e, path: `/presets/${i}/values${e.path}` })));
52
+ errors.push(..._chunkOPSK22DLcjs.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, _chunk5MQOBTNPcjs.EffectUnsupportedError)("Invalid effect authoring document: version 1 required");
173
+ throw new (0, _chunkOPSK22DLcjs.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, _chunk5MQOBTNPcjs.EffectUnsupportedError)("Invalid effect authoring source");
181
+ throw new (0, _chunkOPSK22DLcjs.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, _chunk5MQOBTNPcjs.EffectControlError)(effectId, problems);
186
+ if (problems.length) throw new (0, _chunkOPSK22DLcjs.EffectControlError)(effectId, problems);
187
187
  }
188
188
  if (description.kind === "motion") {
189
- throw new (0, _chunk5MQOBTNPcjs.EffectUnsupportedError)("Motion effects cannot be authored as pixel effects", effectId);
189
+ throw new (0, _chunkOPSK22DLcjs.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, _chunk5MQOBTNPcjs.EffectUnsupportedError)(
210
+ throw new (0, _chunkOPSK22DLcjs.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 = _chunk5MQOBTNPcjs.effectValueErrors.call(void 0, description, { [name]: value });
324
+ const errors = _chunkOPSK22DLcjs.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 = _chunk5MQOBTNPcjs.effectValueErrors.call(void 0, description, { [k]: v });
394
+ const errors = _chunkOPSK22DLcjs.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 = _chunk5MQOBTNPcjs.effectValueErrors.call(void 0, description, { [name]: targetValue });
531
+ const errors = _chunkOPSK22DLcjs.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, _chunk5MQOBTNPcjs.EffectDisposedError)(effectId);
708
+ throw new (0, _chunkOPSK22DLcjs.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, _chunk5MQOBTNPcjs.EffectDisposedError)(effectId);
733
+ if (disposed) throw new (0, _chunkOPSK22DLcjs.EffectDisposedError)(effectId);
734
734
  const effectiveValues = _nullishCoalesce(req.values, () => ( valuesAtTime(req.time, baseValues, tracks)));
735
735
  const pixels = await renderer.render({
736
736
  ...req,
@@ -7,7 +7,7 @@ import {
7
7
  EffectsSlot,
8
8
  createEffectPixels,
9
9
  effectValueErrors
10
- } from "./chunk-UGYUGZE2.js";
10
+ } from "./chunk-TCZ2J7PR.js";
11
11
  import {
12
12
  validateJsonSchema
13
13
  } from "./chunk-V2ZCJRDM.js";
@@ -155,11 +155,20 @@ var withEase = (key, ease) => {
155
155
  const { ease: _old, ...rest } = key;
156
156
  return ease === void 0 || ease === "linear" ? rest : { ...rest, ease };
157
157
  };
158
- function sortVideoKeys(keys) {
158
+ function sortVideoKeys(keys, options = {}) {
159
159
  const sorted = [...keys].sort((a, b) => a.t - b.t);
160
- for (let i = 1; i < sorted.length; i++) if (Math.abs(sorted[i].t - sorted[i - 1].t) < 1e-6) throw new Error(`Two keys are at ${sorted[i].t} ms; give each key its own time.`);
160
+ const problem = videoKeyTimesProblem(sorted, options.jumps !== false);
161
+ if (problem) throw new Error(problem);
161
162
  return sorted;
162
163
  }
164
+ function videoKeyTimesProblem(keys, jumps = true) {
165
+ for (let i = 1; i < keys.length; i++) {
166
+ if (Math.abs(keys[i].t - keys[i - 1].t) >= 1e-6) continue;
167
+ if (!jumps) return `Two keys are at ${keys[i].t} ms; give each key its own time.`;
168
+ if (i >= 2 && Math.abs(keys[i].t - keys[i - 2].t) < 1e-6) return `Three keys are at ${keys[i].t} ms; a time takes one key, or two for a jump (the value just before, then the value after).`;
169
+ }
170
+ return null;
171
+ }
163
172
  function setVideoKey(prop, key, fallback) {
164
173
  const keys = isVideoKeyframes(prop) ? prop.keys.filter((k) => Math.abs(k.t - key.t) >= 1e-6) : [];
165
174
  if (!isVideoKeyframes(prop)) {
@@ -207,7 +216,7 @@ function videoKeyframeFfmpegExpr(prop, time, fallback, step) {
207
216
  for (let i = points.length - 2; i >= 0; i--) {
208
217
  const [t0, v0] = points[i];
209
218
  const [t1, v1] = points[i + 1];
210
- const seg = holds.has(i) || v0 === v1 ? fmt(v0) : `${fmt(v0)}${signed((v1 - v0) / (t1 - t0))}*(${time}${signed(-t0)})`;
219
+ const seg = holds.has(i) || v0 === v1 || t1 <= t0 ? fmt(v0) : `${fmt(v0)}${signed((v1 - v0) / (t1 - t0))}*(${time}${signed(-t0)})`;
211
220
  expr = `if(lt(${time},${fmt(t1)}),${seg},${expr})`;
212
221
  }
213
222
  return `if(lt(${time},${fmt(points[0][0])}),${fmt(points[0][1])},${expr})`;
@@ -2487,6 +2496,7 @@ export {
2487
2496
  retimeVideoAnimatable,
2488
2497
  splitVideoAnimatable,
2489
2498
  sortVideoKeys,
2499
+ videoKeyTimesProblem,
2490
2500
  setVideoKey,
2491
2501
  removeVideoKey,
2492
2502
  videoKeyframeBreakpoints,
@@ -33,7 +33,11 @@ var MOTION_ISSUE_CODES = {
33
33
  "scene-too-short": { severity: "warning", wording: "The scene ends before its target length." },
34
34
  "unknown-control-color": { severity: "warning", wording: "A control stands for a colour the scene never uses." },
35
35
  "empty-shot": { severity: "note", wording: "A shot neither moves the camera nor runs a command." },
36
- "camera-aspect": { severity: "note", wording: "A camera rectangle has a different shape from the artwork, so it is letterboxed." }
36
+ "camera-aspect": { severity: "note", wording: "A camera rectangle has a different shape from the artwork, so it is letterboxed." },
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
+ "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
+ "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." }
37
41
  };
38
42
  function motionIssue(code, message, extra = {}) {
39
43
  return { code, severity: MOTION_ISSUE_CODES[code].severity, message, ...extra };