@pixerate/editor 0.73.0 → 0.75.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 (59) hide show
  1. package/THIRD_PARTY_NOTICES.md +3 -0
  2. package/dist/animation-editor/index.cjs +928 -399
  3. package/dist/animation-editor/index.cjs.map +1 -1
  4. package/dist/animation-editor/index.d.cts +130 -3
  5. package/dist/animation-editor/index.d.ts +130 -3
  6. package/dist/animation-editor/index.js +819 -290
  7. package/dist/animation-editor/index.js.map +1 -1
  8. package/dist/canvas/index.cjs +26 -26
  9. package/dist/canvas/index.js +2 -2
  10. package/dist/chart/index.cjs +11 -9
  11. package/dist/chart/index.cjs.map +1 -1
  12. package/dist/chart/index.js +5 -3
  13. package/dist/chart/index.js.map +1 -1
  14. package/dist/{chunk-3S72F46R.js → chunk-2GFUA4X5.js} +2 -322
  15. package/dist/chunk-2GFUA4X5.js.map +1 -0
  16. package/dist/chunk-33AHNSEB.js +323 -0
  17. package/dist/chunk-33AHNSEB.js.map +1 -0
  18. package/dist/{chunk-3TLL5OID.js → chunk-4JLB2USO.js} +2 -2
  19. package/dist/{chunk-NN3JGUSX.cjs → chunk-5PTR7HTL.cjs} +3 -323
  20. package/dist/chunk-5PTR7HTL.cjs.map +1 -0
  21. package/dist/{chunk-YJLCBA7V.cjs → chunk-7MD7C6XQ.cjs} +87 -87
  22. package/dist/{chunk-YJLCBA7V.cjs.map → chunk-7MD7C6XQ.cjs.map} +1 -1
  23. package/dist/{chunk-4YFMSPBU.js → chunk-EIXXBKUB.js} +6 -4
  24. package/dist/{chunk-4YFMSPBU.js.map → chunk-EIXXBKUB.js.map} +1 -1
  25. package/dist/chunk-EMQWJA3G.cjs +323 -0
  26. package/dist/chunk-EMQWJA3G.cjs.map +1 -0
  27. package/dist/{chunk-BQQZ6MOI.js → chunk-FGW5OWJ4.js} +6 -4
  28. package/dist/{chunk-BQQZ6MOI.js.map → chunk-FGW5OWJ4.js.map} +1 -1
  29. package/dist/{chunk-YZZMX6V7.cjs → chunk-FV5AXTSF.cjs} +11 -9
  30. package/dist/chunk-FV5AXTSF.cjs.map +1 -0
  31. package/dist/{chunk-MLREO6J3.cjs → chunk-JEYDXRWQ.cjs} +46 -46
  32. package/dist/{chunk-MLREO6J3.cjs.map → chunk-JEYDXRWQ.cjs.map} +1 -1
  33. package/dist/{chunk-7ZSU4MQZ.cjs → chunk-PL3MPFPA.cjs} +10 -8
  34. package/dist/chunk-PL3MPFPA.cjs.map +1 -0
  35. package/dist/{chunk-O2UH7LZE.cjs → chunk-XGD3FSNC.cjs} +239 -239
  36. package/dist/chunk-XGD3FSNC.cjs.map +1 -0
  37. package/dist/{chunk-VWZB6BCY.js → chunk-Y3NW4VJE.js} +2 -2
  38. package/dist/{chunk-GTMJ4Y3S.js → chunk-YCSFS3GK.js} +244 -244
  39. package/dist/{chunk-GTMJ4Y3S.js.map → chunk-YCSFS3GK.js.map} +1 -1
  40. package/dist/export/index.cjs +4 -3
  41. package/dist/export/index.cjs.map +1 -1
  42. package/dist/export/index.js +3 -2
  43. package/dist/extensions/index.cjs +4 -3
  44. package/dist/extensions/index.cjs.map +1 -1
  45. package/dist/extensions/index.js +3 -2
  46. package/dist/index.cjs +19 -18
  47. package/dist/index.cjs.map +1 -1
  48. package/dist/index.js +12 -11
  49. package/dist/index.js.map +1 -1
  50. package/dist/vector/index.cjs +532 -532
  51. package/dist/vector/index.js +3 -3
  52. package/package.json +1 -1
  53. package/dist/chunk-3S72F46R.js.map +0 -1
  54. package/dist/chunk-7ZSU4MQZ.cjs.map +0 -1
  55. package/dist/chunk-NN3JGUSX.cjs.map +0 -1
  56. package/dist/chunk-O2UH7LZE.cjs.map +0 -1
  57. package/dist/chunk-YZZMX6V7.cjs.map +0 -1
  58. /package/dist/{chunk-3TLL5OID.js.map → chunk-4JLB2USO.js.map} +0 -0
  59. /package/dist/{chunk-VWZB6BCY.js.map → chunk-Y3NW4VJE.js.map} +0 -0
@@ -18,14 +18,19 @@
18
18
 
19
19
 
20
20
 
21
- var _chunkYJLCBA7Vcjs = require('../chunk-YJLCBA7V.cjs');
22
21
 
23
22
 
23
+ var _chunk7MD7C6XQcjs = require('../chunk-7MD7C6XQ.cjs');
24
24
 
25
25
 
26
26
 
27
27
 
28
- var _chunkO2UH7LZEcjs = require('../chunk-O2UH7LZE.cjs');
28
+
29
+
30
+ var _chunkXGD3FSNCcjs = require('../chunk-XGD3FSNC.cjs');
31
+
32
+
33
+ var _chunkEMQWJA3Gcjs = require('../chunk-EMQWJA3G.cjs');
29
34
 
30
35
 
31
36
  var _chunkQKLRAA4Tcjs = require('../chunk-QKLRAA4T.cjs');
@@ -56,6 +61,8 @@ var _chunk2FHLHZALcjs = require('../chunk-2FHLHZAL.cjs');
56
61
 
57
62
  // src/animation-editor/types.ts
58
63
  var MOTION_TRIGGERS = ["click", "hover", "press", "scroll"];
64
+ var MOTION_TEXT_UNITS = ["letter", "word", "line"];
65
+ var MOTION_CASCADES = ["forward", "backward", "center", "edges", "random"];
59
66
  var MOTION_DRIVEN_PROPERTIES = ["x", "y", "rotation", "scaleX", "scaleY", "opacity"];
60
67
  var MOTION_CLIP_TARGET = "$";
61
68
  var MOTION_COMMAND_TYPES = [
@@ -112,21 +119,21 @@ var MOTION_DEFAULT_HOLD = 600;
112
119
  var MOTION_CAMERA_MS = 1e3;
113
120
  var MOTION_DRAW_MS_PER_UNIT = 1.5;
114
121
  var MOTION_DRAW_RANGE = [500, 2500];
115
- function motionFlairPrimitive(command2) {
116
- switch (command2.type) {
122
+ function motionFlairPrimitive(command3) {
123
+ switch (command3.type) {
117
124
  case "enter":
118
- return `entrance.${_nullishCoalesce(command2.style, () => ( "fade"))}`;
125
+ return `entrance.${_nullishCoalesce(command3.style, () => ( "fade"))}`;
119
126
  case "exit":
120
- return `exit.${_nullishCoalesce(command2.style, () => ( "fade"))}`;
127
+ return `exit.${_nullishCoalesce(command3.style, () => ( "fade"))}`;
121
128
  case "pulse":
122
129
  case "shake":
123
130
  case "teeter":
124
131
  case "wave":
125
132
  case "wiggle":
126
133
  case "color-pulse":
127
- return `emphasis.${command2.type}`;
134
+ return `emphasis.${command3.type}`;
128
135
  case "rotate":
129
- return command2.by !== void 0 ? "emphasis.spin" : void 0;
136
+ return command3.by !== void 0 ? "emphasis.spin" : void 0;
130
137
  case "follow":
131
138
  return "path.follow";
132
139
  default:
@@ -143,6 +150,7 @@ var MOTION_ISSUE_CODES = {
143
150
  "not-a-path": { severity: "error", wording: "A follow path must be a path node, not a group." },
144
151
  "reveal-no-box": { severity: "warning", wording: "A wipe, iris or split cannot clip a node with no width or height (a lone straight line), so it fades instead." },
145
152
  "driver-cycle": { severity: "error", wording: "Drivers follow each other in a loop, so none of them has a value." },
153
+ "split-not-text": { severity: "warning", wording: "A command splits a target into letters, but it is not a group of letter paths, so it animates as a whole." },
146
154
  "unknown-clip": { severity: "error", wording: "A play command names a clip the scene does not have." },
147
155
  "unknown-shape": { severity: "error", wording: "A morph names a shape the artwork does not have." },
148
156
  "match-self": { severity: "error", wording: "A match turns a group into itself or into a group it holds (or that holds it)." },
@@ -224,7 +232,7 @@ ${problems.map((p) => `- ${p}`).join("\n")}`);
224
232
  var SCENE_KEYS = ["version", "name", "description", "artwork", "shots", "clips", "drivers", "motionBlur", "loop", "trigger", "fps", "steps", "beat", "narration", "target", "controls", "presets"];
225
233
  var SHOT_KEYS = ["id", "label", "camera", "at", "run", "hold", "speed"];
226
234
  var CAMERA_KEYS = ["x", "y", "width", "height", "frame", "padding", "ease", "ms", "geometric"];
227
- var BASE_KEYS = ["type", "targets", "duration", "delay", "stagger", "with", "ease", "note", "repeat", "repeatMode", "steps"];
235
+ var BASE_KEYS = ["type", "targets", "duration", "delay", "stagger", "with", "ease", "note", "repeat", "repeatMode", "steps", "split", "cascade"];
228
236
  var DRAW_ORDER_KEYS = ["order", "pace", "align", "sortBy", "origin"];
229
237
  var COMMAND_KEYS = {
230
238
  enter: [...BASE_KEYS, ...DRAW_ORDER_KEYS, "style", "direction", "distance", "scale", "shape", "orientation"],
@@ -254,108 +262,111 @@ var isPoint = (v) => isObject(v) && isNum(v.x) && isNum(v.y);
254
262
  function unknownKeys(value, known, where, problems) {
255
263
  for (const key of Object.keys(value)) if (!known.includes(key)) problems.push(`${where}: unknown field ${namedWithSuggestion(key, known)}.`);
256
264
  }
257
- function commandShapeProblems(command2, where) {
265
+ function commandShapeProblems(command3, where) {
258
266
  const problems = [];
259
- if (!isObject(command2)) return [`${where} must be an object.`];
260
- const type = command2.type;
267
+ if (!isObject(command3)) return [`${where} must be an object.`];
268
+ const type = command3.type;
261
269
  if (typeof type !== "string" || !MOTION_COMMAND_TYPES.includes(type)) {
262
270
  return [`${where}: unknown type ${typeof type === "string" ? namedWithSuggestion(type, MOTION_COMMAND_TYPES) : String(type)}; use one of ${MOTION_COMMAND_TYPES.join(", ")}.`];
263
271
  }
264
- unknownKeys(command2, COMMAND_KEYS[type], `${where} (${type})`, problems);
272
+ unknownKeys(command3, COMMAND_KEYS[type], `${where} (${type})`, problems);
265
273
  const p = (msg) => problems.push(`${where} (${type}): ${msg}`);
266
274
  const nonNegative = (key) => {
267
- if (command2[key] !== void 0 && !(isNum(command2[key]) && command2[key] >= 0)) p(`"${key}" must be a number of milliseconds \u2265 0.`);
275
+ if (command3[key] !== void 0 && !(isNum(command3[key]) && command3[key] >= 0)) p(`"${key}" must be a number of milliseconds \u2265 0.`);
268
276
  };
269
277
  const fraction = (key) => {
270
- if (command2[key] !== void 0 && !(isNum(command2[key]) && command2[key] >= 0 && command2[key] <= 1)) p(`"${key}" must be a fraction from 0 to 1.`);
278
+ if (command3[key] !== void 0 && !(isNum(command3[key]) && command3[key] >= 0 && command3[key] <= 1)) p(`"${key}" must be a fraction from 0 to 1.`);
271
279
  };
272
280
  const optionalNumber = (key) => {
273
- if (command2[key] !== void 0 && !isNum(command2[key])) p(`"${key}" must be a number.`);
281
+ if (command3[key] !== void 0 && !isNum(command3[key])) p(`"${key}" must be a number.`);
274
282
  };
275
- if (command2.with !== void 0 && command2.with !== "after" && command2.with !== "previous") p(`"with" must be "after" or "previous".`);
276
- if (DRAW_ORDER_KEYS.some((k) => command2[k] !== void 0)) {
277
- if (type === "enter" && command2.style !== "draw" || type === "exit" && command2.style !== "undraw") p(`"order", "pace", "align", "sortBy" and "origin" go with drawing: style "${type === "enter" ? "draw" : "undraw"}".`);
278
- if (command2.order !== void 0 && command2.order !== "together" && command2.order !== "sequence") p(`"order" must be "together" or "sequence" (one stroke after another).`);
279
- if (command2.pace !== void 0 && command2.pace !== "time" && command2.pace !== "speed") p(`"pace" must be "time" (each stroke takes the whole time) or "speed" (all at one speed).`);
280
- if (command2.align !== void 0 && command2.align !== "start" && command2.align !== "end") p(`"align" must be "start" or "end".`);
281
- if (command2.sortBy !== void 0 && command2.sortBy !== "document" && command2.sortBy !== "distance") p(`"sortBy" must be "document" or "distance".`);
282
- if (command2.origin !== void 0 && !isPoint(command2.origin)) p(`"origin" must be { "x": number, "y": number } (document units).`);
283
+ if (command3.with !== void 0 && command3.with !== "after" && command3.with !== "previous") p(`"with" must be "after" or "previous".`);
284
+ if (DRAW_ORDER_KEYS.some((k) => command3[k] !== void 0)) {
285
+ if (type === "enter" && command3.style !== "draw" || type === "exit" && command3.style !== "undraw") p(`"order", "pace", "align", "sortBy" and "origin" go with drawing: style "${type === "enter" ? "draw" : "undraw"}".`);
286
+ if (command3.order !== void 0 && command3.order !== "together" && command3.order !== "sequence") p(`"order" must be "together" or "sequence" (one stroke after another).`);
287
+ if (command3.pace !== void 0 && command3.pace !== "time" && command3.pace !== "speed") p(`"pace" must be "time" (each stroke takes the whole time) or "speed" (all at one speed).`);
288
+ if (command3.align !== void 0 && command3.align !== "start" && command3.align !== "end") p(`"align" must be "start" or "end".`);
289
+ if (command3.sortBy !== void 0 && command3.sortBy !== "document" && command3.sortBy !== "distance") p(`"sortBy" must be "document" or "distance".`);
290
+ if (command3.origin !== void 0 && !isPoint(command3.origin)) p(`"origin" must be { "x": number, "y": number } (document units).`);
283
291
  }
284
- if (command2.note !== void 0 && typeof command2.note !== "string") p(`"note" must be a string.`);
292
+ if (command3.note !== void 0 && typeof command3.note !== "string") p(`"note" must be a string.`);
285
293
  if (type === "wait") {
286
- if (!isNum(command2.duration) || command2.duration < 0) p(`"duration" must be a number of milliseconds \u2265 0.`);
294
+ if (!isNum(command3.duration) || command3.duration < 0) p(`"duration" must be a number of milliseconds \u2265 0.`);
287
295
  return problems;
288
296
  }
289
- if (!Array.isArray(command2.targets) || command2.targets.length === 0 || !command2.targets.every((t) => typeof t === "string" && t.length > 0)) p(`"targets" must be a non-empty list of node ids.`);
297
+ if (!Array.isArray(command3.targets) || command3.targets.length === 0 || !command3.targets.every((t) => typeof t === "string" && t.length > 0)) p(`"targets" must be a non-empty list of node ids.`);
290
298
  nonNegative("duration");
291
299
  nonNegative("delay");
292
300
  nonNegative("stagger");
293
- if (command2.ease !== void 0 && !_chunk2FHLHZALcjs.isAnimationEasing.call(void 0, command2.ease)) p(`"ease" is not an easing; use a preset such as "ease-in-out" or "spring-bouncy", or { "type": "cubic-bezier", \u2026 } or { "type": "spring", \u2026 }.`);
294
- if (command2.repeat !== void 0) {
301
+ if (command3.ease !== void 0 && !_chunk2FHLHZALcjs.isAnimationEasing.call(void 0, command3.ease)) p(`"ease" is not an easing; use a preset such as "ease-in-out" or "spring-bouncy", or { "type": "cubic-bezier", \u2026 } or { "type": "spring", \u2026 }.`);
302
+ if (command3.repeat !== void 0) {
295
303
  if (type === "enter" || type === "exit") p(`"repeat" is not for ${type} (it brings things in or out once); repeat an emphasis or a move instead.`);
296
- else if (!(Number.isInteger(command2.repeat) && command2.repeat >= 1 && command2.repeat <= 1e3)) p(`"repeat" must be a whole number from 1 to 1000.`);
304
+ else if (!(Number.isInteger(command3.repeat) && command3.repeat >= 1 && command3.repeat <= 1e3)) p(`"repeat" must be a whole number from 1 to 1000.`);
297
305
  }
298
- if (command2.blend !== void 0 && !MOTION_BLENDS.includes(command2.blend)) p(`"blend" must be "replace" (the change takes over) or "add" (a layer on top).`);
299
- if (command2.repeatMode !== void 0 && !MOTION_REPEAT_MODES.includes(command2.repeatMode)) p(`"repeatMode" must be one of ${MOTION_REPEAT_MODES.join(", ")}.`);
300
- if (command2.steps !== void 0 && !(isNum(command2.steps) && command2.steps > 0 && command2.steps <= 120)) p(`"steps" must be frames per second from above 0 to 120 (12 holds each pose for two frames at 24 fps).`);
306
+ if (command3.blend !== void 0 && !MOTION_BLENDS.includes(command3.blend)) p(`"blend" must be "replace" (the change takes over) or "add" (a layer on top).`);
307
+ if (command3.split !== void 0 && !MOTION_TEXT_UNITS.includes(command3.split)) p(`"split" must be letter, word or line (for outlined text: a group of letter paths).`);
308
+ if (command3.cascade !== void 0 && !MOTION_CASCADES.includes(command3.cascade)) p(`"cascade" must be one of ${MOTION_CASCADES.join(", ")}.`);
309
+ if (command3.cascade !== void 0 && command3.split === void 0) p(`"cascade" goes with "split".`);
310
+ if (command3.repeatMode !== void 0 && !MOTION_REPEAT_MODES.includes(command3.repeatMode)) p(`"repeatMode" must be one of ${MOTION_REPEAT_MODES.join(", ")}.`);
311
+ if (command3.steps !== void 0 && !(isNum(command3.steps) && command3.steps > 0 && command3.steps <= 120)) p(`"steps" must be frames per second from above 0 to 120 (12 holds each pose for two frames at 24 fps).`);
301
312
  switch (type) {
302
313
  case "enter":
303
314
  case "exit": {
304
315
  const styles = type === "enter" ? MOTION_ENTER_STYLES : MOTION_EXIT_STYLES;
305
- if (command2.style !== void 0 && !(typeof command2.style === "string" && styles.includes(command2.style)))
306
- p(`"style" must be one of ${styles.join(", ")}${typeof command2.style === "string" ? ` (got ${namedWithSuggestion(command2.style, styles)})` : ""}.`);
307
- if (command2.direction !== void 0 && !MOTION_DIRECTIONS.includes(command2.direction)) p(`"direction" must be one of ${MOTION_DIRECTIONS.join(", ")}.`);
316
+ if (command3.style !== void 0 && !(typeof command3.style === "string" && styles.includes(command3.style)))
317
+ p(`"style" must be one of ${styles.join(", ")}${typeof command3.style === "string" ? ` (got ${namedWithSuggestion(command3.style, styles)})` : ""}.`);
318
+ if (command3.direction !== void 0 && !MOTION_DIRECTIONS.includes(command3.direction)) p(`"direction" must be one of ${MOTION_DIRECTIONS.join(", ")}.`);
308
319
  optionalNumber("distance");
309
- if (command2.scale !== void 0 && !(isNum(command2.scale) && command2.scale >= 0)) p(`"scale" must be a number \u2265 0 (the starting scale for pop and zoom).`);
310
- if (command2.shape !== void 0 && !MOTION_IRIS_SHAPES.includes(command2.shape)) p(`"shape" must be one of ${MOTION_IRIS_SHAPES.join(", ")} (the opening of an iris).`);
311
- if (command2.orientation !== void 0 && !MOTION_SPLIT_ORIENTATIONS.includes(command2.orientation)) p(`"orientation" must be one of ${MOTION_SPLIT_ORIENTATIONS.join(", ")} (the line a split opens from).`);
320
+ if (command3.scale !== void 0 && !(isNum(command3.scale) && command3.scale >= 0)) p(`"scale" must be a number \u2265 0 (the starting scale for pop and zoom).`);
321
+ if (command3.shape !== void 0 && !MOTION_IRIS_SHAPES.includes(command3.shape)) p(`"shape" must be one of ${MOTION_IRIS_SHAPES.join(", ")} (the opening of an iris).`);
322
+ if (command3.orientation !== void 0 && !MOTION_SPLIT_ORIENTATIONS.includes(command3.orientation)) p(`"orientation" must be one of ${MOTION_SPLIT_ORIENTATIONS.join(", ")} (the line a split opens from).`);
312
323
  break;
313
324
  }
314
325
  case "draw":
315
326
  fraction("from");
316
327
  fraction("to");
317
328
  fraction("comet");
318
- if (command2.comet !== void 0 && (command2.from !== void 0 || command2.to !== void 0)) p(`"comet" runs the whole path; leave out "from" and "to".`);
329
+ if (command3.comet !== void 0 && (command3.from !== void 0 || command3.to !== void 0)) p(`"comet" runs the whole path; leave out "from" and "to".`);
319
330
  break;
320
331
  case "move":
321
- if (command2.by === void 0 && command2.to === void 0) p(`give "by" (an offset) or "to" (a point).`);
322
- if (command2.by !== void 0 && command2.to !== void 0) p(`give "by" or "to", not both.`);
323
- if (command2.by !== void 0 && !isPoint(command2.by)) p(`"by" must be { "x": number, "y": number }.`);
324
- if (command2.to !== void 0 && !isPoint(command2.to)) p(`"to" must be { "x": number, "y": number }.`);
325
- if (command2.via !== void 0 && !(Array.isArray(command2.via) && command2.via.length > 0 && command2.via.length <= 64 && command2.via.every(isPoint))) p(`"via" must be a list of 1 to 64 points { "x": number, "y": number } (document units).`);
326
- if (command2.pacing !== void 0 && command2.pacing !== "even" && command2.pacing !== "equal") p(`"pacing" must be "even" (steady speed) or "equal" (each leg the same time).`);
327
- if (command2.pacing !== void 0 && command2.via === void 0) p(`"pacing" goes with "via".`);
332
+ if (command3.by === void 0 && command3.to === void 0) p(`give "by" (an offset) or "to" (a point).`);
333
+ if (command3.by !== void 0 && command3.to !== void 0) p(`give "by" or "to", not both.`);
334
+ if (command3.by !== void 0 && !isPoint(command3.by)) p(`"by" must be { "x": number, "y": number }.`);
335
+ if (command3.to !== void 0 && !isPoint(command3.to)) p(`"to" must be { "x": number, "y": number }.`);
336
+ if (command3.via !== void 0 && !(Array.isArray(command3.via) && command3.via.length > 0 && command3.via.length <= 64 && command3.via.every(isPoint))) p(`"via" must be a list of 1 to 64 points { "x": number, "y": number } (document units).`);
337
+ if (command3.pacing !== void 0 && command3.pacing !== "even" && command3.pacing !== "equal") p(`"pacing" must be "even" (steady speed) or "equal" (each leg the same time).`);
338
+ if (command3.pacing !== void 0 && command3.via === void 0) p(`"pacing" goes with "via".`);
328
339
  break;
329
340
  case "rotate":
330
- if (command2.by === void 0 && command2.to === void 0) p(`give "by" or "to" (degrees).`);
331
- if (command2.by !== void 0 && command2.to !== void 0) p(`give "by" or "to", not both.`);
341
+ if (command3.by === void 0 && command3.to === void 0) p(`give "by" or "to" (degrees).`);
342
+ if (command3.by !== void 0 && command3.to !== void 0) p(`give "by" or "to", not both.`);
332
343
  optionalNumber("by");
333
344
  optionalNumber("to");
334
345
  break;
335
346
  case "scale":
336
- if (!(isNum(command2.to) || isPoint(command2.to))) p(`"to" must be a number or { "x": number, "y": number }.`);
337
- if (command2.geometric !== void 0 && typeof command2.geometric !== "boolean") p(`"geometric" must be true or false.`);
347
+ if (!(isNum(command3.to) || isPoint(command3.to))) p(`"to" must be a number or { "x": number, "y": number }.`);
348
+ if (command3.geometric !== void 0 && typeof command3.geometric !== "boolean") p(`"geometric" must be true or false.`);
338
349
  break;
339
350
  case "fade":
340
- if (!(isNum(command2.to) && command2.to >= 0 && command2.to <= 1)) p(`"to" must be an opacity from 0 to 1.`);
351
+ if (!(isNum(command3.to) && command3.to >= 0 && command3.to <= 1)) p(`"to" must be an opacity from 0 to 1.`);
341
352
  break;
342
353
  case "style":
343
- if (command2.fill === void 0 && command2.stroke === void 0 && command2.strokeWidth === void 0) p(`give at least one of "fill", "stroke" or "strokeWidth".`);
344
- if (command2.fill !== void 0 && typeof command2.fill !== "string") p(`"fill" must be a CSS colour.`);
345
- if (command2.stroke !== void 0 && typeof command2.stroke !== "string") p(`"stroke" must be a CSS colour.`);
346
- if (command2.strokeWidth !== void 0 && !(isNum(command2.strokeWidth) && command2.strokeWidth >= 0)) p(`"strokeWidth" must be a number \u2265 0.`);
354
+ if (command3.fill === void 0 && command3.stroke === void 0 && command3.strokeWidth === void 0) p(`give at least one of "fill", "stroke" or "strokeWidth".`);
355
+ if (command3.fill !== void 0 && typeof command3.fill !== "string") p(`"fill" must be a CSS colour.`);
356
+ if (command3.stroke !== void 0 && typeof command3.stroke !== "string") p(`"stroke" must be a CSS colour.`);
357
+ if (command3.strokeWidth !== void 0 && !(isNum(command3.strokeWidth) && command3.strokeWidth >= 0)) p(`"strokeWidth" must be a number \u2265 0.`);
347
358
  break;
348
359
  case "play":
349
- if (typeof command2.clip !== "string" || !command2.clip) p(`"clip" must be the name of one of the scene's clips.`);
350
- if (command2.speed !== void 0 && !(isNum(command2.speed) && command2.speed >= 0.05 && command2.speed <= 20)) p(`"speed" must be a number from 0.05 to 20 (2 plays the clip twice as fast).`);
360
+ if (typeof command3.clip !== "string" || !command3.clip) p(`"clip" must be the name of one of the scene's clips.`);
361
+ if (command3.speed !== void 0 && !(isNum(command3.speed) && command3.speed >= 0.05 && command3.speed <= 20)) p(`"speed" must be a number from 0.05 to 20 (2 plays the clip twice as fast).`);
351
362
  break;
352
363
  case "morph":
353
364
  case "match":
354
- if (typeof command2.into !== "string" || !command2.into) p(`"into" must be the id of a ${type === "match" ? "group to become" : "node whose shape to take"}.`);
365
+ if (typeof command3.into !== "string" || !command3.into) p(`"into" must be the id of a ${type === "match" ? "group to become" : "node whose shape to take"}.`);
355
366
  break;
356
367
  case "follow":
357
- if (typeof command2.path !== "string" || !command2.path) p(`"path" must be the id of a path node.`);
358
- if (command2.rotate !== void 0 && typeof command2.rotate !== "boolean") p(`"rotate" must be true or false.`);
368
+ if (typeof command3.path !== "string" || !command3.path) p(`"path" must be the id of a path node.`);
369
+ if (command3.rotate !== void 0 && typeof command3.rotate !== "boolean") p(`"rotate" must be true or false.`);
359
370
  fraction("from");
360
371
  fraction("to");
361
372
  break;
@@ -366,22 +377,22 @@ function commandShapeProblems(command2, where) {
366
377
  case "color-pulse":
367
378
  optionalNumber("amount");
368
379
  optionalNumber("angle");
369
- if (command2.count !== void 0 && !(Number.isInteger(command2.count) && command2.count >= 1)) p(`"count" must be a whole number \u2265 1.`);
370
- if (command2.axis !== void 0 && command2.axis !== "horizontal" && command2.axis !== "vertical") p(`"axis" must be "horizontal" or "vertical".`);
371
- if (command2.color !== void 0 && typeof command2.color !== "string") p(`"color" must be a CSS colour.`);
372
- if (command2.paint !== void 0 && command2.paint !== "fill" && command2.paint !== "stroke") p(`"paint" must be "fill" or "stroke".`);
380
+ if (command3.count !== void 0 && !(Number.isInteger(command3.count) && command3.count >= 1)) p(`"count" must be a whole number \u2265 1.`);
381
+ if (command3.axis !== void 0 && command3.axis !== "horizontal" && command3.axis !== "vertical") p(`"axis" must be "horizontal" or "vertical".`);
382
+ if (command3.color !== void 0 && typeof command3.color !== "string") p(`"color" must be a CSS colour.`);
383
+ if (command3.paint !== void 0 && command3.paint !== "fill" && command3.paint !== "stroke") p(`"paint" must be "fill" or "stroke".`);
373
384
  break;
374
385
  case "wiggle":
375
386
  optionalNumber("amount");
376
387
  optionalNumber("rotate");
377
- if (command2.frequency !== void 0 && !(isNum(command2.frequency) && command2.frequency > 0)) p(`"frequency" must be a number of Hz > 0.`);
378
- if (command2.octaves !== void 0 && !(Number.isInteger(command2.octaves) && command2.octaves >= 1 && command2.octaves <= 8)) p(`"octaves" must be a whole number from 1 to 8.`);
379
- if (command2.seed !== void 0 && !(Number.isInteger(command2.seed) && command2.seed >= 0)) p(`"seed" must be a whole number \u2265 0.`);
388
+ if (command3.frequency !== void 0 && !(isNum(command3.frequency) && command3.frequency > 0)) p(`"frequency" must be a number of Hz > 0.`);
389
+ if (command3.octaves !== void 0 && !(Number.isInteger(command3.octaves) && command3.octaves >= 1 && command3.octaves <= 8)) p(`"octaves" must be a whole number from 1 to 8.`);
390
+ if (command3.seed !== void 0 && !(Number.isInteger(command3.seed) && command3.seed >= 0)) p(`"seed" must be a whole number \u2265 0.`);
380
391
  break;
381
392
  case "effect":
382
- if (typeof command2.effect !== "string" || !command2.effect) p(`"effect" must be the id of one of the node's effects.`);
383
- if (typeof command2.param !== "string" || !command2.param) p(`"param" must name a numeric parameter of the effect.`);
384
- if (!isNum(command2.to)) p(`"to" must be a number.`);
393
+ if (typeof command3.effect !== "string" || !command3.effect) p(`"effect" must be the id of one of the node's effects.`);
394
+ if (typeof command3.param !== "string" || !command3.param) p(`"param" must name a numeric parameter of the effect.`);
395
+ if (!isNum(command3.to)) p(`"to" must be a number.`);
385
396
  break;
386
397
  }
387
398
  return problems;
@@ -398,9 +409,9 @@ function clipsShapeProblems(clips) {
398
409
  }
399
410
  unknownKeys(clip, ["description", "run"], where, problems);
400
411
  if (clip.description !== void 0 && typeof clip.description !== "string") problems.push(`${where}: "description" must be a string.`);
401
- clip.run.forEach((command2, i) => {
402
- if (isObject(command2) && command2.type === "play") problems.push(`${where}, command ${i + 1}: a clip cannot play another clip; copy its commands in instead.`);
403
- else problems.push(...commandShapeProblems(command2, `${where}, command ${i + 1}`));
412
+ clip.run.forEach((command3, i) => {
413
+ if (isObject(command3) && command3.type === "play") problems.push(`${where}, command ${i + 1}: a clip cannot play another clip; copy its commands in instead.`);
414
+ else problems.push(...commandShapeProblems(command3, `${where}, command ${i + 1}`));
404
415
  });
405
416
  }
406
417
  return problems;
@@ -456,7 +467,7 @@ function shotShapeProblems(shot, where) {
456
467
  }
457
468
  }
458
469
  if (!Array.isArray(shot.run)) problems.push(`${name}: "run" must be a list of commands (it may be empty).`);
459
- else shot.run.forEach((command2, i) => problems.push(...commandShapeProblems(command2, `${name}, command ${i + 1}`)));
470
+ else shot.run.forEach((command3, i) => problems.push(...commandShapeProblems(command3, `${name}, command ${i + 1}`)));
460
471
  return problems;
461
472
  }
462
473
  function motionSceneShapeProblems(input) {
@@ -523,7 +534,7 @@ function motionSceneToJson(scene, space = 2) {
523
534
  return JSON.stringify(scene, null, space);
524
535
  }
525
536
  var isMotionShot = (shot) => shotShapeProblems(shot, "Shot").length === 0;
526
- var isMotionCommand = (command2) => commandShapeProblems(command2, "Command").length === 0;
537
+ var isMotionCommand = (command3) => commandShapeProblems(command3, "Command").length === 0;
527
538
 
528
539
  // src/animation-editor/match.ts
529
540
  var SEPARATOR = /[-_/.:]/;
@@ -611,6 +622,87 @@ function smoothMotionTimes(points, start, duration, pacing = "even") {
611
622
  return times;
612
623
  }
613
624
 
625
+ // src/animation-editor/text.ts
626
+ function motionTextLayout(nodes, id) {
627
+ const group = _optionalChain([nodes, 'access', _7 => _7.get, 'call', _8 => _8(id), 'optionalAccess', _9 => _9.node]);
628
+ if (!group || group.type !== "group") return null;
629
+ const letters = _chunk7MD7C6XQcjs.collectPathNodes.call(void 0, group).map((p) => ({ id: p.id, b: _optionalChain([nodes, 'access', _10 => _10.get, 'call', _11 => _11(p.id), 'optionalAccess', _12 => _12.bounds]) })).filter((l) => !!l.b && l.b.width + l.b.height > 0);
630
+ if (!letters.length) return null;
631
+ const lines = [];
632
+ for (const l of [...letters].sort((a, c) => a.b.y + a.b.height / 2 - (c.b.y + c.b.height / 2))) {
633
+ const cy = l.b.y + l.b.height / 2;
634
+ const line = lines.find((x) => cy >= x.top && cy <= x.bottom);
635
+ if (line) {
636
+ line.letters.push(l);
637
+ line.top = Math.min(line.top, l.b.y);
638
+ line.bottom = Math.max(line.bottom, l.b.y + l.b.height);
639
+ } else lines.push({ top: l.b.y, bottom: l.b.y + l.b.height, letters: [l] });
640
+ }
641
+ lines.sort((a, c) => a.top - c.top);
642
+ return {
643
+ lines: lines.map((line) => {
644
+ const row = [...line.letters].sort((a, c) => a.b.x - c.b.x);
645
+ const heights = row.map((l) => l.b.height).sort((a, c) => a - c);
646
+ const gap = heights[Math.floor(heights.length / 2)] * 0.35;
647
+ const words = [];
648
+ row.forEach((l, i) => {
649
+ const prev = row[i - 1];
650
+ if (!prev || l.b.x - (prev.b.x + prev.b.width) > gap) words.push({ letters: [] });
651
+ words[words.length - 1].letters.push(l.id);
652
+ });
653
+ return { words };
654
+ })
655
+ };
656
+ }
657
+ function motionTextUnits(layout, by) {
658
+ if (by === "line") return layout.lines.map((l) => l.words.flatMap((w) => w.letters));
659
+ if (by === "word") return layout.lines.flatMap((l) => l.words.map((w) => w.letters));
660
+ return layout.lines.flatMap((l) => l.words.flatMap((w) => w.letters.map((id) => [id])));
661
+ }
662
+ function motionCascade(units, cascade = "forward", seed = 1) {
663
+ const n = units.length;
664
+ const mid = (n - 1) / 2;
665
+ const indexed = units.map((u, i) => ({ u, i }));
666
+ if (cascade === "backward") return [...units].reverse();
667
+ if (cascade === "center") return indexed.sort((a, b) => Math.abs(a.i - mid) - Math.abs(b.i - mid) || a.i - b.i).map((x) => x.u);
668
+ if (cascade === "edges") return indexed.sort((a, b) => Math.abs(b.i - mid) - Math.abs(a.i - mid) || a.i - b.i).map((x) => x.u);
669
+ if (cascade === "random") {
670
+ let s = seed >>> 0 || 1;
671
+ const rand = () => {
672
+ s = s + 1831565813 >>> 0;
673
+ let t = s;
674
+ t = Math.imul(t ^ t >>> 15, t | 1);
675
+ t ^= t + Math.imul(t ^ t >>> 7, t | 61);
676
+ return ((t ^ t >>> 14) >>> 0) / 4294967296;
677
+ };
678
+ const out = [...units];
679
+ for (let i = n - 1; i > 0; i--) {
680
+ const j = Math.floor(rand() * (i + 1));
681
+ [out[i], out[j]] = [out[j], out[i]];
682
+ }
683
+ return out;
684
+ }
685
+ return [...units];
686
+ }
687
+ var MOTION_TEXT_STAGGER = { letter: 40, word: 120, line: 300 };
688
+ function motionCommandSlots(nodes, command3) {
689
+ if (!command3.split) return { slots: command3.targets.map((id, k) => ({ id, slot: k })), stagger: _nullishCoalesce(command3.stagger, () => ( 0)) };
690
+ const slots = [];
691
+ let slot = 0;
692
+ for (const target of command3.targets) {
693
+ const layout = motionTextLayout(nodes, target);
694
+ if (!layout) {
695
+ slots.push({ id: target, slot: slot++ });
696
+ continue;
697
+ }
698
+ for (const unit of motionCascade(motionTextUnits(layout, command3.split), command3.cascade, _chunkZCNLBRT6cjs.motionSeedFromId.call(void 0, target))) {
699
+ for (const id of unit) slots.push({ id, slot });
700
+ slot++;
701
+ }
702
+ }
703
+ return { slots, stagger: _nullishCoalesce(command3.stagger, () => ( MOTION_TEXT_STAGGER[command3.split])) };
704
+ }
705
+
614
706
  // src/animation-editor/compile.ts
615
707
  var indexCache = /* @__PURE__ */ new WeakMap();
616
708
  function indexMotionNodes(nodes) {
@@ -619,21 +711,21 @@ function indexMotionNodes(nodes) {
619
711
  const out = /* @__PURE__ */ new Map();
620
712
  const walk = (list, parentWorld, ancestors) => {
621
713
  for (const node of list) {
622
- const local = _chunkYJLCBA7Vcjs.vectorNodeBounds.call(void 0, node);
623
- const bounds = _chunkYJLCBA7Vcjs.vectorNodeBounds.call(void 0, node, parentWorld);
714
+ const local = _chunk7MD7C6XQcjs.vectorNodeBounds.call(void 0, node);
715
+ const bounds = _chunk7MD7C6XQcjs.vectorNodeBounds.call(void 0, node, parentWorld);
624
716
  const center = local ? { x: local.x + local.width / 2, y: local.y + local.height / 2 } : { x: 0, y: 0 };
625
717
  if (!out.has(node.id)) out.set(node.id, { node, ancestors, parentWorld, center, bounds, box: local });
626
- if (node.type === "group") walk(node.children, _chunkYJLCBA7Vcjs.multiplyMatrices.call(void 0, _chunkYJLCBA7Vcjs.nodeMatrix.call(void 0, node), parentWorld), [...ancestors, node.id]);
718
+ if (node.type === "group") walk(node.children, _chunk7MD7C6XQcjs.multiplyMatrices.call(void 0, _chunk7MD7C6XQcjs.nodeMatrix.call(void 0, node), parentWorld), [...ancestors, node.id]);
627
719
  }
628
720
  };
629
- walk(nodes, _chunkYJLCBA7Vcjs.IDENTITY_MATRIX, []);
721
+ walk(nodes, _chunk7MD7C6XQcjs.IDENTITY_MATRIX, []);
630
722
  indexCache.set(nodes, out);
631
723
  return out;
632
724
  }
633
725
  var solid = (paint) => typeof paint === "string" && paint !== "none" ? paint : void 0;
634
726
  function firstPaint(node, key) {
635
727
  if (node.type === "path") return solid(node[key]);
636
- for (const p of _chunkYJLCBA7Vcjs.collectPathNodes.call(void 0, node)) {
728
+ for (const p of _chunk7MD7C6XQcjs.collectPathNodes.call(void 0, node)) {
637
729
  const s = solid(p[key]);
638
730
  if (s) return s;
639
731
  }
@@ -641,7 +733,7 @@ function firstPaint(node, key) {
641
733
  }
642
734
  function strokeReach(node) {
643
735
  let reach = 0;
644
- for (const p of node.type === "path" ? [node] : _chunkYJLCBA7Vcjs.collectPathNodes.call(void 0, node)) {
736
+ for (const p of node.type === "path" ? [node] : _chunk7MD7C6XQcjs.collectPathNodes.call(void 0, node)) {
645
737
  if (p.stroke == null || p.stroke === "none") continue;
646
738
  const width = _nullishCoalesce(p.strokeWidth, () => ( 1));
647
739
  reach = Math.max(reach, p.strokeAlign === "outside" ? width : p.strokeAlign === "inside" ? 0 : width / 2);
@@ -690,10 +782,10 @@ function motionRevealClip(entry, reveal) {
690
782
  }
691
783
  }
692
784
  function motionRestValue(entry, property, artwork) {
693
- const node = _optionalChain([entry, 'optionalAccess', _7 => _7.node]);
785
+ const node = _optionalChain([entry, 'optionalAccess', _13 => _13.node]);
694
786
  switch (property) {
695
787
  case "opacity":
696
- return _nullishCoalesce(_optionalChain([node, 'optionalAccess', _8 => _8.opacity]), () => ( 1));
788
+ return _nullishCoalesce(_optionalChain([node, 'optionalAccess', _14 => _14.opacity]), () => ( 1));
697
789
  case "x":
698
790
  case "y":
699
791
  case "rotation":
@@ -711,11 +803,11 @@ function motionRestValue(entry, property, artwork) {
711
803
  case "stroke":
712
804
  return _nullishCoalesce((node && firstPaint(node, property)), () => ( "transparent"));
713
805
  case "strokeWidth": {
714
- const path = _optionalChain([node, 'optionalAccess', _9 => _9.type]) === "path" ? node : node ? _chunkYJLCBA7Vcjs.collectPathNodes.call(void 0, node)[0] : void 0;
715
- return _nullishCoalesce(_optionalChain([path, 'optionalAccess', _10 => _10.strokeWidth]), () => ( 1));
806
+ const path = _optionalChain([node, 'optionalAccess', _15 => _15.type]) === "path" ? node : node ? _chunk7MD7C6XQcjs.collectPathNodes.call(void 0, node)[0] : void 0;
807
+ return _nullishCoalesce(_optionalChain([path, 'optionalAccess', _16 => _16.strokeWidth]), () => ( 1));
716
808
  }
717
809
  case "shape":
718
- return _nullishCoalesce(_optionalChain([node, 'optionalAccess', _11 => _11.id]), () => ( ""));
810
+ return _nullishCoalesce(_optionalChain([node, 'optionalAccess', _17 => _17.id]), () => ( ""));
719
811
  case "clipRect":
720
812
  case "clipCircle": {
721
813
  const pad = entry && motionRevealPadding(entry);
@@ -725,7 +817,7 @@ function motionRestValue(entry, property, artwork) {
725
817
  return [0, 0, artwork.width, artwork.height];
726
818
  }
727
819
  const [, effectId, param] = property.split(":");
728
- const effect = _optionalChain([node, 'optionalAccess', _12 => _12.effects, 'optionalAccess', _13 => _13.find, 'call', _14 => _14((e) => e.id === effectId)]);
820
+ const effect = _optionalChain([node, 'optionalAccess', _18 => _18.effects, 'optionalAccess', _19 => _19.find, 'call', _20 => _20((e) => e.id === effectId)]);
729
821
  const v = effect ? effect.params[param] : void 0;
730
822
  return typeof v === "number" ? v : 0;
731
823
  }
@@ -774,13 +866,13 @@ function segmentValue(segment, time, raw = false) {
774
866
  return typeof segment.from === "string" ? interpolateMotionValue(segment.from, segment.to, Math.min(1, Math.max(0, p))) : interpolateMotionValue(segment.from, segment.to, p);
775
867
  }
776
868
  function activeSegment(track, time) {
777
- if (!_optionalChain([track, 'optionalAccess', _15 => _15.length])) return void 0;
869
+ if (!_optionalChain([track, 'optionalAccess', _21 => _21.length])) return void 0;
778
870
  let found;
779
871
  for (const s of track) if (s.start <= time && (!found || s.start >= found.start)) found = s;
780
872
  return found;
781
873
  }
782
874
  function trackValue(track, time, raw = false) {
783
- if (!_optionalChain([track, 'optionalAccess', _16 => _16.length])) return void 0;
875
+ if (!_optionalChain([track, 'optionalAccess', _22 => _22.length])) return void 0;
784
876
  const s = activeSegment(track, time);
785
877
  if (s) return segmentValue(s, time, raw);
786
878
  let first = track[0];
@@ -813,7 +905,7 @@ function fitMotionRect(rect, aspect) {
813
905
  function resolveMotionCamera(camera, scene) {
814
906
  if ("frame" in camera && typeof camera.frame === "string") {
815
907
  const entry = indexMotionNodes(scene.artwork.nodes).get(camera.frame);
816
- if (!_optionalChain([entry, 'optionalAccess', _17 => _17.bounds])) return null;
908
+ if (!_optionalChain([entry, 'optionalAccess', _23 => _23.bounds])) return null;
817
909
  const pad = _nullishCoalesce(camera.padding, () => ( 24));
818
910
  const b = entry.bounds;
819
911
  const rect = { x: b.x - pad, y: b.y - pad, width: Math.max(1, b.width + pad * 2), height: Math.max(1, b.height + pad * 2) };
@@ -822,12 +914,12 @@ function resolveMotionCamera(camera, scene) {
822
914
  const r = camera;
823
915
  return { x: r.x, y: r.y, width: r.width, height: r.height };
824
916
  }
825
- function motionPathLength(node, parentWorld = _chunkYJLCBA7Vcjs.IDENTITY_MATRIX) {
917
+ function motionPathLength(node, parentWorld = _chunk7MD7C6XQcjs.IDENTITY_MATRIX) {
826
918
  let longest = 0;
827
- const paths = node.type === "path" ? [node] : _chunkYJLCBA7Vcjs.collectPathNodes.call(void 0, node);
919
+ const paths = node.type === "path" ? [node] : _chunk7MD7C6XQcjs.collectPathNodes.call(void 0, node);
828
920
  for (const p of paths) {
829
921
  try {
830
- longest = Math.max(longest, _chunkO2UH7LZEcjs.bezierPathLength.call(void 0, _chunkYJLCBA7Vcjs.vectorPathWorldBezier.call(void 0, p, parentWorld, { effects: false })));
922
+ longest = Math.max(longest, _chunkXGD3FSNCcjs.bezierPathLength.call(void 0, _chunk7MD7C6XQcjs.vectorPathWorldBezier.call(void 0, p, parentWorld, { effects: false })));
831
923
  } catch (e2) {
832
924
  }
833
925
  }
@@ -848,12 +940,12 @@ function motionBeatAt(grid, ms) {
848
940
  function motionCueTime(scene, cue) {
849
941
  if (typeof cue.ms === "number") return Math.max(0, cue.ms);
850
942
  if (typeof cue.beat === "number") return scene.beat && scene.beat.bpm > 0 ? Math.max(0, motionBeatTime(scene.beat, cue.beat)) : void 0;
851
- if (typeof cue.word === "number") return _optionalChain([scene, 'access', _18 => _18.narration, 'optionalAccess', _19 => _19.words, 'access', _20 => _20[cue.word], 'optionalAccess', _21 => _21.start]);
943
+ if (typeof cue.word === "number") return _optionalChain([scene, 'access', _24 => _24.narration, 'optionalAccess', _25 => _25.words, 'access', _26 => _26[cue.word], 'optionalAccess', _27 => _27.start]);
852
944
  return void 0;
853
945
  }
854
946
  var SCALE_LIKE = /* @__PURE__ */ new Set(["scaleX", "scaleY", "opacity"]);
855
947
  function motionPropertyValue(timeline, node, property, time, raw = false, depth = 0) {
856
- const rest = property === "opacity" ? _nullishCoalesce(_optionalChain([timeline, 'access', _22 => _22.restOpacity, 'optionalAccess', _23 => _23.get, 'call', _24 => _24(node)]), () => ( 1)) : SCALE_LIKE.has(property) ? 1 : 0;
948
+ const rest = property === "opacity" ? _nullishCoalesce(_optionalChain([timeline, 'access', _28 => _28.restOpacity, 'optionalAccess', _29 => _29.get, 'call', _30 => _30(node)]), () => ( 1)) : SCALE_LIKE.has(property) ? 1 : 0;
857
949
  const v = trackValue(timeline.tracks.get(motionTrackKey(node, property)), time, raw);
858
950
  const base = property === "opacity" ? typeof v === "number" ? v : rest : withMotionLayers(timeline, node, property, typeof v === "number" ? v : rest, time, raw);
859
951
  return applyMotionDrivers(timeline, node, property, base, time, raw, depth);
@@ -864,7 +956,7 @@ function applyMotionDrivers(timeline, node, property, value, time, raw = false,
864
956
  for (const d of timeline.drivers) {
865
957
  if (d.target !== node || d.property !== property) continue;
866
958
  const src = motionPropertyValue(timeline, d.source.node, d.source.property, time, raw, depth + 1);
867
- const srcRest = d.source.property === "opacity" ? _nullishCoalesce(_optionalChain([timeline, 'access', _25 => _25.restOpacity, 'optionalAccess', _26 => _26.get, 'call', _27 => _27(d.source.node)]), () => ( 1)) : SCALE_LIKE.has(d.source.property) ? 1 : 0;
959
+ const srcRest = d.source.property === "opacity" ? _nullishCoalesce(_optionalChain([timeline, 'access', _31 => _31.restOpacity, 'optionalAccess', _32 => _32.get, 'call', _33 => _33(d.source.node)]), () => ( 1)) : SCALE_LIKE.has(d.source.property) ? 1 : 0;
868
960
  let change = (src - srcRest) * (_nullishCoalesce(d.factor, () => ( 1))) + (_nullishCoalesce(d.offset, () => ( 0)));
869
961
  if (d.clamp) change = Math.min(d.clamp[1], Math.max(d.clamp[0], change));
870
962
  v = SCALE_LIKE.has(property) ? v * (1 + change) : v + change;
@@ -886,16 +978,16 @@ function motionDriverSources(timeline, node, properties, seen = /* @__PURE__ */
886
978
  }
887
979
  return out;
888
980
  }
889
- function motionCommandRepeat(command2) {
890
- if (command2.type === "wait" || command2.type === "enter" || command2.type === "exit") return 1;
891
- const n = _nullishCoalesce(command2.repeat, () => ( 1));
981
+ function motionCommandRepeat(command3) {
982
+ if (command3.type === "wait" || command3.type === "enter" || command3.type === "exit") return 1;
983
+ const n = _nullishCoalesce(command3.repeat, () => ( 1));
892
984
  return Number.isInteger(n) && n >= 1 ? n : 1;
893
985
  }
894
- function motionCommandDuration(command2, scene, target) {
895
- return motionCycleDuration(command2, scene, target) * motionCommandRepeat(command2);
986
+ function motionCommandDuration(command3, scene, target) {
987
+ return motionCycleDuration(command3, scene, target) * motionCommandRepeat(command3);
896
988
  }
897
989
  function motionClipLayout(scene, clip, target, speed = 1) {
898
- const run = _optionalChain([scene, 'access', _28 => _28.clips, 'optionalAccess', _29 => _29[clip], 'optionalAccess', _30 => _30.run]);
990
+ const run = _optionalChain([scene, 'access', _34 => _34.clips, 'optionalAccess', _35 => _35[clip], 'optionalAccess', _36 => _36.run]);
899
991
  if (!run) return { items: [], length: 0 };
900
992
  const f = speed > 0 && Number.isFinite(speed) ? 1 / speed : 1;
901
993
  const items = [];
@@ -909,36 +1001,36 @@ function motionClipLayout(scene, clip, target, speed = 1) {
909
1001
  return;
910
1002
  }
911
1003
  if (raw.type === "play") return;
912
- const command2 = { ...raw, targets: raw.targets.map((t) => t === MOTION_CLIP_TARGET ? target : t) };
913
- const begin = base + (_nullishCoalesce(command2.delay, () => ( 0))) * f;
1004
+ const command3 = { ...raw, targets: raw.targets.map((t) => t === MOTION_CLIP_TARGET ? target : t) };
1005
+ const begin = base + (_nullishCoalesce(command3.delay, () => ( 0))) * f;
914
1006
  let end = begin;
915
- command2.targets.forEach((t, k) => {
916
- const cycle = motionCycleDuration(command2, scene, t) * f;
917
- const s = begin + k * (_nullishCoalesce(command2.stagger, () => ( 0))) * f;
918
- items.push({ command: command2, target: t, start: s, cycle });
919
- end = Math.max(end, s + cycle * motionCommandRepeat(command2));
1007
+ command3.targets.forEach((t, k) => {
1008
+ const cycle = motionCycleDuration(command3, scene, t) * f;
1009
+ const s = begin + k * (_nullishCoalesce(command3.stagger, () => ( 0))) * f;
1010
+ items.push({ command: command3, target: t, start: s, cycle });
1011
+ end = Math.max(end, s + cycle * motionCommandRepeat(command3));
920
1012
  });
921
1013
  prevStart = begin;
922
1014
  runEnd = Math.max(runEnd, end);
923
1015
  });
924
1016
  return { items, length: runEnd };
925
1017
  }
926
- function motionCycleDuration(command2, scene, target) {
927
- if (command2.type === "wait") return command2.duration;
928
- if (command2.type === "play") return motionClipLayout(scene, command2.clip, _nullishCoalesce(target, () => ( MOTION_CLIP_TARGET)), command2.speed).length;
929
- if (command2.duration !== void 0) return command2.duration;
930
- if (command2.ease !== void 0) {
931
- const ease = _chunk2FHLHZALcjs.resolveEasing.call(void 0, command2.ease);
1018
+ function motionCycleDuration(command3, scene, target) {
1019
+ if (command3.type === "wait") return command3.duration;
1020
+ if (command3.type === "play") return motionClipLayout(scene, command3.clip, _nullishCoalesce(target, () => ( MOTION_CLIP_TARGET)), command3.speed).length;
1021
+ if (command3.duration !== void 0) return command3.duration;
1022
+ if (command3.ease !== void 0) {
1023
+ const ease = _chunk2FHLHZALcjs.resolveEasing.call(void 0, command3.ease);
932
1024
  if (ease !== "hold" && ease.type === "spring") return _chunk2FHLHZALcjs.springSettleMs.call(void 0, ease);
933
1025
  }
934
- if (command2.type === "draw" || command2.type === "enter" && command2.style === "draw" || command2.type === "exit" && command2.style === "undraw") {
1026
+ if (command3.type === "draw" || command3.type === "enter" && command3.style === "draw" || command3.type === "exit" && command3.style === "undraw") {
935
1027
  const entry = target ? indexMotionNodes(scene.artwork.nodes).get(target) : void 0;
936
1028
  const length = entry ? motionPathLength(entry.node) : 0;
937
1029
  const ms = length * MOTION_DRAW_MS_PER_UNIT;
938
1030
  return Math.round(Math.min(MOTION_DRAW_RANGE[1], Math.max(MOTION_DRAW_RANGE[0], ms)));
939
1031
  }
940
- if (command2.type === "move" && _optionalChain([command2, 'access', _31 => _31.via, 'optionalAccess', _32 => _32.length])) return MOTION_DEFAULT_DURATIONS.move + 400 * command2.via.length;
941
- return MOTION_DEFAULT_DURATIONS[command2.type];
1032
+ if (command3.type === "move" && _optionalChain([command3, 'access', _37 => _37.via, 'optionalAccess', _38 => _38.length])) return MOTION_DEFAULT_DURATIONS.move + 400 * command3.via.length;
1033
+ return MOTION_DEFAULT_DURATIONS[command3.type];
942
1034
  }
943
1035
  var MOTION_EASE_OUT = { type: "cubic-bezier", x1: 1 / 3, y1: 1, x2: 2 / 3, y2: 1 };
944
1036
  var MOTION_EASE_IN_OUT = { type: "cubic-bezier", x1: 0.65, y1: 0, x2: 0.35, y2: 1 };
@@ -974,14 +1066,15 @@ function compileMotionScene(scene) {
974
1066
  const commandsStart = cursor;
975
1067
  let runEnd = cursor;
976
1068
  let prevStart = cursor;
977
- shot.run.forEach((command2, i) => {
978
- const base = command2.with === "previous" && i > 0 ? prevStart : runEnd;
979
- const begin = base + (command2.type === "wait" ? 0 : (_nullishCoalesce(command2.delay, () => ( 0))) * f);
980
- const targets = command2.type === "wait" ? [] : command2.targets;
981
- const durations = command2.type === "wait" ? [command2.duration * f] : targets.map((id) => motionCommandDuration(command2, scene, id) * f);
982
- const stagger = command2.type === "wait" ? 0 : (_nullishCoalesce(command2.stagger, () => ( 0))) * f;
983
- const end2 = durations.reduce((m, d, k) => Math.max(m, begin + k * stagger + d), begin);
984
- commands.push({ shot: shot.id, shotIndex: index, command: i, type: command2.type, start: begin, end: end2, duration: _nullishCoalesce(durations[0], () => ( 0)) });
1069
+ shot.run.forEach((command3, i) => {
1070
+ const base = command3.with === "previous" && i > 0 ? prevStart : runEnd;
1071
+ const begin = base + (command3.type === "wait" ? 0 : (_nullishCoalesce(command3.delay, () => ( 0))) * f);
1072
+ const split = command3.type === "wait" ? null : motionCommandSlots(nodes, command3);
1073
+ const slots = _nullishCoalesce(_optionalChain([split, 'optionalAccess', _39 => _39.slots]), () => ( []));
1074
+ const durations = command3.type === "wait" ? [command3.duration * f] : slots.map((s) => motionCommandDuration(command3, scene, s.id) * f);
1075
+ const stagger = (_nullishCoalesce(_optionalChain([split, 'optionalAccess', _40 => _40.stagger]), () => ( 0))) * f;
1076
+ const end2 = durations.reduce((m, d, k) => Math.max(m, begin + (_nullishCoalesce(_optionalChain([slots, 'access', _41 => _41[k], 'optionalAccess', _42 => _42.slot]), () => ( k))) * stagger + d), begin);
1077
+ commands.push({ shot: shot.id, shotIndex: index, command: i, type: command3.type, start: begin, end: end2, duration: _nullishCoalesce(durations[0], () => ( 0)) });
985
1078
  prevStart = begin;
986
1079
  runEnd = Math.max(runEnd, end2);
987
1080
  });
@@ -1013,14 +1106,14 @@ function compileMotionScene(scene) {
1013
1106
  }
1014
1107
  for (const timing of commands) {
1015
1108
  const shot = scene.shots[timing.shotIndex];
1016
- const command2 = shot.run[timing.command];
1017
- if (command2.type === "wait") continue;
1018
- if (command2.type === "play") {
1109
+ const command3 = shot.run[timing.command];
1110
+ if (command3.type === "wait") continue;
1111
+ if (command3.type === "play") {
1019
1112
  const f = motionShotTimeScale(shot);
1020
- command2.targets.forEach((target, k) => {
1113
+ command3.targets.forEach((target, k) => {
1021
1114
  if (!nodes.has(target)) return;
1022
- const at = timing.start + k * (_nullishCoalesce(command2.stagger, () => ( 0))) * f;
1023
- for (const item of motionClipLayout(scene, command2.clip, target, (_nullishCoalesce(command2.speed, () => ( 1))) / f).items) {
1115
+ const at = timing.start + k * (_nullishCoalesce(command3.stagger, () => ( 0))) * f;
1116
+ for (const item of motionClipLayout(scene, command3.clip, target, (_nullishCoalesce(command3.speed, () => ( 1))) / f).items) {
1024
1117
  const entry = nodes.get(item.target);
1025
1118
  if (!entry) continue;
1026
1119
  jobs.push({ start: at + item.start, order: order++, run: () => compileRepeated(item.command, item.target, entry, at + item.start, item.cycle, timing) });
@@ -1028,24 +1121,25 @@ function compileMotionScene(scene) {
1028
1121
  });
1029
1122
  continue;
1030
1123
  }
1031
- command2.targets.forEach((target, k) => {
1124
+ const split = motionCommandSlots(nodes, command3);
1125
+ for (const { id: target, slot } of split.slots) {
1032
1126
  const entry = nodes.get(target);
1033
- if (!entry) return;
1127
+ if (!entry) continue;
1034
1128
  const f = motionShotTimeScale(shot);
1035
- const start = timing.start + k * (_nullishCoalesce(command2.stagger, () => ( 0))) * f;
1036
- const cycle = motionCycleDuration(command2, scene, target) * f;
1037
- jobs.push({ start, order: order++, run: () => compileRepeated(command2, target, entry, start, cycle, timing) });
1038
- });
1129
+ const start = timing.start + slot * split.stagger * f;
1130
+ const cycle = motionCycleDuration(command3, scene, target) * f;
1131
+ jobs.push({ start, order: order++, run: () => compileRepeated(command3, target, entry, start, cycle, timing) });
1132
+ }
1039
1133
  }
1040
1134
  jobs.sort((a, b) => a.start - b.start || a.order - b.order).forEach((job) => job.run());
1041
- function compileRepeated(command2, node, entry, start, cycle, timing) {
1135
+ function compileRepeated(command3, node, entry, start, cycle, timing) {
1042
1136
  const first = segments.length;
1043
- compileCommand(command2, node, entry, start, cycle, timing);
1137
+ compileCommand(command3, node, entry, start, cycle, timing);
1044
1138
  const made = segments.slice(first);
1045
- const steps = _nullishCoalesce(command2.steps, () => ( scene.steps));
1139
+ const steps = _nullishCoalesce(command3.steps, () => ( scene.steps));
1046
1140
  if (steps && steps > 0) for (const s of made) s.steps = steps;
1047
- const n = motionCommandRepeat(command2);
1048
- const mode = _nullishCoalesce(command2.repeatMode, () => ( "loop"));
1141
+ const n = motionCommandRepeat(command3);
1142
+ const mode = _nullishCoalesce(command3.repeatMode, () => ( "loop"));
1049
1143
  const shift = (v, by, k) => {
1050
1144
  if (typeof v === "number" && typeof by === "number") return v + by * k;
1051
1145
  if (Array.isArray(v) && Array.isArray(by) && v.length === by.length) return v.map((x, i) => x + by[i] * k);
@@ -1064,30 +1158,30 @@ function compileMotionScene(scene) {
1064
1158
  }
1065
1159
  }
1066
1160
  }
1067
- function compileCommand(command2, node, entry, start, duration2, timing) {
1161
+ function compileCommand(command3, node, entry, start, duration2, timing) {
1068
1162
  const end = start + duration2;
1069
1163
  const where = { shot: timing.shot, command: timing.command };
1070
1164
  const seg = (property, to, ease2, s = start, e = end, extra = {}) => push({ node, property, start: s, end: e, from: _nullishCoalesce(extra.from, () => ( valueAt(node, property, s))), to, ease: ease2, ...where, ...extra });
1071
1165
  const rest = (p) => motionRestValue(entry, p, artwork);
1072
- const num = (p, time = start) => {
1166
+ const num2 = (p, time = start) => {
1073
1167
  const v = valueAt(node, p, time);
1074
1168
  return typeof v === "number" ? v : 0;
1075
1169
  };
1076
- const ease = command2.ease;
1170
+ const ease = command3.ease;
1077
1171
  const drawWindows = (s, e, o) => {
1078
1172
  const order2 = _nullishCoalesce(o.order, () => ( "together"));
1079
1173
  const pace = _nullishCoalesce(o.pace, () => ( "time"));
1080
1174
  if (order2 === "together" && pace === "time") return null;
1081
- const paths = entry.node.type === "path" ? [entry.node] : _chunkYJLCBA7Vcjs.collectPathNodes.call(void 0, entry.node);
1175
+ const paths = entry.node.type === "path" ? [entry.node] : _chunk7MD7C6XQcjs.collectPathNodes.call(void 0, entry.node);
1082
1176
  let list = paths.map((p) => {
1083
1177
  const at = nodes.get(p.id);
1084
1178
  let first = { x: 0, y: 0 };
1085
1179
  try {
1086
- const m = _chunkYJLCBA7Vcjs.vectorPathWorldBezier.call(void 0, p, _nullishCoalesce(_optionalChain([at, 'optionalAccess', _33 => _33.parentWorld]), () => ( _chunkYJLCBA7Vcjs.IDENTITY_MATRIX)), { effects: false })[0];
1180
+ const m = _chunk7MD7C6XQcjs.vectorPathWorldBezier.call(void 0, p, _nullishCoalesce(_optionalChain([at, 'optionalAccess', _43 => _43.parentWorld]), () => ( _chunk7MD7C6XQcjs.IDENTITY_MATRIX)), { effects: false })[0];
1087
1181
  if (m && m.type === "M") first = { x: m.x, y: m.y };
1088
1182
  } catch (e3) {
1089
1183
  }
1090
- return { id: p.id, len: Math.max(1e-6, motionPathLength(p, _optionalChain([at, 'optionalAccess', _34 => _34.parentWorld]))), first };
1184
+ return { id: p.id, len: Math.max(1e-6, motionPathLength(p, _optionalChain([at, 'optionalAccess', _44 => _44.parentWorld]))), first };
1091
1185
  });
1092
1186
  if (!list.length) return null;
1093
1187
  const span = Math.max(0, e - s);
@@ -1111,42 +1205,42 @@ function compileMotionScene(scene) {
1111
1205
  return o.align === "end" ? { id: p.id, start: e - d, end: e } : { id: p.id, start: s, end: s + d };
1112
1206
  });
1113
1207
  };
1114
- switch (command2.type) {
1208
+ switch (command3.type) {
1115
1209
  case "enter": {
1116
- const style = _nullishCoalesce(command2.style, () => ( "fade"));
1210
+ const style = _nullishCoalesce(command3.style, () => ( "fade"));
1117
1211
  const opacity = rest("opacity");
1118
- const reveal = style === "wipe" ? motionRevealClip(entry, { style, direction: command2.direction }) : style === "iris" ? motionRevealClip(entry, { style, shape: command2.shape }) : style === "split" ? motionRevealClip(entry, { style, orientation: command2.orientation }) : null;
1119
- for (const p of ["clipRect", "clipCircle"]) if (p !== _optionalChain([reveal, 'optionalAccess', _35 => _35.property]) && tracks.has(motionTrackKey(node, p))) seg(p, rest(p), "linear", start, start);
1212
+ const reveal = style === "wipe" ? motionRevealClip(entry, { style, direction: command3.direction }) : style === "iris" ? motionRevealClip(entry, { style, shape: command3.shape }) : style === "split" ? motionRevealClip(entry, { style, orientation: command3.orientation }) : null;
1213
+ for (const p of ["clipRect", "clipCircle"]) if (p !== _optionalChain([reveal, 'optionalAccess', _45 => _45.property]) && tracks.has(motionTrackKey(node, p))) seg(p, rest(p), "linear", start, start);
1120
1214
  if (reveal) {
1121
1215
  seg("opacity", opacity, "linear", start, start);
1122
1216
  seg(reveal.property, reveal.to, _nullishCoalesce(ease, () => ( MOTION_EASE_OUT)), start, end, { from: reveal.from });
1123
1217
  break;
1124
1218
  }
1125
1219
  if (style === "draw") {
1126
- const windows = drawWindows(start, start + duration2 * 0.75, command2);
1220
+ const windows = drawWindows(start, start + duration2 * 0.75, command3);
1127
1221
  if (windows) for (const w of windows) push({ node: w.id, property: "drawEnd", start: w.start, end: w.end, from: 0, to: 1, ease: _nullishCoalesce(ease, () => ( MOTION_EASE_IN_OUT)), ...where });
1128
1222
  else seg("drawEnd", 1, _nullishCoalesce(ease, () => ( MOTION_EASE_IN_OUT)), start, start + duration2 * 0.75, { from: 0 });
1129
1223
  seg("fillOpacity", 1, MOTION_EASE_OUT, start + duration2 * 0.6, end, { from: 0 });
1130
1224
  break;
1131
1225
  }
1132
1226
  if (style === "pop" || style === "zoom") {
1133
- const from = _nullishCoalesce(command2.scale, () => ( (style === "pop" ? 0.5 : 1.6)));
1227
+ const from = _nullishCoalesce(command3.scale, () => ( (style === "pop" ? 0.5 : 1.6)));
1134
1228
  seg("opacity", opacity, MOTION_EASE_OUT, start, start + duration2 * (style === "pop" ? 0.4 : 0.5), { from: 0 });
1135
1229
  seg("scaleX", 1, _nullishCoalesce(ease, () => ( (style === "pop" ? "ease-out-back" : MOTION_EASE_OUT))), start, end, { from });
1136
1230
  seg("scaleY", 1, _nullishCoalesce(ease, () => ( (style === "pop" ? "ease-out-back" : MOTION_EASE_OUT))), start, end, { from });
1137
1231
  } else if (style === "rise") {
1138
- const y = num("y");
1232
+ const y = num2("y");
1139
1233
  seg("opacity", opacity, MOTION_EASE_OUT, start, start + duration2 * 0.5, { from: 0 });
1140
- seg("y", y, _nullishCoalesce(ease, () => ( "ease-out-back")), start, end, { from: y + (_nullishCoalesce(command2.distance, () => ( 20))) });
1234
+ seg("y", y, _nullishCoalesce(ease, () => ( "ease-out-back")), start, end, { from: y + (_nullishCoalesce(command3.distance, () => ( 20))) });
1141
1235
  } else if (style === "grow") {
1142
1236
  seg("opacity", opacity, MOTION_EASE_OUT, start, start + duration2 * 0.3, { from: 0 });
1143
1237
  seg("scaleX", 1, _nullishCoalesce(ease, () => ( MOTION_EASE_OUT)), start, end, { from: 0 });
1144
1238
  seg("scaleY", 1, _nullishCoalesce(ease, () => ( MOTION_EASE_OUT)), start, end, { from: 0 });
1145
1239
  } else if (style === "slide") {
1146
- const dir = DIRECTION[_nullishCoalesce(command2.direction, () => ( "up"))];
1147
- const distance = _nullishCoalesce(command2.distance, () => ( 40));
1148
- const x = num("x");
1149
- const y = num("y");
1240
+ const dir = DIRECTION[_nullishCoalesce(command3.direction, () => ( "up"))];
1241
+ const distance = _nullishCoalesce(command3.distance, () => ( 40));
1242
+ const x = num2("x");
1243
+ const y = num2("y");
1150
1244
  seg("opacity", opacity, MOTION_EASE_OUT, start, end, { from: 0 });
1151
1245
  seg("x", x, _nullishCoalesce(ease, () => ( MOTION_EASE_OUT)), start, end, { from: x - dir.x * distance });
1152
1246
  seg("y", y, _nullishCoalesce(ease, () => ( MOTION_EASE_OUT)), start, end, { from: y - dir.y * distance });
@@ -1154,12 +1248,12 @@ function compileMotionScene(scene) {
1154
1248
  break;
1155
1249
  }
1156
1250
  case "exit": {
1157
- const style = _nullishCoalesce(command2.style, () => ( "fade"));
1158
- const wipe = style === "wipe" ? motionRevealClip(entry, { style, direction: command2.direction, hide: true }) : null;
1251
+ const style = _nullishCoalesce(command3.style, () => ( "fade"));
1252
+ const wipe = style === "wipe" ? motionRevealClip(entry, { style, direction: command3.direction, hide: true }) : null;
1159
1253
  if (wipe) seg(wipe.property, wipe.to, _nullishCoalesce(ease, () => ( "ease-in")), start, end, { from: wipe.from });
1160
1254
  else if (style === "undraw") {
1161
1255
  seg("fillOpacity", 0, "ease-in", start, start + duration2 * 0.4);
1162
- const windows = drawWindows(start, end, command2);
1256
+ const windows = drawWindows(start, end, command3);
1163
1257
  if (windows) for (const w of windows) push({ node: w.id, property: "drawEnd", start: w.start, end: w.end, from: valueAt(w.id, "drawEnd", w.start), to: 0, ease: _nullishCoalesce(ease, () => ( MOTION_EASE_IN_OUT)), ...where });
1164
1258
  else seg("drawEnd", 0, _nullishCoalesce(ease, () => ( MOTION_EASE_IN_OUT)));
1165
1259
  } else if (style === "shrink") {
@@ -1167,18 +1261,18 @@ function compileMotionScene(scene) {
1167
1261
  seg("scaleY", 0, _nullishCoalesce(ease, () => ( "ease-in")));
1168
1262
  seg("opacity", 0, "ease-in", start + duration2 * 0.6, end);
1169
1263
  } else if (style === "slide") {
1170
- const dir = DIRECTION[_nullishCoalesce(command2.direction, () => ( "down"))];
1171
- const distance = _nullishCoalesce(command2.distance, () => ( 40));
1172
- seg("x", num("x") + dir.x * distance, _nullishCoalesce(ease, () => ( "ease-in")));
1173
- seg("y", num("y") + dir.y * distance, _nullishCoalesce(ease, () => ( "ease-in")));
1264
+ const dir = DIRECTION[_nullishCoalesce(command3.direction, () => ( "down"))];
1265
+ const distance = _nullishCoalesce(command3.distance, () => ( 40));
1266
+ seg("x", num2("x") + dir.x * distance, _nullishCoalesce(ease, () => ( "ease-in")));
1267
+ seg("y", num2("y") + dir.y * distance, _nullishCoalesce(ease, () => ( "ease-in")));
1174
1268
  seg("opacity", 0, _nullishCoalesce(ease, () => ( "ease-in")));
1175
1269
  } else seg("opacity", 0, _nullishCoalesce(ease, () => ( "ease-in")));
1176
1270
  break;
1177
1271
  }
1178
1272
  case "draw": {
1179
1273
  const e = _nullishCoalesce(ease, () => ( MOTION_EASE_IN_OUT));
1180
- const comet = command2.comet !== void 0 ? Math.min(1, Math.max(0, command2.comet)) : void 0;
1181
- const windows = _nullishCoalesce(drawWindows(start, end, command2), () => ( [{ id: node, start, end }]));
1274
+ const comet = command3.comet !== void 0 ? Math.min(1, Math.max(0, command3.comet)) : void 0;
1275
+ const windows = _nullishCoalesce(drawWindows(start, end, command3), () => ( [{ id: node, start, end }]));
1182
1276
  for (const w of windows) {
1183
1277
  if (comet !== void 0) {
1184
1278
  push({ node: w.id, property: "drawEnd", start: w.start, end: w.end, from: 0, to: 1 + comet, ease: e, ...where });
@@ -1186,27 +1280,27 @@ function compileMotionScene(scene) {
1186
1280
  continue;
1187
1281
  }
1188
1282
  const drawn = tracks.has(motionTrackKey(w.id, "drawEnd"));
1189
- push({ node: w.id, property: "drawEnd", start: w.start, end: w.end, from: _nullishCoalesce(command2.from, () => ( (drawn ? valueAt(w.id, "drawEnd", w.start) : 0))), to: _nullishCoalesce(command2.to, () => ( 1)), ease: e, ...where });
1283
+ push({ node: w.id, property: "drawEnd", start: w.start, end: w.end, from: _nullishCoalesce(command3.from, () => ( (drawn ? valueAt(w.id, "drawEnd", w.start) : 0))), to: _nullishCoalesce(command3.to, () => ( 1)), ease: e, ...where });
1190
1284
  }
1191
1285
  break;
1192
1286
  }
1193
1287
  case "move": {
1194
1288
  const e = _nullishCoalesce(ease, () => ( MOTION_EASE_IN_OUT));
1195
- let dx = _nullishCoalesce(_optionalChain([command2, 'access', _36 => _36.by, 'optionalAccess', _37 => _37.x]), () => ( 0));
1196
- let dy = _nullishCoalesce(_optionalChain([command2, 'access', _38 => _38.by, 'optionalAccess', _39 => _39.y]), () => ( 0));
1197
- if (command2.to) {
1198
- const inv = _chunkYJLCBA7Vcjs.invertMatrix.call(void 0, entry.parentWorld);
1199
- const target = _chunkYJLCBA7Vcjs.applyMatrix.call(void 0, inv, command2.to);
1200
- dx = target.x - (entry.center.x + num("x"));
1201
- dy = target.y - (entry.center.y + num("y"));
1289
+ let dx = _nullishCoalesce(_optionalChain([command3, 'access', _46 => _46.by, 'optionalAccess', _47 => _47.x]), () => ( 0));
1290
+ let dy = _nullishCoalesce(_optionalChain([command3, 'access', _48 => _48.by, 'optionalAccess', _49 => _49.y]), () => ( 0));
1291
+ if (command3.to) {
1292
+ const inv = _chunk7MD7C6XQcjs.invertMatrix.call(void 0, entry.parentWorld);
1293
+ const target = _chunk7MD7C6XQcjs.applyMatrix.call(void 0, inv, command3.to);
1294
+ dx = target.x - (entry.center.x + num2("x"));
1295
+ dy = target.y - (entry.center.y + num2("y"));
1202
1296
  }
1203
- if (_optionalChain([command2, 'access', _40 => _40.via, 'optionalAccess', _41 => _41.length])) {
1204
- const inv = _chunkYJLCBA7Vcjs.invertMatrix.call(void 0, entry.parentWorld);
1205
- const x0 = num("x");
1206
- const y0 = num("y");
1207
- const add = command2.blend === "add";
1208
- const offsets = [{ x: 0, y: 0 }, ...command2.via.map((p) => _chunkYJLCBA7Vcjs.applyMatrix.call(void 0, inv, p)).map((p) => ({ x: p.x - entry.center.x - x0, y: p.y - entry.center.y - y0 })), { x: dx, y: dy }];
1209
- const times = smoothMotionTimes(offsets, start, end - start, _nullishCoalesce(command2.pacing, () => ( "even")));
1297
+ if (_optionalChain([command3, 'access', _50 => _50.via, 'optionalAccess', _51 => _51.length])) {
1298
+ const inv = _chunk7MD7C6XQcjs.invertMatrix.call(void 0, entry.parentWorld);
1299
+ const x0 = num2("x");
1300
+ const y0 = num2("y");
1301
+ const add = command3.blend === "add";
1302
+ const offsets = [{ x: 0, y: 0 }, ...command3.via.map((p) => _chunk7MD7C6XQcjs.applyMatrix.call(void 0, inv, p)).map((p) => ({ x: p.x - entry.center.x - x0, y: p.y - entry.center.y - y0 })), { x: dx, y: dy }];
1303
+ const times = smoothMotionTimes(offsets, start, end - start, _nullishCoalesce(command3.pacing, () => ( "even")));
1210
1304
  for (const axis of ["x", "y"]) {
1211
1305
  const base = add ? 0 : axis === "x" ? x0 : y0;
1212
1306
  for (const leg of smoothMotionLegs(times, offsets.map((o) => o[axis])))
@@ -1214,42 +1308,42 @@ function compileMotionScene(scene) {
1214
1308
  }
1215
1309
  break;
1216
1310
  }
1217
- if (command2.blend === "add") {
1311
+ if (command3.blend === "add") {
1218
1312
  seg("x", dx, e, start, end, { from: 0, blend: "add" });
1219
1313
  seg("y", dy, e, start, end, { from: 0, blend: "add" });
1220
1314
  } else {
1221
- seg("x", num("x") + dx, e);
1222
- seg("y", num("y") + dy, e);
1315
+ seg("x", num2("x") + dx, e);
1316
+ seg("y", num2("y") + dy, e);
1223
1317
  }
1224
1318
  break;
1225
1319
  }
1226
1320
  case "rotate": {
1227
1321
  const e = _nullishCoalesce(ease, () => ( MOTION_EASE_IN_OUT));
1228
- if (command2.blend === "add") seg("rotation", command2.to !== void 0 ? command2.to - num("rotation") : _nullishCoalesce(command2.by, () => ( 0)), e, start, end, { from: 0, blend: "add" });
1229
- else seg("rotation", _nullishCoalesce(command2.to, () => ( num("rotation") + (_nullishCoalesce(command2.by, () => ( 0))))), e);
1322
+ if (command3.blend === "add") seg("rotation", command3.to !== void 0 ? command3.to - num2("rotation") : _nullishCoalesce(command3.by, () => ( 0)), e, start, end, { from: 0, blend: "add" });
1323
+ else seg("rotation", _nullishCoalesce(command3.to, () => ( num2("rotation") + (_nullishCoalesce(command3.by, () => ( 0))))), e);
1230
1324
  break;
1231
1325
  }
1232
1326
  case "scale": {
1233
- const to = typeof command2.to === "number" ? { x: command2.to, y: command2.to } : command2.to;
1234
- const extra = { ...command2.geometric ? { geometric: true } : {}, ...command2.blend === "add" ? { from: 1, blend: "add" } : {} };
1327
+ const to = typeof command3.to === "number" ? { x: command3.to, y: command3.to } : command3.to;
1328
+ const extra = { ...command3.geometric ? { geometric: true } : {}, ...command3.blend === "add" ? { from: 1, blend: "add" } : {} };
1235
1329
  seg("scaleX", to.x, _nullishCoalesce(ease, () => ( MOTION_EASE_IN_OUT)), start, end, extra);
1236
1330
  seg("scaleY", to.y, _nullishCoalesce(ease, () => ( MOTION_EASE_IN_OUT)), start, end, extra);
1237
1331
  break;
1238
1332
  }
1239
1333
  case "fade":
1240
- seg("opacity", command2.to, _nullishCoalesce(ease, () => ( MOTION_EASE_IN_OUT)));
1334
+ seg("opacity", command3.to, _nullishCoalesce(ease, () => ( MOTION_EASE_IN_OUT)));
1241
1335
  break;
1242
1336
  case "style":
1243
- if (command2.fill !== void 0) seg("fill", command2.fill, _nullishCoalesce(ease, () => ( MOTION_EASE_IN_OUT)));
1244
- if (command2.stroke !== void 0) seg("stroke", command2.stroke, _nullishCoalesce(ease, () => ( MOTION_EASE_IN_OUT)));
1245
- if (command2.strokeWidth !== void 0) seg("strokeWidth", command2.strokeWidth, _nullishCoalesce(ease, () => ( MOTION_EASE_IN_OUT)));
1337
+ if (command3.fill !== void 0) seg("fill", command3.fill, _nullishCoalesce(ease, () => ( MOTION_EASE_IN_OUT)));
1338
+ if (command3.stroke !== void 0) seg("stroke", command3.stroke, _nullishCoalesce(ease, () => ( MOTION_EASE_IN_OUT)));
1339
+ if (command3.strokeWidth !== void 0) seg("strokeWidth", command3.strokeWidth, _nullishCoalesce(ease, () => ( MOTION_EASE_IN_OUT)));
1246
1340
  break;
1247
1341
  case "morph":
1248
- seg("shape", command2.into, _nullishCoalesce(ease, () => ( MOTION_EASE_IN_OUT)));
1342
+ seg("shape", command3.into, _nullishCoalesce(ease, () => ( MOTION_EASE_IN_OUT)));
1249
1343
  break;
1250
1344
  case "match": {
1251
- const into = nodes.get(command2.into);
1252
- if (!into || command2.into === node) break;
1345
+ const into = nodes.get(command3.into);
1346
+ if (!into || command3.into === node) break;
1253
1347
  const match = matchMotionNodes(entry.node, into.node);
1254
1348
  const e = _nullishCoalesce(ease, () => ( MOTION_EASE_IN_OUT));
1255
1349
  for (const [a, b] of match.pairs) push({ node: a, property: "shape", start, end, from: valueAt(a, "shape", start), to: b, ease: e, ...where });
@@ -1257,7 +1351,7 @@ function compileMotionScene(scene) {
1257
1351
  break;
1258
1352
  }
1259
1353
  case "follow":
1260
- seg("follow", _nullishCoalesce(command2.to, () => ( 1)), _nullishCoalesce(ease, () => ( MOTION_EASE_IN_OUT)), start, end, { from: _nullishCoalesce(command2.from, () => ( 0)), path: command2.path, rotate: _nullishCoalesce(command2.rotate, () => ( false)) });
1354
+ seg("follow", _nullishCoalesce(command3.to, () => ( 1)), _nullishCoalesce(ease, () => ( MOTION_EASE_IN_OUT)), start, end, { from: _nullishCoalesce(command3.from, () => ( 0)), path: command3.path, rotate: _nullishCoalesce(command3.rotate, () => ( false)) });
1261
1355
  break;
1262
1356
  // Emphasis: a change toward a peak that a behaviour swings round (as Flair's primitives do).
1263
1357
  case "pulse":
@@ -1265,40 +1359,40 @@ function compileMotionScene(scene) {
1265
1359
  case "teeter":
1266
1360
  case "wave":
1267
1361
  case "wiggle": {
1268
- const add = (_nullishCoalesce(command2.blend, () => ( "add"))) === "add";
1362
+ const add = (_nullishCoalesce(command3.blend, () => ( "add"))) === "add";
1269
1363
  const swing = (p, by, extra) => {
1270
1364
  const scale = p === "scaleX" || p === "scaleY";
1271
1365
  if (add) seg(p, by, _nullishCoalesce(ease, () => ( "linear")), start, end, { from: scale ? 1 : 0, blend: "add", ...extra });
1272
- else seg(p, scale ? num(p) * by : num(p) + by, _nullishCoalesce(ease, () => ( "linear")), start, end, extra);
1366
+ else seg(p, scale ? num2(p) * by : num2(p) + by, _nullishCoalesce(ease, () => ( "linear")), start, end, extra);
1273
1367
  };
1274
- if (command2.type === "pulse") {
1275
- const behavior = { type: "pulse", count: _nullishCoalesce(command2.count, () => ( 1)) };
1276
- swing("scaleX", 1 + (_nullishCoalesce(command2.amount, () => ( 0.15))), { behavior });
1277
- swing("scaleY", 1 + (_nullishCoalesce(command2.amount, () => ( 0.15))), { behavior });
1278
- } else if (command2.type === "shake") swing(command2.axis === "vertical" ? "y" : "x", _nullishCoalesce(command2.amount, () => ( 8)), { behavior: { type: "oscillate", count: _nullishCoalesce(command2.count, () => ( 3)), decay: 1 } });
1279
- else if (command2.type === "teeter") swing("rotation", _nullishCoalesce(command2.angle, () => ( 8)), { behavior: { type: "oscillate", count: _nullishCoalesce(command2.count, () => ( 3)), decay: 1 } });
1280
- else if (command2.type === "wave") swing("y", -(_nullishCoalesce(command2.amount, () => ( 6))), { behavior: { type: "oscillate", count: _nullishCoalesce(command2.count, () => ( 2)) } });
1368
+ if (command3.type === "pulse") {
1369
+ const behavior = { type: "pulse", count: _nullishCoalesce(command3.count, () => ( 1)) };
1370
+ swing("scaleX", 1 + (_nullishCoalesce(command3.amount, () => ( 0.15))), { behavior });
1371
+ swing("scaleY", 1 + (_nullishCoalesce(command3.amount, () => ( 0.15))), { behavior });
1372
+ } else if (command3.type === "shake") swing(command3.axis === "vertical" ? "y" : "x", _nullishCoalesce(command3.amount, () => ( 8)), { behavior: { type: "oscillate", count: _nullishCoalesce(command3.count, () => ( 3)), decay: 1 } });
1373
+ else if (command3.type === "teeter") swing("rotation", _nullishCoalesce(command3.angle, () => ( 8)), { behavior: { type: "oscillate", count: _nullishCoalesce(command3.count, () => ( 3)), decay: 1 } });
1374
+ else if (command3.type === "wave") swing("y", -(_nullishCoalesce(command3.amount, () => ( 6))), { behavior: { type: "oscillate", count: _nullishCoalesce(command3.count, () => ( 2)) } });
1281
1375
  else {
1282
- const behavior = { type: "wriggle", frequency: _nullishCoalesce(command2.frequency, () => ( 3)), octaves: _nullishCoalesce(command2.octaves, () => ( 2)), seed: command2.seed || _chunkZCNLBRT6cjs.motionSeedFromId.call(void 0, node) };
1283
- const amount = _nullishCoalesce(command2.amount, () => ( 6));
1376
+ const behavior = { type: "wriggle", frequency: _nullishCoalesce(command3.frequency, () => ( 3)), octaves: _nullishCoalesce(command3.octaves, () => ( 2)), seed: command3.seed || _chunkZCNLBRT6cjs.motionSeedFromId.call(void 0, node) };
1377
+ const amount = _nullishCoalesce(command3.amount, () => ( 6));
1284
1378
  swing("x", amount, { behavior, index: 0 });
1285
1379
  swing("y", amount, { behavior, index: 1 });
1286
- if (command2.rotate) swing("rotation", command2.rotate, { behavior, index: 2 });
1380
+ if (command3.rotate) swing("rotation", command3.rotate, { behavior, index: 2 });
1287
1381
  }
1288
1382
  break;
1289
1383
  }
1290
1384
  case "color-pulse":
1291
- seg(_nullishCoalesce(command2.paint, () => ( "fill")), _nullishCoalesce(command2.color, () => ( "#f59e0b")), _nullishCoalesce(ease, () => ( "linear")), start, end, { behavior: { type: "pulse", count: _nullishCoalesce(command2.count, () => ( 1)) } });
1385
+ seg(_nullishCoalesce(command3.paint, () => ( "fill")), _nullishCoalesce(command3.color, () => ( "#f59e0b")), _nullishCoalesce(ease, () => ( "linear")), start, end, { behavior: { type: "pulse", count: _nullishCoalesce(command3.count, () => ( 1)) } });
1292
1386
  break;
1293
1387
  case "effect":
1294
- seg(`effect:${command2.effect}:${command2.param}`, command2.to, _nullishCoalesce(ease, () => ( MOTION_EASE_IN_OUT)));
1388
+ seg(`effect:${command3.effect}:${command3.param}`, command3.to, _nullishCoalesce(ease, () => ( MOTION_EASE_IN_OUT)));
1295
1389
  break;
1296
1390
  }
1297
1391
  }
1298
1392
  for (const track of tracks.values()) track.sort((a, b) => a.start - b.start);
1299
1393
  segments.sort((a, b) => a.start - b.start);
1300
1394
  const duration = shots.length ? shots[shots.length - 1].end : 0;
1301
- const timeline = { duration, shots, commands, segments, tracks, ..._optionalChain([scene, 'access', _42 => _42.drivers, 'optionalAccess', _43 => _43.length]) ? { drivers: scene.drivers, restOpacity: new Map([...nodes].map(([id, e]) => [id, _nullishCoalesce(e.node.opacity, () => ( 1))])) } : {} };
1395
+ const timeline = { duration, shots, commands, segments, tracks, ..._optionalChain([scene, 'access', _52 => _52.drivers, 'optionalAccess', _53 => _53.length]) ? { drivers: scene.drivers, restOpacity: new Map([...nodes].map(([id, e]) => [id, _nullishCoalesce(e.node.opacity, () => ( 1))])) } : {} };
1302
1396
  timelineCache.set(scene, timeline);
1303
1397
  return timeline;
1304
1398
  }
@@ -1311,7 +1405,7 @@ var MOTION_DRAW_EFFECT_ID = "~draw";
1311
1405
  var TRANSFORM_PROPS = ["x", "y", "rotation", "scaleX", "scaleY", "follow"];
1312
1406
  var tidy = (v) => Math.round(v * 1e6) / 1e6 || 0;
1313
1407
  function applyMotionControlValues(scene, values) {
1314
- if (!values || !_optionalChain([scene, 'access', _44 => _44.controls, 'optionalAccess', _45 => _45.length])) return scene;
1408
+ if (!values || !_optionalChain([scene, 'access', _54 => _54.controls, 'optionalAccess', _55 => _55.length])) return scene;
1315
1409
  const map = /* @__PURE__ */ new Map();
1316
1410
  for (const control of scene.controls) {
1317
1411
  const value = values[control.name];
@@ -1347,17 +1441,17 @@ function morphBetween(a, b) {
1347
1441
  let inner = morphCache.get(a);
1348
1442
  if (!inner) morphCache.set(a, inner = /* @__PURE__ */ new WeakMap());
1349
1443
  let morph = inner.get(b);
1350
- if (!morph) inner.set(b, morph = _chunkYJLCBA7Vcjs.createVectorMorph.call(void 0, a, b, { id: a.id }));
1444
+ if (!morph) inner.set(b, morph = _chunk7MD7C6XQcjs.createVectorMorph.call(void 0, a, b, { id: a.id }));
1351
1445
  return morph;
1352
1446
  }
1353
1447
  function shapeIn(entry, shape) {
1354
1448
  if (shape === entry) return entry.node;
1355
- const m = _chunkYJLCBA7Vcjs.multiplyMatrices.call(void 0, _chunkYJLCBA7Vcjs.multiplyMatrices.call(void 0, _chunkYJLCBA7Vcjs.nodeMatrix.call(void 0, shape.node), shape.parentWorld), _chunkYJLCBA7Vcjs.invertMatrix.call(void 0, entry.parentWorld));
1356
- return { ...shape.node, ..._chunkYJLCBA7Vcjs.isIdentityMatrix.call(void 0, m) ? { transform: void 0 } : { transform: m } };
1449
+ const m = _chunk7MD7C6XQcjs.multiplyMatrices.call(void 0, _chunk7MD7C6XQcjs.multiplyMatrices.call(void 0, _chunk7MD7C6XQcjs.nodeMatrix.call(void 0, shape.node), shape.parentWorld), _chunk7MD7C6XQcjs.invertMatrix.call(void 0, entry.parentWorld));
1450
+ return { ...shape.node, ..._chunk7MD7C6XQcjs.isIdentityMatrix.call(void 0, m) ? { transform: void 0 } : { transform: m } };
1357
1451
  }
1358
1452
  function shapeAt(entry, timeline, nodes, time) {
1359
1453
  const track = timeline.tracks.get(motionTrackKey(entry.node.id, "shape"));
1360
- if (!_optionalChain([track, 'optionalAccess', _46 => _46.length])) return null;
1454
+ if (!_optionalChain([track, 'optionalAccess', _56 => _56.length])) return null;
1361
1455
  const s = activeSegment(track, time);
1362
1456
  const fromId = String(s ? s.from : track[0].from);
1363
1457
  if (!s) return fromId === entry.node.id ? null : nodes.get(fromId) ? shapeIn(entry, nodes.get(fromId)) : null;
@@ -1373,20 +1467,20 @@ function shapeAt(entry, timeline, nodes, time) {
1373
1467
  function motionFollowRoute(entry, path) {
1374
1468
  if (!path || path.node.type !== "path") return null;
1375
1469
  try {
1376
- return _chunkO2UH7LZEcjs.transformBezierPath.call(void 0, _chunkYJLCBA7Vcjs.vectorPathWorldBezier.call(void 0, path.node, path.parentWorld, { effects: false }), _chunkYJLCBA7Vcjs.invertMatrix.call(void 0, entry.parentWorld));
1470
+ return _chunkXGD3FSNCcjs.transformBezierPath.call(void 0, _chunk7MD7C6XQcjs.vectorPathWorldBezier.call(void 0, path.node, path.parentWorld, { effects: false }), _chunk7MD7C6XQcjs.invertMatrix.call(void 0, entry.parentWorld));
1377
1471
  } catch (e4) {
1378
1472
  return null;
1379
1473
  }
1380
1474
  }
1381
1475
  function followAt(entry, timeline, nodes, time) {
1382
1476
  const track = timeline.tracks.get(motionTrackKey(entry.node.id, "follow"));
1383
- const s = _nullishCoalesce(activeSegment(track, time), () => ( _optionalChain([track, 'optionalAccess', _47 => _47[0]])));
1384
- if (!_optionalChain([s, 'optionalAccess', _48 => _48.path])) return null;
1477
+ const s = _nullishCoalesce(activeSegment(track, time), () => ( _optionalChain([track, 'optionalAccess', _57 => _57[0]])));
1478
+ if (!_optionalChain([s, 'optionalAccess', _58 => _58.path])) return null;
1385
1479
  const route = motionFollowRoute(entry, nodes.get(s.path));
1386
1480
  const progress = Number(trackValue(track, time));
1387
1481
  if (!route || !(progress >= 0)) return null;
1388
1482
  try {
1389
- const at = _chunkO2UH7LZEcjs.bezierPathPointAtLength.call(void 0, route, Math.min(1, Math.max(0, progress)) * _chunkO2UH7LZEcjs.bezierPathLength.call(void 0, route));
1483
+ const at = _chunkXGD3FSNCcjs.bezierPathPointAtLength.call(void 0, route, Math.min(1, Math.max(0, progress)) * _chunkXGD3FSNCcjs.bezierPathLength.call(void 0, route));
1390
1484
  if (!at) return null;
1391
1485
  return { x: at.point[0], y: at.point[1], angle: s.rotate ? at.angle * 180 / Math.PI : 0 };
1392
1486
  } catch (e5) {
@@ -1399,7 +1493,7 @@ function motionNodeOffset(entry, timeline, time, nodes = indexMotionNodes([entry
1399
1493
  const layered = withMotionLayers(timeline, entry.node.id, p, typeof v === "number" ? v : rest, time, raw);
1400
1494
  return p === "follow" ? layered : applyMotionDrivers(timeline, entry.node.id, p, layered, time, raw);
1401
1495
  };
1402
- const driven = _optionalChain([timeline, 'access', _49 => _49.drivers, 'optionalAccess', _50 => _50.some, 'call', _51 => _51((d) => d.target === entry.node.id && d.property !== "opacity")]);
1496
+ const driven = _optionalChain([timeline, 'access', _59 => _59.drivers, 'optionalAccess', _60 => _60.some, 'call', _61 => _61((d) => d.target === entry.node.id && d.property !== "opacity")]);
1403
1497
  if (!driven && !TRANSFORM_PROPS.some((p) => (follow || p !== "follow") && (timeline.tracks.has(motionTrackKey(entry.node.id, p)) || timeline.tracks.has(motionAddTrackKey(entry.node.id, p))))) return null;
1404
1498
  const dx = read("x", 0);
1405
1499
  const dy = read("y", 0);
@@ -1407,9 +1501,9 @@ function motionNodeOffset(entry, timeline, time, nodes = indexMotionNodes([entry
1407
1501
  const scaleX = read("scaleX", 1);
1408
1502
  const scaleY = read("scaleY", 1);
1409
1503
  const c = entry.center;
1410
- let matrix = _chunkYJLCBA7Vcjs.multiplyMatrices.call(void 0, _chunkYJLCBA7Vcjs.multiplyMatrices.call(void 0, _chunkYJLCBA7Vcjs.scaleMatrix.call(void 0, scaleX, scaleY, c), _chunkYJLCBA7Vcjs.rotateMatrix.call(void 0, rotation, c)), _chunkYJLCBA7Vcjs.translateMatrix.call(void 0, dx, dy));
1504
+ let matrix = _chunk7MD7C6XQcjs.multiplyMatrices.call(void 0, _chunk7MD7C6XQcjs.multiplyMatrices.call(void 0, _chunk7MD7C6XQcjs.scaleMatrix.call(void 0, scaleX, scaleY, c), _chunk7MD7C6XQcjs.rotateMatrix.call(void 0, rotation, c)), _chunk7MD7C6XQcjs.translateMatrix.call(void 0, dx, dy));
1411
1505
  const along = follow ? followAt(entry, timeline, nodes, time) : null;
1412
- if (along) matrix = _chunkYJLCBA7Vcjs.multiplyMatrices.call(void 0, matrix, _chunkYJLCBA7Vcjs.multiplyMatrices.call(void 0, _chunkYJLCBA7Vcjs.multiplyMatrices.call(void 0, _chunkYJLCBA7Vcjs.translateMatrix.call(void 0, -c.x, -c.y), _chunkYJLCBA7Vcjs.rotateMatrix.call(void 0, along.angle)), _chunkYJLCBA7Vcjs.translateMatrix.call(void 0, along.x, along.y)));
1506
+ if (along) matrix = _chunk7MD7C6XQcjs.multiplyMatrices.call(void 0, matrix, _chunk7MD7C6XQcjs.multiplyMatrices.call(void 0, _chunk7MD7C6XQcjs.multiplyMatrices.call(void 0, _chunk7MD7C6XQcjs.translateMatrix.call(void 0, -c.x, -c.y), _chunk7MD7C6XQcjs.rotateMatrix.call(void 0, along.angle)), _chunk7MD7C6XQcjs.translateMatrix.call(void 0, along.x, along.y)));
1413
1507
  return { matrix, dx, dy, rotation, scaleX, scaleY, follow: along };
1414
1508
  }
1415
1509
  function motionNodeClips(id, timeline, time) {
@@ -1443,7 +1537,7 @@ function motionFrameAt(scene, time, options = {}) {
1443
1537
  if (out.visible === void 0) delete out.visible;
1444
1538
  }
1445
1539
  const effectTracks = [...timeline.tracks.keys()].filter((k) => k.startsWith(`${id}\0effect:`));
1446
- if (effectTracks.length && _optionalChain([out, 'access', _52 => _52.effects, 'optionalAccess', _53 => _53.length])) {
1540
+ if (effectTracks.length && _optionalChain([out, 'access', _62 => _62.effects, 'optionalAccess', _63 => _63.length])) {
1447
1541
  out = {
1448
1542
  ...out,
1449
1543
  effects: out.effects.map((effect) => {
@@ -1476,22 +1570,22 @@ function motionFrameAt(scene, time, options = {}) {
1476
1570
  const start = Math.min(1, Math.max(0, typeof drawStart === "number" ? drawStart : 0));
1477
1571
  const end = Math.min(1, Math.max(0, typeof drawEnd === "number" ? drawEnd : 1));
1478
1572
  if (start > 0 || end < 1) {
1479
- const trim = _chunkYJLCBA7Vcjs.createVectorEffect.call(void 0, "trim", MOTION_DRAW_EFFECT_ID, { start: tidy(start * 100), end: tidy(end * 100) });
1573
+ const trim = _chunk7MD7C6XQcjs.createVectorEffect.call(void 0, "trim", MOTION_DRAW_EFFECT_ID, { start: tidy(start * 100), end: tidy(end * 100) });
1480
1574
  p = { ...p, effects: [trim, ...(_nullishCoalesce(p.effects, () => ( []))).filter((e) => e.id !== MOTION_DRAW_EFFECT_ID)] };
1481
1575
  }
1482
1576
  }
1483
1577
  return p;
1484
1578
  });
1485
1579
  }
1486
- const opacity = _optionalChain([timeline, 'access', _54 => _54.drivers, 'optionalAccess', _55 => _55.some, 'call', _56 => _56((d) => d.target === id && d.property === "opacity")]) ? motionPropertyValue(timeline, id, "opacity", time) : value(id, "opacity");
1580
+ const opacity = _optionalChain([timeline, 'access', _64 => _64.drivers, 'optionalAccess', _65 => _65.some, 'call', _66 => _66((d) => d.target === id && d.property === "opacity")]) ? motionPropertyValue(timeline, id, "opacity", time) : value(id, "opacity");
1487
1581
  if (typeof opacity === "number") {
1488
1582
  const o = Math.min(1, Math.max(0, tidy(opacity)));
1489
1583
  out = { ...out, opacity: o };
1490
1584
  if (o >= 1) delete out.opacity;
1491
1585
  }
1492
1586
  const offset = options.offsets === false ? null : motionNodeOffset(entry, timeline, time, nodes);
1493
- if (offset && !_chunkYJLCBA7Vcjs.isIdentityMatrix.call(void 0, offset.matrix)) {
1494
- const m = _chunkYJLCBA7Vcjs.multiplyMatrices.call(void 0, _chunkYJLCBA7Vcjs.nodeMatrix.call(void 0, out), offset.matrix).map(tidy);
1587
+ if (offset && !_chunk7MD7C6XQcjs.isIdentityMatrix.call(void 0, offset.matrix)) {
1588
+ const m = _chunk7MD7C6XQcjs.multiplyMatrices.call(void 0, _chunk7MD7C6XQcjs.nodeMatrix.call(void 0, out), offset.matrix).map(tidy);
1495
1589
  out = { ...out, transform: m };
1496
1590
  }
1497
1591
  return out;
@@ -1511,7 +1605,7 @@ function motionFrameToSvg(scene, time, options = {}) {
1511
1605
  const wrap = { ...options.wrap };
1512
1606
  for (const [id, clips] of Object.entries(frame.clips))
1513
1607
  wrap[id] = [..._nullishCoalesce(wrap[id], () => ( [])), ...clips.map((clip, i) => ({ clip: { id: `${prefix}reveal-${id}-${i}`, shape: clip.shape, attrs: motionClipAttrs(clip) } }))];
1514
- return withViewBox(_chunkYJLCBA7Vcjs.vectorDocumentToSvg.call(void 0, frame.document, { ...options, wrap }), frame.camera);
1608
+ return withViewBox(_chunk7MD7C6XQcjs.vectorDocumentToSvg.call(void 0, frame.document, { ...options, wrap }), frame.camera);
1515
1609
  }
1516
1610
  function withViewBox(svg, box) {
1517
1611
  const f = (n) => String(Math.round(n * 1e3) / 1e3);
@@ -1556,7 +1650,7 @@ function motionPath(scene, id, options = {}) {
1556
1650
  const r = (n) => Math.round(n * 100) / 100;
1557
1651
  const centreAt = (t) => {
1558
1652
  const node = indexMotionNodes(motionFrameAtCache(scene, t)).get(id);
1559
- const b = _optionalChain([node, 'optionalAccess', _57 => _57.bounds]);
1653
+ const b = _optionalChain([node, 'optionalAccess', _67 => _67.bounds]);
1560
1654
  return b ? { t: r(t), x: r(b.x + b.width / 2), y: r(b.y + b.height / 2) } : null;
1561
1655
  };
1562
1656
  const points = [];
@@ -1590,8 +1684,8 @@ function motionBlurTimes(scene, time, blur = {}) {
1590
1684
  const b = indexMotionNodes(motionFrameAt(scene, time + span / 2).document.nodes);
1591
1685
  let travel = 0;
1592
1686
  for (const id of ids) {
1593
- const p = _optionalChain([a, 'access', _58 => _58.get, 'call', _59 => _59(id), 'optionalAccess', _60 => _60.bounds]);
1594
- const q = _optionalChain([b, 'access', _61 => _61.get, 'call', _62 => _62(id), 'optionalAccess', _63 => _63.bounds]);
1687
+ const p = _optionalChain([a, 'access', _68 => _68.get, 'call', _69 => _69(id), 'optionalAccess', _70 => _70.bounds]);
1688
+ const q = _optionalChain([b, 'access', _71 => _71.get, 'call', _72 => _72(id), 'optionalAccess', _73 => _73.bounds]);
1595
1689
  if (p && q) travel = Math.max(travel, Math.abs(p.x - q.x), Math.abs(p.y - q.y), Math.abs(p.x + p.width - q.x - q.width), Math.abs(p.y + p.height - q.y - q.height));
1596
1690
  }
1597
1691
  const n = Math.min(most, Math.ceil(travel / 1.5));
@@ -1603,7 +1697,7 @@ function averagedFrames(scene, times, options, prefix) {
1603
1697
  return `<g data-motion-blur="${n}" style="isolation:isolate">${times.map((t, k) => {
1604
1698
  const own = `${prefix}blur${k}-`;
1605
1699
  const frame = motionFrameToSvg(scene, t, { ...options, idPrefix: own, background: false });
1606
- const view = _nullishCoalesce(_optionalChain([/viewBox="([^"]*)"/, 'access', _64 => _64.exec, 'call', _65 => _65(frame), 'optionalAccess', _66 => _66[1]]), () => ( ""));
1700
+ const view = _nullishCoalesce(_optionalChain([/viewBox="([^"]*)"/, 'access', _74 => _74.exec, 'call', _75 => _75(frame), 'optionalAccess', _76 => _76[1]]), () => ( ""));
1607
1701
  const inner = k === n - 1 ? innerSvg(frame) : innerSvg(frame).replace(new RegExp(` id="(?!${own.replace(/[.*+?^${}()|[\]\\]/g, "\\$&")})[^"]*"`, "g"), "");
1608
1702
  return `<svg viewBox="${view}" preserveAspectRatio="none" style="mix-blend-mode:plus-lighter" opacity="${Math.round(1e4 / n) / 1e4}">${inner}</svg>`;
1609
1703
  }).join("")}</g>`;
@@ -1618,7 +1712,7 @@ function motionStageSvg(scene, time, options = {}) {
1618
1712
  if (blurTimes.length > 1) {
1619
1713
  const open2 = current.indexOf(">") + 1;
1620
1714
  const body2 = current.slice(open2, current.lastIndexOf("</svg>"));
1621
- const bg2 = svgOptions.background !== false && scene.artwork.background ? _nullishCoalesce(_optionalChain([/^\s*<rect[^>]*\/>/, 'access', _67 => _67.exec, 'call', _68 => _68(body2), 'optionalAccess', _69 => _69[0]]), () => ( "")) : "";
1715
+ const bg2 = svgOptions.background !== false && scene.artwork.background ? _nullishCoalesce(_optionalChain([/^\s*<rect[^>]*\/>/, 'access', _77 => _77.exec, 'call', _78 => _78(body2), 'optionalAccess', _79 => _79[0]]), () => ( "")) : "";
1622
1716
  current = `${current.slice(0, open2)}${bg2}${averagedFrames(scene, blurTimes, svgOptions, prefix)}</svg>`;
1623
1717
  }
1624
1718
  if (!onionSkin && !motionPaths) return current;
@@ -1633,7 +1727,7 @@ function motionStageSvg(scene, time, options = {}) {
1633
1727
  const open = current.indexOf(">") + 1;
1634
1728
  const close = current.lastIndexOf("</svg>");
1635
1729
  const body = current.slice(open, close);
1636
- const bg = svgOptions.background !== false && scene.artwork.background ? _nullishCoalesce(_optionalChain([/^\s*<rect[^>]*\/>/, 'access', _70 => _70.exec, 'call', _71 => _71(body), 'optionalAccess', _72 => _72[0]]), () => ( "")) : "";
1730
+ const bg = svgOptions.background !== false && scene.artwork.background ? _nullishCoalesce(_optionalChain([/^\s*<rect[^>]*\/>/, 'access', _80 => _80.exec, 'call', _81 => _81(body), 'optionalAccess', _82 => _82[0]]), () => ( "")) : "";
1637
1731
  return `${current.slice(0, open)}${bg}${ghosts.join("")}${body.slice(bg.length)}${paths}</svg>`;
1638
1732
  }
1639
1733
  function motionContactSheetSvg(scene, times, options = {}) {
@@ -1652,7 +1746,7 @@ function motionContactSheetSvg(scene, times, options = {}) {
1652
1746
  const x = gap + i % cols * (w + gap);
1653
1747
  const y = gap + Math.floor(i / cols) * (h + label + gap);
1654
1748
  const frame = motionStageSvg(scene, t, { ...frameOptions, idPrefix: `${prefix}f${i}-` });
1655
- const view = _nullishCoalesce(_optionalChain([/viewBox="([^"]*)"/, 'access', _73 => _73.exec, 'call', _74 => _74(frame), 'optionalAccess', _75 => _75[1]]), () => ( `0 0 ${w} ${h}`));
1749
+ const view = _nullishCoalesce(_optionalChain([/viewBox="([^"]*)"/, 'access', _83 => _83.exec, 'call', _84 => _84(frame), 'optionalAccess', _85 => _85[1]]), () => ( `0 0 ${w} ${h}`));
1656
1750
  const inner = innerSvg(frame).replace(new RegExp(` id="(?!${`${prefix}f${i}-`.replace(/[.*+?^${}()|[\]\\]/g, "\\$&")})[^"]*"`, "g"), "");
1657
1751
  const shot = _nullishCoalesce(timeline.shots.find((s) => t >= s.start && t < s.end), () => ( timeline.shots[timeline.shots.length - 1]));
1658
1752
  const caption = labels ? `<text x="${x + w / 2}" y="${y + h + label * 0.8}" font-family="system-ui, sans-serif" font-size="${Math.round(label * 0.7)}" text-anchor="middle" fill="#64748b">${(t / 1e3).toFixed(2)} s${shot ? ` \xB7 ${shot.id.replace(/[<&]/g, "")}` : ""}</text>` : "";
@@ -1664,7 +1758,7 @@ function motionContactSheetSvg(scene, times, options = {}) {
1664
1758
  // src/animation-editor/check.ts
1665
1759
  var MOTION_MIN_CHANGE_MS = 120;
1666
1760
  var NUMERIC_PARAMS = (node, effectId) => {
1667
- const effect = _optionalChain([node, 'access', _76 => _76.effects, 'optionalAccess', _77 => _77.find, 'call', _78 => _78((e) => e.id === effectId)]);
1761
+ const effect = _optionalChain([node, 'access', _86 => _86.effects, 'optionalAccess', _87 => _87.find, 'call', _88 => _88((e) => e.id === effectId)]);
1668
1762
  if (!effect) return null;
1669
1763
  return Object.entries(effect.params).filter(([, v]) => typeof v === "number").map(([k]) => k);
1670
1764
  };
@@ -1682,24 +1776,24 @@ function usesColor(scene, color) {
1682
1776
  return found;
1683
1777
  }
1684
1778
  function visibleAt(timeline, id, time, node) {
1685
- const num = (p, rest) => {
1779
+ const num2 = (p, rest) => {
1686
1780
  const v = trackValue(timeline.tracks.get(motionTrackKey(id, p)), time);
1687
1781
  return typeof v === "number" ? v : rest;
1688
1782
  };
1689
1783
  if (node.visible === false) return false;
1690
- if (num("opacity", _nullishCoalesce(node.opacity, () => ( 1))) <= 0.01) return false;
1691
- if (Math.abs(num("scaleX", 1) * num("scaleY", 1)) <= 1e-4) return false;
1784
+ if (num2("opacity", _nullishCoalesce(node.opacity, () => ( 1))) <= 0.01) return false;
1785
+ if (Math.abs(num2("scaleX", 1) * num2("scaleY", 1)) <= 1e-4) return false;
1692
1786
  const clip = (p) => trackValue(timeline.tracks.get(motionTrackKey(id, p)), time);
1693
1787
  const rect = clip("clipRect");
1694
1788
  if (Array.isArray(rect) && (rect[2] <= 1e-4 || rect[3] <= 1e-4)) return false;
1695
1789
  const circle = clip("clipCircle");
1696
1790
  if (Array.isArray(circle) && circle[2] <= 1e-4) return false;
1697
- const hasFill = node.type === "path" ? !!node.fill && node.fill !== "none" : _chunkYJLCBA7Vcjs.collectPathNodes.call(void 0, node).some((p) => p.fill && p.fill !== "none");
1698
- const drawn = num("drawEnd", 1) > 0.01 && (node.type === "path" || _chunkYJLCBA7Vcjs.collectPathNodes.call(void 0, node).length === 0 || _chunkYJLCBA7Vcjs.collectPathNodes.call(void 0, node).some((p) => {
1791
+ const hasFill = node.type === "path" ? !!node.fill && node.fill !== "none" : _chunk7MD7C6XQcjs.collectPathNodes.call(void 0, node).some((p) => p.fill && p.fill !== "none");
1792
+ const drawn = num2("drawEnd", 1) > 0.01 && (node.type === "path" || _chunk7MD7C6XQcjs.collectPathNodes.call(void 0, node).length === 0 || _chunk7MD7C6XQcjs.collectPathNodes.call(void 0, node).some((p) => {
1699
1793
  const v = trackValue(timeline.tracks.get(motionTrackKey(p.id, "drawEnd")), time);
1700
1794
  return typeof v !== "number" || v > 0.01;
1701
1795
  }));
1702
- return drawn || hasFill && num("fillOpacity", 1) > 0.01;
1796
+ return drawn || hasFill && num2("fillOpacity", 1) > 0.01;
1703
1797
  }
1704
1798
  function checkMotionScene(scene) {
1705
1799
  const issues = [];
@@ -1727,48 +1821,53 @@ function checkMotionScene(scene) {
1727
1821
  issues.push(motionIssue("camera-aspect", `The camera is ${c.width} \xD7 ${c.height}, a different shape from the ${scene.artwork.width} \xD7 ${scene.artwork.height} artwork, so it shows bands at the sides; frame a node instead to match.`, { shot: shot.id }));
1728
1822
  }
1729
1823
  if (!shot.run.length && !shot.camera && shotIndex2 > 0) issues.push(motionIssue("empty-shot", "This shot only holds; add commands or a camera move, or remove it.", { shot: shot.id }));
1730
- shot.run.forEach((command2, i) => {
1824
+ shot.run.forEach((command3, i) => {
1731
1825
  const where = { shot: shot.id, command: i };
1732
- if (command2.type === "wait") return;
1733
- const targets = command2.targets.filter((id) => known(id, "unknown-target", "node", where));
1826
+ if (command3.type === "wait") return;
1827
+ const targets = command3.targets.filter((id) => known(id, "unknown-target", "node", where));
1734
1828
  const timing = timeline.commands.find((c) => c.shot === shot.id && c.command === i);
1735
1829
  if (timing && timing.duration > 0 && timing.duration < MOTION_MIN_CHANGE_MS)
1736
- issues.push(motionIssue("too-fast", `This ${command2.type} lasts ${timing.duration} ms, too quick to follow; make it at least ${MOTION_MIN_CHANGE_MS} ms.`, { ...where, measured: timing.duration, needed: MOTION_MIN_CHANGE_MS, unit: "ms", ids: targets }));
1737
- if (command2.duration !== void 0 && command2.ease !== void 0) {
1738
- const ease = _chunk2FHLHZALcjs.resolveEasing.call(void 0, command2.ease);
1830
+ issues.push(motionIssue("too-fast", `This ${command3.type} lasts ${timing.duration} ms, too quick to follow; make it at least ${MOTION_MIN_CHANGE_MS} ms.`, { ...where, measured: timing.duration, needed: MOTION_MIN_CHANGE_MS, unit: "ms", ids: targets }));
1831
+ if (command3.duration !== void 0 && command3.ease !== void 0) {
1832
+ const ease = _chunk2FHLHZALcjs.resolveEasing.call(void 0, command3.ease);
1739
1833
  if (ease !== "hold" && ease.type === "spring") {
1740
1834
  const settle = _chunk2FHLHZALcjs.springSettleMs.call(void 0, ease);
1741
- if (command2.duration < settle * 0.9)
1742
- issues.push(motionIssue("spring-cut-short", `This ${command2.type}'s spring takes ${settle} ms to settle but lasts ${command2.duration} ms, so it jumps to its end; leave "duration" out (the spring sets it) or make it at least ${settle} ms.`, { ...where, measured: command2.duration, needed: settle, unit: "ms", ids: targets }));
1835
+ if (command3.duration < settle * 0.9)
1836
+ issues.push(motionIssue("spring-cut-short", `This ${command3.type}'s spring takes ${settle} ms to settle but lasts ${command3.duration} ms, so it jumps to its end; leave "duration" out (the spring sets it) or make it at least ${settle} ms.`, { ...where, measured: command3.duration, needed: settle, unit: "ms", ids: targets }));
1743
1837
  }
1744
1838
  }
1745
- if (command2.type === "follow" && known(command2.path, "unknown-path", "path", where) && nodes.get(command2.path).node.type !== "path")
1746
- issues.push(motionIssue("not-a-path", `"${command2.path}" is a group; follow a single path node.`, { ...where, ids: [command2.path] }));
1747
- if (command2.type === "morph") known(command2.into, "unknown-shape", "shape", where);
1748
- if (command2.type === "play" && !_optionalChain([scene, 'access', _79 => _79.clips, 'optionalAccess', _80 => _80[command2.clip]]))
1749
- issues.push(motionIssue("unknown-clip", `There is no clip ${namedWithSuggestion(command2.clip, Object.keys(_nullishCoalesce(scene.clips, () => ( {}))))}; define it with set_animation_clip.`, where));
1750
- if (command2.type === "match" && known(command2.into, "unknown-shape", "group", where)) {
1751
- const into = nodes.get(command2.into);
1839
+ if (command3.type === "follow" && known(command3.path, "unknown-path", "path", where) && nodes.get(command3.path).node.type !== "path")
1840
+ issues.push(motionIssue("not-a-path", `"${command3.path}" is a group; follow a single path node.`, { ...where, ids: [command3.path] }));
1841
+ if (command3.type === "morph") known(command3.into, "unknown-shape", "shape", where);
1842
+ if (command3.type !== "play" && command3.split) {
1843
+ for (const id of targets)
1844
+ if (!motionTextLayout(nodes, id))
1845
+ issues.push(motionIssue("split-not-text", `"${id}" is not a group of letter paths, so "split": "${command3.split}" animates it as a whole; convert the text to outlines and group the letters (get_text_layout shows how they read).`, { ...where, ids: [id] }));
1846
+ }
1847
+ if (command3.type === "play" && !_optionalChain([scene, 'access', _89 => _89.clips, 'optionalAccess', _90 => _90[command3.clip]]))
1848
+ issues.push(motionIssue("unknown-clip", `There is no clip ${namedWithSuggestion(command3.clip, Object.keys(_nullishCoalesce(scene.clips, () => ( {}))))}; define it with set_animation_clip.`, where));
1849
+ if (command3.type === "match" && known(command3.into, "unknown-shape", "group", where)) {
1850
+ const into = nodes.get(command3.into);
1752
1851
  for (const id of targets) {
1753
1852
  const entry = nodes.get(id);
1754
- if (id === command2.into || into.ancestors.includes(id) || entry.ancestors.includes(command2.into)) {
1755
- issues.push(motionIssue("match-self", `"${id}" cannot match "${command2.into}": one is the other or holds it. Match two separate groups (the second usually hidden).`, { ...where, ids: [id, command2.into] }));
1853
+ if (id === command3.into || into.ancestors.includes(id) || entry.ancestors.includes(command3.into)) {
1854
+ issues.push(motionIssue("match-self", `"${id}" cannot match "${command3.into}": one is the other or holds it. Match two separate groups (the second usually hidden).`, { ...where, ids: [id, command3.into] }));
1756
1855
  continue;
1757
1856
  }
1758
1857
  const m = matchMotionNodes(entry.node, into.node);
1759
1858
  if (m.unpairedInto.length)
1760
- issues.push(motionIssue("match-unpaired", `${m.unpairedInto.map((x) => `"${x}"`).join(", ")} in "${command2.into}" ${m.unpairedInto.length > 1 ? "have" : "has"} no partner in "${id}", so ${m.unpairedInto.length > 1 ? "they do" : "it does"} not appear; add a part with the same name or id to "${id}" (it can start hidden or tiny).`, { ...where, ids: m.unpairedInto }));
1859
+ issues.push(motionIssue("match-unpaired", `${m.unpairedInto.map((x) => `"${x}"`).join(", ")} in "${command3.into}" ${m.unpairedInto.length > 1 ? "have" : "has"} no partner in "${id}", so ${m.unpairedInto.length > 1 ? "they do" : "it does"} not appear; add a part with the same name or id to "${id}" (it can start hidden or tiny).`, { ...where, ids: m.unpairedInto }));
1761
1860
  }
1762
1861
  }
1763
- const style = command2.type === "enter" || command2.type === "exit" ? command2.style : void 0;
1862
+ const style = command3.type === "enter" || command3.type === "exit" ? command3.style : void 0;
1764
1863
  if (style === "wipe" || style === "iris" || style === "split") {
1765
1864
  for (const id of targets)
1766
1865
  if (!motionRevealPadding(nodes.get(id)))
1767
- issues.push(motionIssue("reveal-no-box", `"${id}" has no width or height (a straight line), so a ${style} cannot clip it and it fades instead; use style "${command2.type === "enter" ? "draw" : "undraw"}" for a line.`, { ...where, ids: [id] }));
1866
+ issues.push(motionIssue("reveal-no-box", `"${id}" has no width or height (a straight line), so a ${style} cannot clip it and it fades instead; use style "${command3.type === "enter" ? "draw" : "undraw"}" for a line.`, { ...where, ids: [id] }));
1768
1867
  }
1769
- if (command2.type === "style") {
1868
+ if (command3.type === "style") {
1770
1869
  for (const key of ["fill", "stroke"]) {
1771
- const color = command2[key];
1870
+ const color = command3[key];
1772
1871
  if (color === void 0) continue;
1773
1872
  if (_chunkA6PHCAGXcjs.normalizeTokenColor.call(void 0, color) === null) {
1774
1873
  issues.push(motionIssue("invalid-color", `${key} "${color}" is not a colour this editor reads; use hex ("#0ea5e9"), rgb() or rgba().`, where));
@@ -1776,33 +1875,33 @@ function checkMotionScene(scene) {
1776
1875
  }
1777
1876
  for (const id of targets) {
1778
1877
  const node = nodes.get(id).node;
1779
- const paths = node.type === "path" ? [node] : _chunkYJLCBA7Vcjs.collectPathNodes.call(void 0, node);
1878
+ const paths = node.type === "path" ? [node] : _chunk7MD7C6XQcjs.collectPathNodes.call(void 0, node);
1780
1879
  if (paths.some((p) => p[key] !== void 0 && p[key] !== null && typeof p[key] !== "string"))
1781
1880
  issues.push(motionIssue("paint-not-color", `"${id}" has a gradient ${key}; it switches to ${color} at the end instead of blending.`, { ...where, ids: [id] }));
1782
1881
  }
1783
1882
  }
1784
1883
  }
1785
- if (command2.type === "color-pulse") {
1786
- const key = _nullishCoalesce(command2.paint, () => ( "fill"));
1787
- const color = _nullishCoalesce(command2.color, () => ( "#f59e0b"));
1884
+ if (command3.type === "color-pulse") {
1885
+ const key = _nullishCoalesce(command3.paint, () => ( "fill"));
1886
+ const color = _nullishCoalesce(command3.color, () => ( "#f59e0b"));
1788
1887
  if (_chunkA6PHCAGXcjs.normalizeTokenColor.call(void 0, color) === null) issues.push(motionIssue("invalid-color", `color "${color}" is not a colour this editor reads; use hex ("#0ea5e9"), rgb() or rgba().`, where));
1789
1888
  else
1790
1889
  for (const id of targets) {
1791
1890
  const node = nodes.get(id).node;
1792
- const paths = node.type === "path" ? [node] : _chunkYJLCBA7Vcjs.collectPathNodes.call(void 0, node);
1891
+ const paths = node.type === "path" ? [node] : _chunk7MD7C6XQcjs.collectPathNodes.call(void 0, node);
1793
1892
  if (paths.some((p) => typeof p[key] !== "string"))
1794
1893
  issues.push(motionIssue("paint-not-color", `"${id}" has a gradient or no ${key}, so a colour pulse cannot tint it; give it a solid ${key} or pulse the ${key === "fill" ? "stroke" : "fill"}.`, { ...where, ids: [id] }));
1795
1894
  }
1796
1895
  }
1797
- if (command2.type === "effect") {
1896
+ if (command3.type === "effect") {
1798
1897
  for (const id of targets) {
1799
1898
  const node = nodes.get(id).node;
1800
- const params = NUMERIC_PARAMS(node, command2.effect);
1899
+ const params = NUMERIC_PARAMS(node, command3.effect);
1801
1900
  const effects = (_nullishCoalesce(node.effects, () => ( []))).map((e) => e.id);
1802
1901
  if (!params)
1803
- issues.push(motionIssue("unknown-effect", `"${id}" has no effect ${namedWithSuggestion(command2.effect, effects)}${effects.length ? `; its effects are ${effects.map((e) => `"${e}"`).join(", ")}` : "; it has no live effects"}.`, { ...where, ids: [id] }));
1804
- else if (!params.includes(command2.param))
1805
- issues.push(motionIssue("unknown-effect-param", `Effect "${command2.effect}" has no numeric parameter ${namedWithSuggestion(command2.param, params)}; use one of ${params.join(", ")}.`, { ...where, ids: [id] }));
1902
+ issues.push(motionIssue("unknown-effect", `"${id}" has no effect ${namedWithSuggestion(command3.effect, effects)}${effects.length ? `; its effects are ${effects.map((e) => `"${e}"`).join(", ")}` : "; it has no live effects"}.`, { ...where, ids: [id] }));
1903
+ else if (!params.includes(command3.param))
1904
+ issues.push(motionIssue("unknown-effect-param", `Effect "${command3.effect}" has no numeric parameter ${namedWithSuggestion(command3.param, params)}; use one of ${params.join(", ")}.`, { ...where, ids: [id] }));
1806
1905
  }
1807
1906
  }
1808
1907
  if (timing) {
@@ -1810,11 +1909,11 @@ function checkMotionScene(scene) {
1810
1909
  const node = nodes.get(id).node;
1811
1910
  const before = Math.max(0, timing.start - 0.5);
1812
1911
  const visible = timing.start > 0 ? visibleAt(timeline, id, before, node) : node.visible !== false;
1813
- if (command2.type === "enter" && visible && timing.start > 0)
1912
+ if (command3.type === "enter" && visible && timing.start > 0)
1814
1913
  issues.push(motionIssue("enter-visible", `"${id}" is already showing when it enters, so it blinks out first; remove the enter or exit it earlier.`, { ...where, ids: [id] }));
1815
- const probe = command2.type === "exit" ? timing.start + 0.5 : (timing.start + timing.end) / 2;
1816
- if (command2.type !== "enter" && command2.type !== "draw" && !visibleAt(timeline, id, probe, node))
1817
- issues.push(motionIssue("exit-hidden", `"${id}" is hidden when this ${command2.type} starts, so it is not seen; enter it first.`, { ...where, ids: [id] }));
1914
+ const probe = command3.type === "exit" ? timing.start + 0.5 : (timing.start + timing.end) / 2;
1915
+ if (command3.type !== "enter" && command3.type !== "draw" && !visibleAt(timeline, id, probe, node))
1916
+ issues.push(motionIssue("exit-hidden", `"${id}" is hidden when this ${command3.type} starts, so it is not seen; enter it first.`, { ...where, ids: [id] }));
1818
1917
  }
1819
1918
  }
1820
1919
  });
@@ -1842,9 +1941,9 @@ function checkMotionScene(scene) {
1842
1941
  if (loop.length) issues.push(motionIssue("driver-cycle", `Drivers follow each other round in a loop (${loop.join(" \u2190 ")}); remove one of them.`, { ids: [...new Set(loop.map((k) => k.split(".")[0]))] }));
1843
1942
  }
1844
1943
  for (const [name, clip] of Object.entries(_nullishCoalesce(scene.clips, () => ( {}))))
1845
- for (const command2 of clip.run)
1846
- if (command2.type !== "wait") {
1847
- for (const id of command2.targets)
1944
+ for (const command3 of clip.run)
1945
+ if (command3.type !== "wait") {
1946
+ for (const id of command3.targets)
1848
1947
  if (id !== MOTION_CLIP_TARGET && !nodes.has(id)) issues.push(motionIssue("unknown-target", `Clip "${name}" names ${namedWithSuggestion(id, [...nodes.keys()])}, which the artwork does not have; use "$" for the node the clip is played on.`, { ids: [id] }));
1849
1948
  }
1850
1949
  if (scene.trigger) {
@@ -1860,7 +1959,7 @@ function checkMotionScene(scene) {
1860
1959
  if (!shot.at) return;
1861
1960
  const timing = timeline.shots[i];
1862
1961
  if (timing.cue === void 0) {
1863
- const what = shot.at.beat !== void 0 ? `beat ${shot.at.beat}, but the scene has no beat grid; set one with set_animation_settings ({ "beat": { "bpm": \u2026 } })` : `word ${shot.at.word}, but the narration has ${_nullishCoalesce(_optionalChain([scene, 'access', _81 => _81.narration, 'optionalAccess', _82 => _82.words, 'access', _83 => _83.length]), () => ( 0))} words (indexes from 0)`;
1962
+ const what = shot.at.beat !== void 0 ? `beat ${shot.at.beat}, but the scene has no beat grid; set one with set_animation_settings ({ "beat": { "bpm": \u2026 } })` : `word ${shot.at.word}, but the narration has ${_nullishCoalesce(_optionalChain([scene, 'access', _91 => _91.narration, 'optionalAccess', _92 => _92.words, 'access', _93 => _93.length]), () => ( 0))} words (indexes from 0)`;
1864
1963
  issues.push(motionIssue("unknown-cue", `Shot "${shot.id}" is cued to ${what}.`, { shot: shot.id }));
1865
1964
  } else if (timing.missedBy !== void 0) {
1866
1965
  const late = Math.round(timing.missedBy);
@@ -1887,18 +1986,18 @@ function checkMotionScene(scene) {
1887
1986
  }
1888
1987
  const reported = /* @__PURE__ */ new Set();
1889
1988
  for (const timing of timeline.commands) {
1890
- const command2 = scene.shots[timing.shotIndex].run[timing.command];
1891
- if (command2.type === "wait" || command2.type === "exit") continue;
1989
+ const command3 = scene.shots[timing.shotIndex].run[timing.command];
1990
+ if (command3.type === "wait" || command3.type === "exit") continue;
1892
1991
  const frame = motionFrameAt(scene, timing.end);
1893
1992
  const cam = frame.camera;
1894
1993
  const docIndex = indexMotionNodes(frame.document.nodes);
1895
- for (const id of command2.targets) {
1896
- const b = _optionalChain([docIndex, 'access', _84 => _84.get, 'call', _85 => _85(id), 'optionalAccess', _86 => _86.bounds]);
1994
+ for (const id of command3.targets) {
1995
+ const b = _optionalChain([docIndex, 'access', _94 => _94.get, 'call', _95 => _95(id), 'optionalAccess', _96 => _96.bounds]);
1897
1996
  if (!b || reported.has(id) || !visibleAt(timeline, id, timing.end, nodes.get(id).node)) continue;
1898
1997
  const outside = b.x + b.width < cam.x || b.y + b.height < cam.y || b.x > cam.x + cam.width || b.y > cam.y + cam.height;
1899
1998
  if (outside) {
1900
1999
  reported.add(id);
1901
- issues.push(motionIssue("out-of-frame", `"${id}" is outside the camera when this ${command2.type} ends, so it is not seen; move it into frame or move the camera.`, { shot: timing.shot, command: timing.command, ids: [id] }));
2000
+ issues.push(motionIssue("out-of-frame", `"${id}" is outside the camera when this ${command3.type} ends, so it is not seen; move it into frame or move the camera.`, { shot: timing.shot, command: timing.command, ids: [id] }));
1902
2001
  }
1903
2002
  }
1904
2003
  }
@@ -2052,8 +2151,8 @@ function applyOne(scene, edit) {
2052
2151
  case "add_commands": {
2053
2152
  const i = shotIndex(scene, edit.shot);
2054
2153
  if (!edit.commands.length) fail("Give at least one command.");
2055
- edit.commands.forEach((command2, k) => {
2056
- const problems = commandShapeProblems(command2, `Command ${k + 1}`);
2154
+ edit.commands.forEach((command3, k) => {
2155
+ const problems = commandShapeProblems(command3, `Command ${k + 1}`);
2057
2156
  if (problems.length) fail(problems.join(" "));
2058
2157
  });
2059
2158
  const run = scene.shots[i].run;
@@ -2065,14 +2164,14 @@ function applyOne(scene, edit) {
2065
2164
  const run = scene.shots[i].run;
2066
2165
  if (!Number.isInteger(edit.command) || edit.command < 0 || edit.command >= run.length)
2067
2166
  fail(`Shot "${edit.shot}" has ${run.length} command${run.length === 1 ? "" : "s"}; "command" must be an index from 0 to ${run.length - 1} (got ${edit.command}).`);
2068
- const command2 = { ...run[edit.command], ..._nullishCoalesce(edit.set, () => ( {})) };
2167
+ const command3 = { ...run[edit.command], ..._nullishCoalesce(edit.set, () => ( {})) };
2069
2168
  for (const key of _nullishCoalesce(edit.remove, () => ( []))) {
2070
2169
  if (key === "type") fail('"type" cannot be removed; replace the command instead.');
2071
- delete command2[key];
2170
+ delete command3[key];
2072
2171
  }
2073
- const problems = commandShapeProblems(command2, `Command ${edit.command}`);
2172
+ const problems = commandShapeProblems(command3, `Command ${edit.command}`);
2074
2173
  if (problems.length) fail(problems.join(" "));
2075
- return replaceShot(scene, i, { ...scene.shots[i], run: run.map((c, k) => k === edit.command ? command2 : c) });
2174
+ return replaceShot(scene, i, { ...scene.shots[i], run: run.map((c, k) => k === edit.command ? command3 : c) });
2076
2175
  }
2077
2176
  case "delete_commands": {
2078
2177
  const i = shotIndex(scene, edit.shot);
@@ -2103,9 +2202,9 @@ function applyOne(scene, edit) {
2103
2202
  else delete next.presets;
2104
2203
  }
2105
2204
  const names = new Set(edit.controls.map((c) => c.name));
2106
- for (const preset of _nullishCoalesce(next.presets, () => ( [])))
2107
- for (const key of Object.keys(preset.values))
2108
- if (!names.has(key)) fail(`Preset "${preset.name}" sets ${namedWithSuggestion(key, names)}, which is not a control.`);
2205
+ for (const preset2 of _nullishCoalesce(next.presets, () => ( [])))
2206
+ for (const key of Object.keys(preset2.values))
2207
+ if (!names.has(key)) fail(`Preset "${preset2.name}" sets ${namedWithSuggestion(key, names)}, which is not a control.`);
2109
2208
  return next;
2110
2209
  }
2111
2210
  case "replace_scene":
@@ -2125,9 +2224,9 @@ function applyMotionEdits(scene, edits) {
2125
2224
  let next = scene;
2126
2225
  for (let i = 0; i < list.length; i++) {
2127
2226
  const edit = list[i];
2128
- const prefix = list.length > 1 ? `op ${i + 1} (${_optionalChain([edit, 'optionalAccess', _87 => _87.op])}): ` : "";
2227
+ const prefix = list.length > 1 ? `op ${i + 1} (${_optionalChain([edit, 'optionalAccess', _97 => _97.op])}): ` : "";
2129
2228
  if (!edit || !MOTION_EDIT_OPS.includes(edit.op))
2130
- return { ok: false, error: `${prefix}Unknown op ${namedWithSuggestion(String(_optionalChain([edit, 'optionalAccess', _88 => _88.op])), MOTION_EDIT_OPS)}. Nothing was changed.` };
2229
+ return { ok: false, error: `${prefix}Unknown op ${namedWithSuggestion(String(_optionalChain([edit, 'optionalAccess', _98 => _98.op])), MOTION_EDIT_OPS)}. Nothing was changed.` };
2131
2230
  try {
2132
2231
  next = applyOne(next, edit);
2133
2232
  } catch (error) {
@@ -2149,7 +2248,7 @@ function resolveMotionEditRefs(scene, edits) {
2149
2248
  const resolved = [];
2150
2249
  let next = scene;
2151
2250
  for (let i = 0; i < edits.length; i++) {
2152
- const where = `op ${i + 1} (${_optionalChain([edits, 'access', _89 => _89[i], 'optionalAccess', _90 => _90.op])})`;
2251
+ const where = `op ${i + 1} (${_optionalChain([edits, 'access', _99 => _99[i], 'optionalAccess', _100 => _100.op])})`;
2153
2252
  let problem = null;
2154
2253
  const walk = (v) => {
2155
2254
  if (typeof v === "string") {
@@ -2182,9 +2281,9 @@ function resolveMotionEditRefs(scene, edits) {
2182
2281
  resolved.push(edit);
2183
2282
  const out = { op: edit.op };
2184
2283
  if (edit.op === "insert_shots") out.shots = edit.shots.map((s) => s.id);
2185
- if ("shot" in edit && typeof edit.shot === "string") out.shot = edit.op === "update_shot" && _optionalChain([edit, 'access', _91 => _91.set, 'optionalAccess', _92 => _92.id]) ? edit.set.id : edit.shot;
2284
+ if ("shot" in edit && typeof edit.shot === "string") out.shot = edit.op === "update_shot" && _optionalChain([edit, 'access', _101 => _101.set, 'optionalAccess', _102 => _102.id]) ? edit.set.id : edit.shot;
2186
2285
  if (edit.op === "add_commands") {
2187
- const at = _nullishCoalesce(_nullishCoalesce(edit.at, () => ( _optionalChain([before, 'access', _93 => _93.shots, 'access', _94 => _94.find, 'call', _95 => _95((s) => s.id === edit.shot), 'optionalAccess', _96 => _96.run, 'access', _97 => _97.length]))), () => ( 0));
2286
+ const at = _nullishCoalesce(_nullishCoalesce(edit.at, () => ( _optionalChain([before, 'access', _103 => _103.shots, 'access', _104 => _104.find, 'call', _105 => _105((s) => s.id === edit.shot), 'optionalAccess', _106 => _106.run, 'access', _107 => _107.length]))), () => ( 0));
2188
2287
  out.indexes = edit.commands.map((_, k) => at + k);
2189
2288
  }
2190
2289
  if (edit.op === "move_command") out.indexes = [edit.to];
@@ -2272,7 +2371,7 @@ function toFlairSvgMotion(scene, options = {}) {
2272
2371
  const springs = flairAtLeast(flair, "0.39");
2273
2372
  const strobes = flairAtLeast(flair, "0.39");
2274
2373
  const writeEase = (ease) => typeof ease === "string" || "spring" in ease ? ease : ease.map(round2);
2275
- const easeOf = (segment) => {
2374
+ const easeOf2 = (segment) => {
2276
2375
  const resolved = _chunk2FHLHZALcjs.resolveEasing.call(void 0, segment.ease);
2277
2376
  if (resolved !== "hold" && resolved.type === "spring") {
2278
2377
  if (!springs || segment.reverse) return void 0;
@@ -2337,13 +2436,13 @@ function toFlairSvgMotion(scene, options = {}) {
2337
2436
  if (!route) continue;
2338
2437
  const routeId = freshId(`${id}--follow-route`);
2339
2438
  ids.set(seg.path, routeId);
2340
- routes.push(`<path id="${routeId}" d="${_chunkO2UH7LZEcjs.bezierPathToSvg.call(void 0, route, { precision })}" fill="none"/>`);
2439
+ routes.push(`<path id="${routeId}" d="${_chunkXGD3FSNCcjs.bezierPathToSvg.call(void 0, route, { precision })}" fill="none"/>`);
2341
2440
  }
2342
2441
  if (ids.size) {
2343
2442
  const wrapper = freshId(`${id}--follow`);
2344
2443
  const centre = freshId(`${id}--follow-centre`);
2345
2444
  const c = entry.center;
2346
- const at = _optionalChain([motionNodeOffset, 'call', _98 => _98(entry, timeline, 0, nodes), 'optionalAccess', _99 => _99.follow]);
2445
+ const at = _optionalChain([motionNodeOffset, 'call', _108 => _108(entry, timeline, 0, nodes), 'optionalAccess', _109 => _109.follow]);
2347
2446
  const placed = at ? `translate(${f(at.x)} ${f(at.y)})${at.angle ? ` rotate(${f(at.angle)})` : ""}` : void 0;
2348
2447
  (wrap[id] ??= []).push({ id: wrapper, ...placed ? { transform: placed } : {} }, { id: centre, transform: `translate(${f(-c.x)} ${f(-c.y)})` });
2349
2448
  follows.push({ id, wrapper, routes: ids, segments: followSegments.filter((seg) => seg.path && ids.has(seg.path)) });
@@ -2373,7 +2472,7 @@ function toFlairSvgMotion(scene, options = {}) {
2373
2472
  raw: (t) => transformAt(t, true)
2374
2473
  });
2375
2474
  }
2376
- const paths = node.type === "path" ? [node] : _chunkYJLCBA7Vcjs.collectPathNodes.call(void 0, node);
2475
+ const paths = node.type === "path" ? [node] : _chunk7MD7C6XQcjs.collectPathNodes.call(void 0, node);
2377
2476
  for (const path of paths) {
2378
2477
  if (pathsSeen.has(path.id)) continue;
2379
2478
  pathsSeen.add(path.id);
@@ -2395,7 +2494,7 @@ function toFlairSvgMotion(scene, options = {}) {
2395
2494
  if (shapeSegments.length) {
2396
2495
  if (node.type !== "path") notes.push(`"${id}" is a group: its morph or effect animation is not exported (Flair tweens one path's "d").`);
2397
2496
  else {
2398
- const inverse = _chunkYJLCBA7Vcjs.invertMatrix.call(void 0, _chunkYJLCBA7Vcjs.nodeMatrix.call(void 0, node));
2497
+ const inverse = _chunk7MD7C6XQcjs.invertMatrix.call(void 0, _chunk7MD7C6XQcjs.nodeMatrix.call(void 0, node));
2399
2498
  channels.push({
2400
2499
  id,
2401
2500
  kind: "attr",
@@ -2404,10 +2503,10 @@ function toFlairSvgMotion(scene, options = {}) {
2404
2503
  exact: () => false,
2405
2504
  value: (t) => {
2406
2505
  const frame = motionFrameAt(scene, t, { draw: false, offsets: false });
2407
- const now = _chunkYJLCBA7Vcjs.expandVectorAppearance.call(void 0, _optionalChain([indexMotionNodes, 'call', _100 => _100(frame.document.nodes), 'access', _101 => _101.get, 'call', _102 => _102(id), 'optionalAccess', _103 => _103.node]));
2506
+ const now = _chunk7MD7C6XQcjs.expandVectorAppearance.call(void 0, _optionalChain([indexMotionNodes, 'call', _110 => _110(frame.document.nodes), 'access', _111 => _111.get, 'call', _112 => _112(id), 'optionalAccess', _113 => _113.node]));
2408
2507
  if (!now || now.type !== "path") return "";
2409
- const bezier = _chunkO2UH7LZEcjs.transformBezierPath.call(void 0, _chunkYJLCBA7Vcjs.vectorPathWorldBezier.call(void 0, now, _chunkYJLCBA7Vcjs.IDENTITY_MATRIX, { effects: false }), inverse);
2410
- return _chunkO2UH7LZEcjs.bezierPathToSvg.call(void 0, bezier, { precision: Math.min(precision, 2) });
2508
+ const bezier = _chunkXGD3FSNCcjs.transformBezierPath.call(void 0, _chunk7MD7C6XQcjs.vectorPathWorldBezier.call(void 0, now, _chunk7MD7C6XQcjs.IDENTITY_MATRIX, { effects: false }), inverse);
2509
+ return _chunkXGD3FSNCcjs.bezierPathToSvg.call(void 0, bezier, { precision: Math.min(precision, 2) });
2411
2510
  }
2412
2511
  });
2413
2512
  }
@@ -2425,8 +2524,8 @@ function toFlairSvgMotion(scene, options = {}) {
2425
2524
  ...delay > 0 ? { delay: round2(delay) } : {},
2426
2525
  ms: round2(ms),
2427
2526
  ease: written,
2428
- ..._optionalChain([segment, 'optionalAccess', _104 => _104.behavior]) ? { behavior: segment.behavior } : {},
2429
- ..._optionalChain([segment, 'optionalAccess', _105 => _105.steps]) ? { strobe: segment.steps } : {}
2527
+ ..._optionalChain([segment, 'optionalAccess', _114 => _114.behavior]) ? { behavior: segment.behavior } : {},
2528
+ ..._optionalChain([segment, 'optionalAccess', _115 => _115.steps]) ? { strobe: segment.steps } : {}
2430
2529
  };
2431
2530
  if (channel.kind === "view") animate.push({ ...base, view: to });
2432
2531
  else if (channel.kind === "draw") animate.push({ ...base, draw: [from, to] });
@@ -2451,7 +2550,7 @@ function toFlairSvgMotion(scene, options = {}) {
2451
2550
  const first = active[0];
2452
2551
  const key = (x) => JSON.stringify([x.ease, x.behavior, _nullishCoalesce(x.reverse, () => ( false)), _nullishCoalesce(x.steps, () => ( 0))]);
2453
2552
  const together = active.every((x) => x.start === a && x.end === b && channel.exact(x) && key(x) === key(first));
2454
- const curve = together ? easeOf(first) : void 0;
2553
+ const curve = together ? easeOf2(first) : void 0;
2455
2554
  const ease = curve && (!overshoots(curve) || channel.overshoot) ? curve : void 0;
2456
2555
  if (ease) {
2457
2556
  const from = channel.value(a);
@@ -2478,7 +2577,7 @@ function toFlairSvgMotion(scene, options = {}) {
2478
2577
  for (const seg of follow.segments) {
2479
2578
  const along = { id: follow.wrapper, along: follow.routes.get(seg.path), ...seg.rotate ? { orient: true } : {} };
2480
2579
  const progress = (t) => round2(Math.min(1, Math.max(0, Number(segmentValue(seg, t)))));
2481
- const curve = seg.end > seg.start ? plays(seg) ? easeOf(seg) : void 0 : "linear";
2580
+ const curve = seg.end > seg.start ? plays(seg) ? easeOf2(seg) : void 0 : "linear";
2482
2581
  if (curve && !(overshoots(curve) && seg.rotate)) {
2483
2582
  const ends = seg.reverse ? [Number(seg.to), Number(seg.from)] : [Number(seg.from), Number(seg.to)];
2484
2583
  animate.push({ ...along, ...seg.start > 0 ? { delay: round2(seg.start) } : {}, ms: round2(seg.end - seg.start), ease: writeEase(curve), ...seg.steps ? { strobe: seg.steps } : {}, at: [round2(ends[0]), round2(ends[1])] });
@@ -2513,10 +2612,10 @@ function toFlairSvgMotion(scene, options = {}) {
2513
2612
  }
2514
2613
  if (scene.motionBlur) notes.push("Motion blur is drawn on the stage only; Flair plays the scene sharp.");
2515
2614
  if (trigger && (_nullishCoalesce(scene.loop, () => ( "none"))) !== "none") notes.push(`The scene plays on ${trigger}, so its loop does not apply in Flair.`);
2516
- let svg = _chunkYJLCBA7Vcjs.vectorDocumentToSvg.call(void 0, { ...scene.artwork, nodes: _chunkYJLCBA7Vcjs.expandVectorAppearances.call(void 0, scene.artwork.nodes) }, { pretty: false, precision, wrap });
2615
+ let svg = _chunk7MD7C6XQcjs.vectorDocumentToSvg.call(void 0, { ...scene.artwork, nodes: _chunk7MD7C6XQcjs.expandVectorAppearances.call(void 0, scene.artwork.nodes) }, { pretty: false, precision, wrap });
2517
2616
  if (routes.length) svg = svg.replace(/^(<svg[^>]*>)/, `$1<defs>${routes.join("")}</defs>`);
2518
2617
  let motion = { svg, animate };
2519
- if (_optionalChain([scene, 'access', _106 => _106.controls, 'optionalAccess', _107 => _107.length])) {
2618
+ if (_optionalChain([scene, 'access', _116 => _116.controls, 'optionalAccess', _117 => _117.length])) {
2520
2619
  const declarations = scene.controls.map((c) => `{{${c.name}: color default=${c.default.toUpperCase()}${c.label ? ` label='${c.label.replace(/'/g, "\u2019")}'` : ""}}}`).join(" ");
2521
2620
  const swap = (text) => scene.controls.reduce((s, c) => s.replace(new RegExp(`${c.default}(?![0-9a-fA-F])`, "gi"), `{{${c.name}}}`), text);
2522
2621
  svg = svg.replace(/^(<svg[^>]*>)/, `$1<!-- ${declarations} -->`);
@@ -2538,7 +2637,7 @@ function toFlairVectorEffect(scene, options = {}) {
2538
2637
  tags: _nullishCoalesce(options.tags, () => ( [])),
2539
2638
  motion: out.motion,
2540
2639
  hold: out.hold,
2541
- ..._optionalChain([scene, 'access', _108 => _108.presets, 'optionalAccess', _109 => _109.length]) ? { presets: scene.presets } : {},
2640
+ ..._optionalChain([scene, 'access', _118 => _118.presets, 'optionalAccess', _119 => _119.length]) ? { presets: scene.presets } : {},
2542
2641
  ...out.trigger ? { trigger: out.trigger } : {}
2543
2642
  };
2544
2643
  }
@@ -2582,7 +2681,7 @@ function resolvePlaceholders(motion, notes) {
2582
2681
  const filled = text.replace(PLACEHOLDER, (_, name) => {
2583
2682
  const d = declared.get(name);
2584
2683
  if (!d) unknown.add(name);
2585
- return (_nullishCoalesce(_optionalChain([d, 'optionalAccess', _110 => _110.value]), () => ( ""))).replace(/"/g, '\\"');
2684
+ return (_nullishCoalesce(_optionalChain([d, 'optionalAccess', _120 => _120.value]), () => ( ""))).replace(/"/g, '\\"');
2586
2685
  });
2587
2686
  for (const name of unknown) notes.push(`Placeholder "{{${name}}}" has no declaration, so it was left empty.`);
2588
2687
  return { motion: JSON.parse(filled), controls };
@@ -2597,14 +2696,14 @@ function easingOf(ease, fallback) {
2597
2696
  return fallback;
2598
2697
  }
2599
2698
  function poseOf(value, entry) {
2600
- const t = _chunkYJLCBA7Vcjs.parseSvgTransform.call(void 0, String(value));
2601
- const o = _chunkYJLCBA7Vcjs.multiplyMatrices.call(void 0, _nullishCoalesce(_chunkYJLCBA7Vcjs.invertMatrix.call(void 0, _chunkYJLCBA7Vcjs.nodeMatrix.call(void 0, entry.node)), () => ( [1, 0, 0, 1, 0, 0])), t);
2699
+ const t = _chunk7MD7C6XQcjs.parseSvgTransform.call(void 0, String(value));
2700
+ const o = _chunk7MD7C6XQcjs.multiplyMatrices.call(void 0, _nullishCoalesce(_chunk7MD7C6XQcjs.invertMatrix.call(void 0, _chunk7MD7C6XQcjs.nodeMatrix.call(void 0, entry.node)), () => ( [1, 0, 0, 1, 0, 0])), t);
2602
2701
  const sx = Math.hypot(o[0], o[1]);
2603
2702
  if (!(sx > 1e-9)) return null;
2604
2703
  const sy = (o[0] * o[3] - o[1] * o[2]) / sx;
2605
2704
  if (Math.abs(o[2] + o[1] / sx * sy) > 1e-6 || Math.abs(o[3] - o[0] / sx * sy) > 1e-6) return null;
2606
2705
  const c = entry.center;
2607
- const moved = _chunkYJLCBA7Vcjs.applyMatrix.call(void 0, o, c);
2706
+ const moved = _chunk7MD7C6XQcjs.applyMatrix.call(void 0, o, c);
2608
2707
  return { dx: moved.x - c.x, dy: moved.y - c.y, rotation: Math.atan2(o[1], o[0]) * 180 / Math.PI, sx, sy };
2609
2708
  }
2610
2709
  var near = (a, b, eps = 1e-6) => Math.abs(a - b) < eps;
@@ -2636,7 +2735,7 @@ function commandsFor(anim, nodes, entered, notes) {
2636
2735
  if (anim.along) {
2637
2736
  const path = nodes.get(anim.along);
2638
2737
  if (!path || path.node.type !== "path") notes.push(`${where} follows "${anim.along}", which is not a path in the artwork; the follow was skipped.`);
2639
- else out.push({ type: "follow", targets, path: anim.along, from: _nullishCoalesce(_optionalChain([anim, 'access', _111 => _111.at, 'optionalAccess', _112 => _112[0]]), () => ( 0)), to: _nullishCoalesce(_optionalChain([anim, 'access', _113 => _113.at, 'optionalAccess', _114 => _114[1]]), () => ( 1)), ...anim.orient ? { rotate: true } : {}, ...timing, ...ease(MOTION_EASE_IN_OUT) });
2738
+ else out.push({ type: "follow", targets, path: anim.along, from: _nullishCoalesce(_optionalChain([anim, 'access', _121 => _121.at, 'optionalAccess', _122 => _122[0]]), () => ( 0)), to: _nullishCoalesce(_optionalChain([anim, 'access', _123 => _123.at, 'optionalAccess', _124 => _124[1]]), () => ( 1)), ...anim.orient ? { rotate: true } : {}, ...timing, ...ease(MOTION_EASE_IN_OUT) });
2640
2739
  }
2641
2740
  for (const [attr, [a, b]] of Object.entries(_nullishCoalesce(anim.attrs, () => ( {})))) {
2642
2741
  if (attr === "opacity") {
@@ -2647,7 +2746,7 @@ function commandsFor(anim, nodes, entered, notes) {
2647
2746
  out.push({ type: "enter", targets, style: "fade", ...timing, ...ease(MOTION_EASE_OUT) });
2648
2747
  } else out.push({ type: "fade", targets, to: Math.min(1, Math.max(0, to)), ...timing, ...ease(MOTION_EASE_IN_OUT) });
2649
2748
  } else if (attr === "fill" || attr === "stroke") {
2650
- if (_optionalChain([behavior, 'optionalAccess', _115 => _115.type]) === "pulse") out.push({ type: "color-pulse", targets, color: String(b), paint: attr, count: _nullishCoalesce(behavior.count, () => ( 1)), ...timing, ...ease("linear") });
2749
+ if (_optionalChain([behavior, 'optionalAccess', _125 => _125.type]) === "pulse") out.push({ type: "color-pulse", targets, color: String(b), paint: attr, count: _nullishCoalesce(behavior.count, () => ( 1)), ...timing, ...ease("linear") });
2651
2750
  else {
2652
2751
  if (behavior) notes.push(`${where}: a ${attr} ${behavior.type} became a plain colour change.`);
2653
2752
  out.push({ type: "style", targets, [attr]: String(b), ...timing, ...ease(MOTION_EASE_IN_OUT) });
@@ -2689,7 +2788,7 @@ function fromFlairSvgMotion(input, options = {}) {
2689
2788
  const notes = [];
2690
2789
  if (!input || typeof input.svg !== "string") throw new Error('Give a Flair SvgMotion: { svg: "<svg \u2026>", animate: [ \u2026 ] }.');
2691
2790
  const { motion, controls } = resolvePlaceholders(input, notes);
2692
- const artwork = _chunkYJLCBA7Vcjs.parseVectorSvg.call(void 0, motion.svg);
2791
+ const artwork = _chunk7MD7C6XQcjs.parseVectorSvg.call(void 0, motion.svg);
2693
2792
  const nodes = indexMotionNodes(artwork.nodes);
2694
2793
  const byStep = /* @__PURE__ */ new Map();
2695
2794
  for (const anim of _nullishCoalesce(motion.animate, () => ( []))) {
@@ -2722,9 +2821,9 @@ function fromFlairSvgMotion(input, options = {}) {
2722
2821
  if (!anim.id) continue;
2723
2822
  const at = Math.max(0, (_nullishCoalesce(anim.delay, () => ( 0))) - cameraMs);
2724
2823
  if (cameraMs && (_nullishCoalesce(anim.delay, () => ( 0))) < cameraMs) notes.push(`Step ${step}: a change to "${anim.id}" during the camera move now starts after it.`);
2725
- for (const command2 of commandsFor(anim, nodes, entered, notes)) {
2824
+ for (const command3 of commandsFor(anim, nodes, entered, notes)) {
2726
2825
  const first = run.length === 0;
2727
- run.push({ ...command2, ...first ? {} : { with: "previous" }, ...at - last > 0 || first ? { delay: first ? at : at - last } : {} });
2826
+ run.push({ ...command3, ...first ? {} : { with: "previous" }, ...at - last > 0 || first ? { delay: first ? at : at - last } : {} });
2728
2827
  if (first && at === 0) delete run[run.length - 1].delay;
2729
2828
  last = at;
2730
2829
  }
@@ -2936,11 +3035,11 @@ function toLottie(scene, options = {}) {
2936
3035
  const items = [];
2937
3036
  const shapeSegments = [...timeline.tracks.entries()].filter(([k]) => k.startsWith(`${path.id}\0`) && (k.endsWith("\0shape") || k.includes("\0effect:"))).flatMap(([, v]) => v);
2938
3037
  const geometryAt = (t) => {
2939
- if (!shapeSegments.length) return _chunkYJLCBA7Vcjs.vectorPathWorldBezier.call(void 0, path);
2940
- const now = _optionalChain([indexMotionNodes, 'call', _116 => _116(motionFrameAt(scene, t, { draw: false, offsets: false }).document.nodes), 'access', _117 => _117.get, 'call', _118 => _118(path.id), 'optionalAccess', _119 => _119.node]);
2941
- return now && now.type === "path" ? _chunkYJLCBA7Vcjs.vectorPathWorldBezier.call(void 0, _chunkYJLCBA7Vcjs.expandVectorAppearances.call(void 0, [now])[0]) : _chunkYJLCBA7Vcjs.vectorPathWorldBezier.call(void 0, path);
3038
+ if (!shapeSegments.length) return _chunk7MD7C6XQcjs.vectorPathWorldBezier.call(void 0, path);
3039
+ const now = _optionalChain([indexMotionNodes, 'call', _126 => _126(motionFrameAt(scene, t, { draw: false, offsets: false }).document.nodes), 'access', _127 => _127.get, 'call', _128 => _128(path.id), 'optionalAccess', _129 => _129.node]);
3040
+ return now && now.type === "path" ? _chunk7MD7C6XQcjs.vectorPathWorldBezier.call(void 0, _chunk7MD7C6XQcjs.expandVectorAppearances.call(void 0, [now])[0]) : _chunk7MD7C6XQcjs.vectorPathWorldBezier.call(void 0, path);
2942
3041
  };
2943
- const rest = lottieContours(_chunkO2UH7LZEcjs.toBezierPath.call(void 0, geometryAt(0)), round2);
3042
+ const rest = lottieContours(_chunkXGD3FSNCcjs.toBezierPath.call(void 0, geometryAt(0)), round2);
2944
3043
  rest.forEach((contour, n) => {
2945
3044
  let ks = { a: 0, k: contour };
2946
3045
  if (shapeSegments.length) {
@@ -2949,7 +3048,7 @@ function toLottie(scene, options = {}) {
2949
3048
  const count = Math.max(1, Math.ceil(end * fps / 1e3));
2950
3049
  for (let k = 0; k <= count && fits; k++) {
2951
3050
  const t = end * k / count;
2952
- const c = lottieContours(_chunkO2UH7LZEcjs.toBezierPath.call(void 0, geometryAt(t)), round2)[n];
3051
+ const c = lottieContours(_chunkXGD3FSNCcjs.toBezierPath.call(void 0, geometryAt(t)), round2)[n];
2953
3052
  if (!c || c.v.length !== contour.v.length || c.c !== contour.c) fits = false;
2954
3053
  else keys.push({ t: frame(t), s: [c], ...k < count ? { o: { x: [0], y: [0] }, i: { x: [1], y: [1] } } : {} });
2955
3054
  }
@@ -2974,28 +3073,28 @@ function toLottie(scene, options = {}) {
2974
3073
  }
2975
3074
  const stroke = paintOf(path.stroke, "stroke", path.id);
2976
3075
  if (stroke || strokeFrom) {
2977
- const colour = strokeFrom ? track(strokeFrom, "stroke") : [];
3076
+ const colour2 = strokeFrom ? track(strokeFrom, "stroke") : [];
2978
3077
  items.push({
2979
3078
  ty: "st",
2980
3079
  nm: "Stroke",
2981
- c: colour.length ? animate(colour, (t) => _nullishCoalesce(_nullishCoalesce(rgba(trackValue(colour, t)), () => ( stroke)), () => ( [0, 0, 0, 1]))) : { a: 0, k: (_nullishCoalesce(stroke, () => ( [0, 0, 0, 1]))).map(round2) },
3080
+ c: colour2.length ? animate(colour2, (t) => _nullishCoalesce(_nullishCoalesce(rgba(trackValue(colour2, t)), () => ( stroke)), () => ( [0, 0, 0, 1]))) : { a: 0, k: (_nullishCoalesce(stroke, () => ( [0, 0, 0, 1]))).map(round2) },
2982
3081
  o: { a: 0, k: 100 },
2983
3082
  w: widthFrom ? animate(track(widthFrom, "strokeWidth"), (t) => [Math.max(0, Number(_nullishCoalesce(_nullishCoalesce(trackValue(track(widthFrom, "strokeWidth"), t), () => ( path.strokeWidth)), () => ( 1))))]) : { a: 0, k: round2(_nullishCoalesce(path.strokeWidth, () => ( 1))) },
2984
3083
  lc: path.strokeLinecap === "round" ? 2 : path.strokeLinecap === "square" ? 3 : 1,
2985
3084
  lj: path.strokeLinejoin === "round" ? 2 : path.strokeLinejoin === "bevel" ? 3 : 1,
2986
3085
  ml: 4
2987
3086
  });
2988
- if (_optionalChain([path, 'access', _120 => _120.strokeDasharray, 'optionalAccess', _121 => _121.length])) notes.push(`"${path.id}": its dashes are not exported; the stroke is solid.`);
3087
+ if (_optionalChain([path, 'access', _130 => _130.strokeDasharray, 'optionalAccess', _131 => _131.length])) notes.push(`"${path.id}": its dashes are not exported; the stroke is solid.`);
2989
3088
  if (path.strokeAlign && path.strokeAlign !== "center") notes.push(`"${path.id}": its stroke is drawn centred (Lottie has no inside or outside strokes).`);
2990
3089
  }
2991
3090
  const fill = paintOf(path.fill, "fill", path.id);
2992
3091
  if (fill || fillFrom) {
2993
- const colour = fillFrom ? track(fillFrom, "fill") : [];
3092
+ const colour2 = fillFrom ? track(fillFrom, "fill") : [];
2994
3093
  const fade = fadeFrom ? track(fadeFrom, "fillOpacity") : [];
2995
3094
  items.push({
2996
3095
  ty: "fl",
2997
3096
  nm: "Fill",
2998
- c: colour.length ? animate(colour, (t) => _nullishCoalesce(_nullishCoalesce(rgba(trackValue(colour, t)), () => ( fill)), () => ( [0, 0, 0, 1]))) : { a: 0, k: (_nullishCoalesce(fill, () => ( [0, 0, 0, 1]))).map(round2) },
3097
+ c: colour2.length ? animate(colour2, (t) => _nullishCoalesce(_nullishCoalesce(rgba(trackValue(colour2, t)), () => ( fill)), () => ( [0, 0, 0, 1]))) : { a: 0, k: (_nullishCoalesce(fill, () => ( [0, 0, 0, 1]))).map(round2) },
2999
3098
  o: fade.length ? animate(fade, (t) => [Math.min(1, Math.max(0, Number(_nullishCoalesce(trackValue(fade, t), () => ( 1))))) * 100]) : { a: 0, k: 100 },
3000
3099
  r: path.fillRule === "evenodd" ? 2 : 1
3001
3100
  });
@@ -3012,8 +3111,8 @@ function toLottie(scene, options = {}) {
3012
3111
  if (node.type === "path") inner = [...pathShapes(node)];
3013
3112
  else {
3014
3113
  inner = shown(node.children).reverse().map(nodeGroup);
3015
- const m = _chunkYJLCBA7Vcjs.nodeMatrix.call(void 0, node);
3016
- if (m !== _chunkYJLCBA7Vcjs.IDENTITY_MATRIX) {
3114
+ const m = _chunk7MD7C6XQcjs.nodeMatrix.call(void 0, node);
3115
+ if (m !== _chunk7MD7C6XQcjs.IDENTITY_MATRIX) {
3017
3116
  const parts = staticParts(m);
3018
3117
  if (!parts) notes.push(`"${node.id}": its transform skews, which is not exported; it is drawn without the skew.`);
3019
3118
  else {
@@ -3026,7 +3125,7 @@ function toLottie(scene, options = {}) {
3026
3125
  const body = { ty: "gr", nm: `${node.id} (own)`, it: [...inner, innerTr] };
3027
3126
  return { ty: "gr", nm: _nullishCoalesce(node.name, () => ( node.id)), it: [body, poseTransform(entry)] };
3028
3127
  };
3029
- const artwork = { ...scene.artwork, nodes: _chunkYJLCBA7Vcjs.expandVectorAppearances.call(void 0, scene.artwork.nodes) };
3128
+ const artwork = { ...scene.artwork, nodes: _chunk7MD7C6XQcjs.expandVectorAppearances.call(void 0, scene.artwork.nodes) };
3030
3129
  const content = shown(artwork.nodes).reverse().map(nodeGroup);
3031
3130
  if (artwork.background && rgba(artwork.background))
3032
3131
  content.push({ ty: "gr", nm: "Background", it: [{ ty: "rc", nm: "Background", p: { a: 0, k: [artwork.width / 2, artwork.height / 2] }, s: { a: 0, k: [artwork.width, artwork.height] }, r: { a: 0, k: 0 } }, { ty: "fl", c: { a: 0, k: rgba(artwork.background).map(round2) }, o: { a: 0, k: 100 }, r: 1 }, STATIC_TR()] });
@@ -3069,6 +3168,383 @@ function toLottie(scene, options = {}) {
3069
3168
  return { lottie, sampled, notes: [...new Set(notes)] };
3070
3169
  }
3071
3170
 
3171
+ // src/animation-editor/pptx.ts
3172
+ var EMU = 9525;
3173
+ var kids = (el, name) => el ? Array.from(el.children).filter((c) => c.localName === name) : [];
3174
+ var kid = (el, name) => _nullishCoalesce(kids(el, name)[0], () => ( null));
3175
+ var deep = (el, name) => el ? Array.from(el.getElementsByTagNameNS("*", name)) : [];
3176
+ var num = (el, attr, fallback = 0) => {
3177
+ const v = _optionalChain([el, 'optionalAccess', _132 => _132.getAttribute, 'call', _133 => _133(attr)]);
3178
+ const n = v === null || v === void 0 ? NaN : Number(v);
3179
+ return Number.isFinite(n) ? n : fallback;
3180
+ };
3181
+ var dirname = (p) => p.slice(0, p.lastIndexOf("/") + 1);
3182
+ function resolve(base, target) {
3183
+ const out = [];
3184
+ for (const seg of (target.startsWith("/") ? target.slice(1) : base + target).split("/")) {
3185
+ if (seg === "..") out.pop();
3186
+ else if (seg && seg !== ".") out.push(seg);
3187
+ }
3188
+ return out.join("/");
3189
+ }
3190
+ async function xml(ctx, path) {
3191
+ if (!ctx.zip.has(path)) return null;
3192
+ const text = new TextDecoder().decode(await ctx.zip.read(path));
3193
+ const doc = new ctx.Parser().parseFromString(text, "application/xml");
3194
+ return doc.getElementsByTagName("parsererror").length ? null : doc;
3195
+ }
3196
+ async function rels(ctx, part) {
3197
+ const doc = await xml(ctx, `${dirname(part)}_rels/${part.slice(part.lastIndexOf("/") + 1)}.rels`);
3198
+ const out = /* @__PURE__ */ new Map();
3199
+ for (const r of deep(doc, "Relationship")) if (r.getAttribute("TargetMode") !== "External") out.set(_nullishCoalesce(r.getAttribute("Id"), () => ( "")), resolve(dirname(part), _nullishCoalesce(r.getAttribute("Target"), () => ( ""))));
3200
+ return out;
3201
+ }
3202
+ function colour(ctx, holder) {
3203
+ if (!holder) return null;
3204
+ const c = holder.firstElementChild;
3205
+ if (!c) return null;
3206
+ let hex = null;
3207
+ if (c.localName === "srgbClr") hex = `#${(_nullishCoalesce(c.getAttribute("val"), () => ( "000000"))).toLowerCase()}`;
3208
+ else if (c.localName === "sysClr") hex = `#${(_nullishCoalesce(c.getAttribute("lastClr"), () => ( "000000"))).toLowerCase()}`;
3209
+ else if (c.localName === "schemeClr") {
3210
+ const name = _nullishCoalesce(c.getAttribute("val"), () => ( ""));
3211
+ const alias = { tx1: "dk1", bg1: "lt1", tx2: "dk2", bg2: "lt2" };
3212
+ hex = _nullishCoalesce(ctx.theme.get(_nullishCoalesce(alias[name], () => ( name))), () => ( null));
3213
+ } else if (c.localName === "prstClr") hex = _nullishCoalesce({ black: "#000000", white: "#ffffff", red: "#ff0000", green: "#008000", blue: "#0000ff" }[_nullishCoalesce(c.getAttribute("val"), () => ( ""))], () => ( null));
3214
+ if (!hex || !/^#[0-9a-f]{6}$/.test(hex)) return null;
3215
+ const lum = kid(c, "lumMod");
3216
+ if (lum) {
3217
+ const k = num(lum, "val", 1e5) / 1e5;
3218
+ const off = num(kid(c, "lumOff"), "val", 0) / 1e5;
3219
+ hex = `#${[1, 3, 5].map((i) => Math.round(Math.min(255, Math.max(0, parseInt(hex.slice(i, i + 2), 16) * k + 255 * off))).toString(16).padStart(2, "0")).join("")}`;
3220
+ }
3221
+ return hex;
3222
+ }
3223
+ function preset(prst, w, h) {
3224
+ const poly = (pts) => [{ closed: true, anchors: pts.map(([x, y]) => ({ x, y })) }];
3225
+ switch (prst) {
3226
+ case "rect":
3227
+ return [_chunk7MD7C6XQcjs.rectSubpath.call(void 0, { x: 0, y: 0, width: w, height: h })];
3228
+ case "roundRect":
3229
+ return [_chunk7MD7C6XQcjs.rectSubpath.call(void 0, { x: 0, y: 0, width: w, height: h }, Math.min(w, h) * 0.16667)];
3230
+ case "ellipse":
3231
+ return [_chunk7MD7C6XQcjs.ellipseSubpath.call(void 0, { x: 0, y: 0, width: w, height: h })];
3232
+ case "triangle":
3233
+ return poly([[w / 2, 0], [w, h], [0, h]]);
3234
+ case "rtTriangle":
3235
+ return poly([[0, 0], [w, h], [0, h]]);
3236
+ case "diamond":
3237
+ return poly([[w / 2, 0], [w, h / 2], [w / 2, h], [0, h / 2]]);
3238
+ case "parallelogram":
3239
+ return poly([[w * 0.25, 0], [w, 0], [w * 0.75, h], [0, h]]);
3240
+ case "hexagon":
3241
+ return poly([[w * 0.25, 0], [w * 0.75, 0], [w, h / 2], [w * 0.75, h], [w * 0.25, h], [0, h / 2]]);
3242
+ case "line":
3243
+ case "straightConnector1":
3244
+ return [{ closed: false, anchors: [{ x: 0, y: 0 }, { x: w, y: h }] }];
3245
+ default:
3246
+ return null;
3247
+ }
3248
+ }
3249
+ function custom(geom, w, h) {
3250
+ const out = [];
3251
+ for (const path of deep(geom, "path")) {
3252
+ const sx = w / (num(path, "w") || w || 1);
3253
+ const sy = h / (num(path, "h") || h || 1);
3254
+ const pt = (el) => ({ x: num(el, "x") * sx, y: num(el, "y") * sy });
3255
+ let cur = null;
3256
+ for (const cmd of Array.from(path.children)) {
3257
+ const pts = kids(cmd, "pt").map(pt);
3258
+ if (cmd.localName === "moveTo" && pts[0]) {
3259
+ if (cur) out.push(cur);
3260
+ cur = { closed: false, anchors: [{ x: pts[0].x, y: pts[0].y }] };
3261
+ } else if (!cur) continue;
3262
+ else if (cmd.localName === "lnTo" && pts[0]) cur.anchors.push({ x: pts[0].x, y: pts[0].y });
3263
+ else if (cmd.localName === "cubicBezTo" && pts.length === 3) {
3264
+ cur.anchors[cur.anchors.length - 1].out = pts[0];
3265
+ cur.anchors.push({ x: pts[2].x, y: pts[2].y, in: pts[1] });
3266
+ } else if (cmd.localName === "quadBezTo" && pts.length === 2) {
3267
+ const p0 = cur.anchors[cur.anchors.length - 1];
3268
+ cur.anchors[cur.anchors.length - 1].out = { x: p0.x + 2 / 3 * (pts[0].x - p0.x), y: p0.y + 2 / 3 * (pts[0].y - p0.y) };
3269
+ cur.anchors.push({ x: pts[1].x, y: pts[1].y, in: { x: pts[1].x + 2 / 3 * (pts[0].x - pts[1].x), y: pts[1].y + 2 / 3 * (pts[0].y - pts[1].y) } });
3270
+ } else if (cmd.localName === "close") {
3271
+ cur.closed = true;
3272
+ out.push(cur);
3273
+ cur = null;
3274
+ }
3275
+ }
3276
+ if (cur) out.push(cur);
3277
+ }
3278
+ return out;
3279
+ }
3280
+ function placement(xfrm, map) {
3281
+ const off = kid(xfrm, "off");
3282
+ const ext = kid(xfrm, "ext");
3283
+ const [x, y, w, h] = map(num(off, "x"), num(off, "y"), num(ext, "cx"), num(ext, "cy"));
3284
+ const rot = num(xfrm, "rot") / 6e4 * (Math.PI / 180);
3285
+ const fx = _optionalChain([xfrm, 'optionalAccess', _134 => _134.getAttribute, 'call', _135 => _135("flipH")]) === "1" ? -1 : 1;
3286
+ const fy = _optionalChain([xfrm, 'optionalAccess', _136 => _136.getAttribute, 'call', _137 => _137("flipV")]) === "1" ? -1 : 1;
3287
+ const cx = x + w / 2;
3288
+ const cy = y + h / 2;
3289
+ const cos = Math.cos(rot);
3290
+ const sin = Math.sin(rot);
3291
+ const a = cos * fx;
3292
+ const b = sin * fx;
3293
+ const c = -sin * fy;
3294
+ const d = cos * fy;
3295
+ const e = cx - a * (w / 2) - c * (h / 2);
3296
+ const f = cy - b * (w / 2) - d * (h / 2);
3297
+ return { x, y, w, h, transform: [a, b, c, d, e, f].map((v) => v + 0) };
3298
+ }
3299
+ var idFor = (ctx, spid, name) => {
3300
+ const base = `${name.toLowerCase().replace(/[^a-z0-9]+/g, "-").replace(/^-+|-+$/g, "") || "shape"}-${spid}`;
3301
+ let id = base;
3302
+ for (let k = 2; ctx.used.has(id); k++) id = `${base}-${k}`;
3303
+ ctx.used.add(id);
3304
+ ctx.nodes.set(spid, id);
3305
+ return id;
3306
+ };
3307
+ function shapes(ctx, tree, map) {
3308
+ const out = [];
3309
+ for (const el of Array.from(tree.children)) {
3310
+ const nv = el.firstElementChild;
3311
+ const cNvPr = kid(nv, "cNvPr");
3312
+ const spid = num(cNvPr, "id", -1);
3313
+ const name = _nullishCoalesce(_optionalChain([cNvPr, 'optionalAccess', _138 => _138.getAttribute, 'call', _139 => _139("name")]), () => ( ""));
3314
+ if (el.localName === "grpSp") {
3315
+ const xfrm = kid(kid(el, "grpSpPr"), "xfrm");
3316
+ const off = kid(xfrm, "off");
3317
+ const ext = kid(xfrm, "ext");
3318
+ const chOff = kid(xfrm, "chOff");
3319
+ const chExt = kid(xfrm, "chExt");
3320
+ const sx = num(ext, "cx") / (num(chExt, "cx") || num(ext, "cx") || 1);
3321
+ const sy = num(ext, "cy") / (num(chExt, "cy") || num(ext, "cy") || 1);
3322
+ const inner = (x, y, w2, h2) => map(num(off, "x") + (x - num(chOff, "x")) * sx, num(off, "y") + (y - num(chOff, "y")) * sy, w2 * sx, h2 * sy);
3323
+ const id = idFor(ctx, spid, name || "group");
3324
+ out.push({ id, type: "group", ...name ? { name } : {}, children: shapes(ctx, el, inner) });
3325
+ continue;
3326
+ }
3327
+ if (el.localName === "pic" || el.localName === "graphicFrame") {
3328
+ ctx.notes.push(`"${name || `shape ${spid}`}" is a ${el.localName === "pic" ? "picture" : "chart, table or diagram"}, which the artwork cannot hold; it was left out.`);
3329
+ continue;
3330
+ }
3331
+ if (el.localName !== "sp" && el.localName !== "cxnSp") continue;
3332
+ const spPr = kid(el, "spPr");
3333
+ const { w, h, transform } = placement(kid(spPr, "xfrm"), map);
3334
+ const prst = _optionalChain([kid, 'call', _140 => _140(spPr, "prstGeom"), 'optionalAccess', _141 => _141.getAttribute, 'call', _142 => _142("prst")]);
3335
+ const cust = kid(spPr, "custGeom");
3336
+ let subpaths = cust ? custom(cust, w, h) : prst ? preset(prst, w, h) : null;
3337
+ if (!subpaths) {
3338
+ if (prst) ctx.notes.push(`"${name}" is a ${prst}, drawn as its bounding rectangle.`);
3339
+ subpaths = [_chunk7MD7C6XQcjs.rectSubpath.call(void 0, { x: 0, y: 0, width: w, height: h })];
3340
+ }
3341
+ const style = kid(el, "style");
3342
+ const fill = kid(spPr, "noFill") ? null : _nullishCoalesce(colour(ctx, kid(spPr, "solidFill")), () => ( (el.localName === "cxnSp" ? null : colour(ctx, kid(style, "fillRef")))));
3343
+ const ln = kid(spPr, "ln");
3344
+ const stroke = kid(ln, "noFill") ? null : _nullishCoalesce(colour(ctx, kid(ln, "solidFill")), () => ( colour(ctx, kid(style, "lnRef"))));
3345
+ if (kid(spPr, "gradFill")) ctx.notes.push(`"${name}" has a gradient fill, imported as its first colour or none.`);
3346
+ const text = deep(kid(el, "txBody"), "t").map((t) => _nullishCoalesce(t.textContent, () => ( ""))).join("").trim();
3347
+ if (text) ctx.notes.push(`"${name}" has text ("${text.slice(0, 40)}${text.length > 40 ? "\u2026" : ""}"), which the artwork cannot hold; only its shape was imported.`);
3348
+ const node = {
3349
+ id: idFor(ctx, spid, name),
3350
+ type: "path",
3351
+ ...name ? { name } : {},
3352
+ transform,
3353
+ fill: _nullishCoalesce(fill, () => ( (kid(spPr, "gradFill") ? colour(ctx, kid(deep(spPr, "gs")[0], "srgbClr") ? deep(spPr, "gs")[0] : null) : null))),
3354
+ stroke,
3355
+ strokeWidth: Math.max(0.5, (num(ln, "w", 12700) || 12700) / EMU),
3356
+ subpaths
3357
+ };
3358
+ out.push(node);
3359
+ }
3360
+ return out;
3361
+ }
3362
+ var DIRECTION_FROM = { 1: "down", 2: "left", 4: "up", 8: "right" };
3363
+ var DIRECTION_TO = { 1: "up", 2: "right", 4: "down", 8: "left" };
3364
+ function easeOf(ctn) {
3365
+ const accel = num(ctn, "accel") / 1e5;
3366
+ const decel = num(ctn, "decel") / 1e5;
3367
+ if (!accel && !decel) return void 0;
3368
+ return { type: "cubic-bezier", x1: Math.min(1, accel), y1: 0, x2: Math.max(0, 1 - decel), y2: 1 };
3369
+ }
3370
+ function command(ctx, ctn, scene, artwork, target, hidden) {
3371
+ const cls = ctn.getAttribute("presetClass");
3372
+ const id = num(ctn, "presetID");
3373
+ const sub = num(ctn, "presetSubtype");
3374
+ const durs = [ctn, ...deep(ctn, "cTn")].map((c) => num(c, "dur", NaN)).filter(Number.isFinite);
3375
+ const duration = durs.length ? Math.max(...durs) : void 0;
3376
+ const ease = easeOf(ctn);
3377
+ const base = { targets: [target], ...duration !== void 0 ? { duration } : {}, ...ease ? { ease } : {} };
3378
+ const repeat = num(ctn, "repeatCount", 1e3) / 1e3;
3379
+ const auto = ctn.getAttribute("autoRev") === "1";
3380
+ const timing = auto || repeat > 1 ? { repeat: Math.max(1, Math.round(repeat)) * (auto ? 2 : 1), ...auto ? { repeatMode: "mirror" } : {} } : {};
3381
+ const label = `${cls} effect ${id} on "${target}"`;
3382
+ if (cls === "entr") {
3383
+ const by = {
3384
+ 1: { type: "enter", ...base, duration: 0 },
3385
+ 2: { type: "enter", style: "slide", ...base, direction: _nullishCoalesce(DIRECTION_FROM[sub], () => ( "up")), distance: Math.round(Math.max(scene.width, scene.height) / 3) },
3386
+ 10: { type: "enter", style: "fade", ...base },
3387
+ 9: { type: "enter", style: "fade", ...base },
3388
+ 22: { type: "enter", style: "wipe", ...base, direction: _nullishCoalesce(DIRECTION_FROM[sub], () => ( "right")) },
3389
+ 16: { type: "enter", style: "split", ...base, orientation: sub === 26 || sub === 42 ? "vertical" : "horizontal" },
3390
+ 6: { type: "enter", style: "iris", ...base, shape: "circle" },
3391
+ 4: { type: "enter", style: "iris", ...base, shape: "box" },
3392
+ 53: { type: "enter", style: "zoom", ...base },
3393
+ 23: { type: "enter", style: "zoom", ...base },
3394
+ 31: { type: "enter", style: "pop", ...base },
3395
+ 26: { type: "enter", style: "pop", ...base },
3396
+ 55: { type: "enter", style: "grow", ...base },
3397
+ 37: { type: "enter", style: "rise", ...base },
3398
+ 42: { type: "enter", style: "rise", ...base }
3399
+ };
3400
+ if (by[id]) return by[id];
3401
+ ctx.notes.push(`${label} has no match here; it fades in instead.`);
3402
+ return { type: "enter", style: "fade", ...base };
3403
+ }
3404
+ if (cls === "exit") {
3405
+ const by = {
3406
+ 1: { type: "exit", ...base, duration: 0 },
3407
+ 10: { type: "exit", style: "fade", ...base },
3408
+ 9: { type: "exit", style: "fade", ...base },
3409
+ 2: { type: "exit", style: "slide", ...base, direction: _nullishCoalesce(DIRECTION_TO[sub], () => ( "down")), distance: Math.round(Math.max(scene.width, scene.height) / 3) },
3410
+ 22: { type: "exit", style: "wipe", ...base, direction: _nullishCoalesce(DIRECTION_TO[sub], () => ( "right")) },
3411
+ 53: { type: "exit", style: "shrink", ...base },
3412
+ 31: { type: "exit", style: "shrink", ...base }
3413
+ };
3414
+ if (by[id]) return by[id];
3415
+ ctx.notes.push(`${label} has no match here; it fades out instead.`);
3416
+ return { type: "exit", style: "fade", ...base };
3417
+ }
3418
+ if (cls === "emph") {
3419
+ const rot = deep(ctn, "animRot")[0];
3420
+ const scale = kid(deep(ctn, "animScale")[0], "by");
3421
+ const clr = colour(ctx, kid(deep(ctn, "animClr")[0], "to"));
3422
+ switch (id) {
3423
+ case 26:
3424
+ return { type: "pulse", ...base, ...timing };
3425
+ case 27:
3426
+ return { type: "color-pulse", ...base, ...clr ? { color: clr } : {}, ...timing };
3427
+ case 32:
3428
+ return { type: "teeter", ...base, ...timing };
3429
+ case 34:
3430
+ return { type: "wave", ...base, ...timing };
3431
+ case 8:
3432
+ return { type: "rotate", ...base, by: rot ? num(rot, "by") / 6e4 : 360, ...timing };
3433
+ case 6:
3434
+ return { type: "scale", ...base, to: scale ? { x: num(scale, "x", 1e5) / 1e5, y: num(scale, "y", 1e5) / 1e5 } : 1.5, ...timing };
3435
+ case 9:
3436
+ return { type: "fade", ...base, to: 0.5, ...timing };
3437
+ case 1:
3438
+ case 19:
3439
+ return clr ? { type: "style", ...base, fill: clr, ...timing } : null;
3440
+ case 7:
3441
+ return clr ? { type: "style", ...base, stroke: clr, ...timing } : null;
3442
+ }
3443
+ ctx.notes.push(`${label} has no match here and was skipped.`);
3444
+ return null;
3445
+ }
3446
+ if (cls === "path") {
3447
+ const motion = deep(ctn, "animMotion")[0];
3448
+ const d = _optionalChain([motion, 'optionalAccess', _143 => _143.getAttribute, 'call', _144 => _144("path")]);
3449
+ if (!d) return null;
3450
+ const entry = artwork.flatMap(function all(n) {
3451
+ return n.type === "group" ? [n, ...n.children.flatMap(all)] : [n];
3452
+ }).find((n) => n.id === target);
3453
+ const t = _nullishCoalesce(_optionalChain([entry, 'optionalAccess', _145 => _145.transform]), () => ( [1, 0, 0, 1, 0, 0]));
3454
+ const p = _optionalChain([entry, 'optionalAccess', _146 => _146.type]) === "path" ? _optionalChain([entry, 'access', _147 => _147.subpaths, 'access', _148 => _148[0], 'optionalAccess', _149 => _149.anchors]) : void 0;
3455
+ const xs = (_nullishCoalesce(p, () => ( []))).map((a) => a.x);
3456
+ const ys = (_nullishCoalesce(p, () => ( []))).map((a) => a.y);
3457
+ const lx = xs.length ? (Math.min(...xs) + Math.max(...xs)) / 2 : 0;
3458
+ const ly = ys.length ? (Math.min(...ys) + Math.max(...ys)) / 2 : 0;
3459
+ const cx = t[0] * lx + t[2] * ly + t[4];
3460
+ const cy = t[1] * lx + t[3] * ly + t[5];
3461
+ const nums = _nullishCoalesce(d.match(/[MLCZE]|-?[\d.]+(?:e-?\d+)?/gi), () => ( []));
3462
+ const sp = { closed: false, anchors: [] };
3463
+ let i = 0;
3464
+ const read = () => ({ x: cx + Number(nums[i++]) * scene.width, y: cy + Number(nums[i++]) * scene.height });
3465
+ while (i < nums.length) {
3466
+ const c = nums[i++].toUpperCase();
3467
+ if (c === "M" || c === "L") sp.anchors.push(read());
3468
+ else if (c === "C") {
3469
+ const c1 = read();
3470
+ const c2 = read();
3471
+ const e = read();
3472
+ if (sp.anchors.length) sp.anchors[sp.anchors.length - 1].out = c1;
3473
+ sp.anchors.push({ ...e, in: c2 });
3474
+ } else if (c === "Z") sp.closed = true;
3475
+ }
3476
+ if (sp.anchors.length < 2) return null;
3477
+ const pathId = `${target}-path-${hidden.length + 1}`;
3478
+ hidden.push({ id: pathId, type: "path", name: "Motion path", visible: false, fill: null, stroke: "#94a3b8", subpaths: [sp] });
3479
+ return { type: "follow", ...base, path: pathId, ...timing };
3480
+ }
3481
+ return null;
3482
+ }
3483
+ async function fromPptxAnimation(data, options = {}) {
3484
+ const Parser = _nullishCoalesce(options.DOMParser, () => ( globalThis.DOMParser));
3485
+ if (!Parser) throw new Error("No DOMParser: pass one in options (jsdom's or xmldom's in Node).");
3486
+ let zip;
3487
+ try {
3488
+ zip = await _chunkEMQWJA3Gcjs.readZip.call(void 0, data, options);
3489
+ } catch (error) {
3490
+ throw new Error(`This is not a PowerPoint file (.pptx): ${error.message}`);
3491
+ }
3492
+ const ctx = { zip, Parser, notes: [], theme: /* @__PURE__ */ new Map(), nodes: /* @__PURE__ */ new Map(), used: /* @__PURE__ */ new Set() };
3493
+ const pres = await xml(ctx, "ppt/presentation.xml");
3494
+ if (!pres) throw new Error("This is not a PowerPoint file (.pptx): it has no ppt/presentation.xml.");
3495
+ const presRels = await rels(ctx, "ppt/presentation.xml");
3496
+ const slidePaths = deep(pres, "sldId").map((s) => _nullishCoalesce(presRels.get(_nullishCoalesce(_nullishCoalesce(s.getAttributeNS("http://schemas.openxmlformats.org/officeDocument/2006/relationships", "id"), () => ( s.getAttribute("r:id"))), () => ( ""))), () => ( ""))).filter(Boolean);
3497
+ const n = _nullishCoalesce(options.slide, () => ( 1));
3498
+ if (!(Number.isInteger(n) && n >= 1 && n <= slidePaths.length)) throw new Error(`There is no slide ${n}; the file has ${slidePaths.length} slide${slidePaths.length === 1 ? "" : "s"}.`);
3499
+ const size = deep(pres, "sldSz")[0];
3500
+ const width = Math.round(num(size, "cx", 12192e3) / EMU);
3501
+ const height = Math.round(num(size, "cy", 6858e3) / EMU);
3502
+ for (const [, path] of presRels) {
3503
+ if (!path.includes("theme/")) continue;
3504
+ const theme = await xml(ctx, path);
3505
+ const scheme = deep(theme, "clrScheme")[0];
3506
+ for (const c of Array.from(_nullishCoalesce(_optionalChain([scheme, 'optionalAccess', _150 => _150.children]), () => ( [])))) {
3507
+ const v = c.firstElementChild;
3508
+ const hex = _optionalChain([v, 'optionalAccess', _151 => _151.localName]) === "srgbClr" ? v.getAttribute("val") : _optionalChain([v, 'optionalAccess', _152 => _152.getAttribute, 'call', _153 => _153("lastClr")]);
3509
+ if (hex) ctx.theme.set(c.localName, `#${hex.toLowerCase()}`);
3510
+ }
3511
+ break;
3512
+ }
3513
+ const slide = await xml(ctx, slidePaths[n - 1]);
3514
+ if (!slide) throw new Error(`Slide ${n} cannot be read.`);
3515
+ const tree = deep(slide, "spTree")[0];
3516
+ const toPx = (x, y, w, h) => [x / EMU, y / EMU, w / EMU, h / EMU];
3517
+ const nodes = tree ? shapes(ctx, tree, toPx) : [];
3518
+ const bg = _nullishCoalesce(colour(ctx, _nullishCoalesce(deep(deep(slide, "bg")[0], "solidFill")[0], () => ( null))), () => ( "#ffffff"));
3519
+ const hidden = [];
3520
+ const shots = [];
3521
+ const effects = deep(deep(slide, "timing")[0], "cTn").filter((c) => c.hasAttribute("presetClass"));
3522
+ for (const ctn of effects) {
3523
+ const spid = num(deep(ctn, "spTgt")[0], "spid", -1);
3524
+ const target = ctx.nodes.get(spid);
3525
+ if (!target) {
3526
+ ctx.notes.push(`An effect targets shape ${spid}, which was not imported; it was skipped.`);
3527
+ continue;
3528
+ }
3529
+ if (deep(ctn, "txEl").length) ctx.notes.push(`An effect on "${target}" animates part of its text, which was not imported; it animates the whole shape.`);
3530
+ const cmd = command(ctx, ctn, { width, height }, nodes, target, hidden);
3531
+ if (!cmd) continue;
3532
+ const kind = ctn.getAttribute("nodeType");
3533
+ const delay = num(kid(kid(ctn, "stCondLst"), "cond"), "delay", 0);
3534
+ if (kind === "clickEffect" || !shots.length) shots.push({ id: `click-${shots.length + 1}`, run: [] });
3535
+ const run = shots[shots.length - 1].run;
3536
+ const placed = { ...cmd, ...delay > 0 ? { delay } : {}, ...kind === "withEffect" && run.length ? { with: "previous" } : {} };
3537
+ run.push(placed);
3538
+ }
3539
+ if (effects.length && !shots.length) ctx.notes.push("The slide's effects could not be read.");
3540
+ const artwork = { width, height, background: bg, nodes: [...nodes, ...hidden] };
3541
+ const scene = { version: 1, name: `slide-${n}`, artwork, shots, ...shots.length > 1 ? { trigger: "click" } : {} };
3542
+ const problems = motionSceneShapeProblems(scene);
3543
+ if (problems.length) throw new Error(`The imported scene is not well formed:
3544
+ ${problems.join("\n")}`);
3545
+ return { scene, notes: [...new Set(ctx.notes)], issues: checkMotionScene(scene), slides: slidePaths.length };
3546
+ }
3547
+
3072
3548
  // src/animation-editor/controller.ts
3073
3549
  var BLANK = { width: 800, height: 450, background: "#ffffff", nodes: [] };
3074
3550
  var AnimationEditorController = (_class = class {
@@ -3136,7 +3612,7 @@ var AnimationEditorController = (_class = class {
3136
3612
  time: p.time,
3137
3613
  playing: p.playing,
3138
3614
  loop: p.loop,
3139
- currentShot: _nullishCoalesce(_optionalChain([motionShotAt, 'call', _122 => _122(timeline, p.time), 'optionalAccess', _123 => _123.id]), () => ( null)),
3615
+ currentShot: _nullishCoalesce(_optionalChain([motionShotAt, 'call', _154 => _154(timeline, p.time), 'optionalAccess', _155 => _155.id]), () => ( null)),
3140
3616
  selection: this.selection,
3141
3617
  readOnly: this.readOnly,
3142
3618
  error: this.error,
@@ -3175,7 +3651,7 @@ var AnimationEditorController = (_class = class {
3175
3651
  this.applying = false;
3176
3652
  }
3177
3653
  this.notify();
3178
- _optionalChain([this, 'access', _124 => _124.onChange, 'optionalCall', _125 => _125(scene)]);
3654
+ _optionalChain([this, 'access', _156 => _156.onChange, 'optionalCall', _157 => _157(scene)]);
3179
3655
  }
3180
3656
  commit(label, next) {
3181
3657
  const before = this.scene;
@@ -3184,14 +3660,14 @@ var AnimationEditorController = (_class = class {
3184
3660
  this.notify();
3185
3661
  return;
3186
3662
  }
3187
- const command2 = {
3663
+ const command3 = {
3188
3664
  name: label,
3189
3665
  tag: "animation-editor",
3190
3666
  execute: () => this.setScene(next),
3191
3667
  undo: () => this.setScene(before),
3192
3668
  redo: () => this.setScene(next)
3193
3669
  };
3194
- this.history.execute(command2);
3670
+ this.history.execute(command3);
3195
3671
  }
3196
3672
  refuse(message) {
3197
3673
  this.error = message;
@@ -3348,6 +3824,8 @@ var BASE = {
3348
3824
  with: { type: "string", enum: ["after", "previous"], description: '"after" (default) starts when the commands before it finish; "previous" starts together with the command before.' },
3349
3825
  ease: EASE,
3350
3826
  note: { type: "string", description: "Why this command is here, in a few words." },
3827
+ split: { type: "string", enum: ["letter", "word", "line"], description: "For outlined text (a group of letter paths): animate it a letter, word or line at a time in reading order, staggered (default 40 ms a letter, 120 a word, 300 a line). get_text_layout shows the units." },
3828
+ cascade: { type: "string", enum: ["forward", "backward", "center", "edges", "random"], description: "With split: the order units go in (default forward; random is the same every time)." },
3351
3829
  repeat: { type: "integer", minimum: 1, maximum: 1e3, description: 'Play it this many times (default 1); "duration" is one cycle. Not for enter or exit.' },
3352
3830
  repeatMode: { type: "string", enum: [...MOTION_REPEAT_MODES], description: "loop (default) restarts each cycle, mirror plays every other one backwards, offset carries on from where the last ended (rotate by 90 with repeat 4 makes a full turn)." },
3353
3831
  steps: { type: "number", exclusiveMinimum: 0, maximum: 120, description: `Stepped time: hold on a grid of this many frames per second (12 = "on twos"). Default: the scene's steps, else smooth.` }
@@ -3364,11 +3842,11 @@ var DRAW_ORDER = {
3364
3842
  sortBy: { type: "string", enum: ["document", "distance"], description: "With sequence: stacking order (default) or distance from origin (a radial reveal)." },
3365
3843
  origin: object({ x: { type: "number", description: "Document units." }, y: { type: "number", description: "Document units." } }, ["x", "y"], "With sortBy distance: where strokes are drawn out from (default the target's centre).")
3366
3844
  };
3367
- var command = (type, description, extra = {}, required = []) => object({ type: { const: type, description }, ...BASE, ...extra }, ["type", "targets", ...required], description);
3845
+ var command2 = (type, description, extra = {}, required = []) => object({ type: { const: type, description }, ...BASE, ...extra }, ["type", "targets", ...required], description);
3368
3846
  var MOTION_COMMAND_SCHEMA = {
3369
3847
  description: `A command (one of ${MOTION_COMMAND_TYPES.join(", ")}). Commands in a shot play one after another unless "with": "previous".`,
3370
3848
  oneOf: [
3371
- command("enter", "Brings hidden targets in; they are hidden before this shot.", {
3849
+ command2("enter", "Brings hidden targets in; they are hidden before this shot.", {
3372
3850
  ...DRAW_ORDER,
3373
3851
  style: {
3374
3852
  type: "string",
@@ -3381,18 +3859,18 @@ var MOTION_COMMAND_SCHEMA = {
3381
3859
  shape: { type: "string", enum: [...MOTION_IRIS_SHAPES], description: "For iris: open through a circle (default; an ellipse on a box that is not square) or a box." },
3382
3860
  orientation: { type: "string", enum: [...MOTION_SPLIT_ORIENTATIONS], description: "For split: horizontal (default) opens up and down from a horizontal line through the middle; vertical opens left and right." }
3383
3861
  }),
3384
- command("exit", "Takes targets out.", {
3862
+ command2("exit", "Takes targets out.", {
3385
3863
  ...DRAW_ORDER,
3386
3864
  style: { type: "string", enum: [...MOTION_EXIT_STYLES], description: "fade (default), shrink, slide, undraw, or wipe (hides behind a travelling edge through a clip over the target's box: a wipe in, reversed)." },
3387
3865
  direction: { type: "string", enum: [...MOTION_DIRECTIONS], description: "For slide: direction of travel (default down). For wipe: the way the edge travels (default right, hiding from the left)." },
3388
3866
  distance: { type: "number", description: "For slide: document units (default 40)." }
3389
3867
  }),
3390
- command("draw", "Draws strokes on (or off) to a fraction of their length.", { ...DRAW_ORDER, comet: FRACTION("A travelling stroke this fraction of the length long: it runs along the path and off the end (leave out from and to)."), to: FRACTION("Fraction drawn at the end (default 1)."), from: FRACTION("Fraction drawn at the start (default: what is drawn now).") }),
3391
- command("move", "Moves targets by an offset or so their centre lands on a point.", { via: { type: "array", items: POINT("A point the centre passes through."), minItems: 1, maxItems: 64, description: 'Waypoints (document units) the centre passes through on the way to "to" or "by", smoothly (velocity carries through each) and without overshooting any. Default length 700 ms + 400 per waypoint.' }, pacing: { type: "string", enum: ["even", "equal"], description: "With via: even (default) keeps the speed steady along the path; equal gives each stretch the same time." }, blend: BLEND("replace"), by: POINT("Offset in document units."), to: POINT("Point (document coordinates) for the centre.") }),
3392
- command("rotate", "Turns targets about their centre (degrees, clockwise).", { blend: BLEND("replace"), by: { type: "number", description: "Degrees to turn by." }, to: { type: "number", description: "Angle to turn to, in degrees from rest." } }),
3393
- command("scale", "Scales targets about their centre (1 = rest size).", { blend: BLEND("replace"), to: { description: "A factor, or { x, y } factors.", anyOf: [{ type: "number" }, POINT("Factors per axis.")] }, geometric: { type: "boolean", description: "Grow by a steady factor, which reads as an even zoom (default false)." } }, ["to"]),
3394
- command("fade", "Changes opacity.", { to: FRACTION("Opacity at the end (0\u20131).") }, ["to"]),
3395
- command("style", "Blends solid fill / stroke colours (in OKLab) and stroke width. On a group it changes every path in it.", {
3868
+ command2("draw", "Draws strokes on (or off) to a fraction of their length.", { ...DRAW_ORDER, comet: FRACTION("A travelling stroke this fraction of the length long: it runs along the path and off the end (leave out from and to)."), to: FRACTION("Fraction drawn at the end (default 1)."), from: FRACTION("Fraction drawn at the start (default: what is drawn now).") }),
3869
+ command2("move", "Moves targets by an offset or so their centre lands on a point.", { via: { type: "array", items: POINT("A point the centre passes through."), minItems: 1, maxItems: 64, description: 'Waypoints (document units) the centre passes through on the way to "to" or "by", smoothly (velocity carries through each) and without overshooting any. Default length 700 ms + 400 per waypoint.' }, pacing: { type: "string", enum: ["even", "equal"], description: "With via: even (default) keeps the speed steady along the path; equal gives each stretch the same time." }, blend: BLEND("replace"), by: POINT("Offset in document units."), to: POINT("Point (document coordinates) for the centre.") }),
3870
+ command2("rotate", "Turns targets about their centre (degrees, clockwise).", { blend: BLEND("replace"), by: { type: "number", description: "Degrees to turn by." }, to: { type: "number", description: "Angle to turn to, in degrees from rest." } }),
3871
+ command2("scale", "Scales targets about their centre (1 = rest size).", { blend: BLEND("replace"), to: { description: "A factor, or { x, y } factors.", anyOf: [{ type: "number" }, POINT("Factors per axis.")] }, geometric: { type: "boolean", description: "Grow by a steady factor, which reads as an even zoom (default false)." } }, ["to"]),
3872
+ command2("fade", "Changes opacity.", { to: FRACTION("Opacity at the end (0\u20131).") }, ["to"]),
3873
+ command2("style", "Blends solid fill / stroke colours (in OKLab) and stroke width. On a group it changes every path in it.", {
3396
3874
  fill: { type: "string", description: "CSS colour (hex, rgb(), rgba())." },
3397
3875
  stroke: { type: "string", description: "CSS colour (hex, rgb(), rgba())." },
3398
3876
  strokeWidth: { type: "number", minimum: 0, description: "Document units." }
@@ -3411,24 +3889,24 @@ var MOTION_COMMAND_SCHEMA = {
3411
3889
  ["type", "targets", "clip"],
3412
3890
  "Plays a clip on the targets."
3413
3891
  ),
3414
- command("match", `Smart Animate: tweens a group into another group (usually hidden), part by part. Parts pair by name, then by id with the group ids taken out (card-a-title \u2194 card-b-title), then by kind in order; each takes its partner's shape, paint and place. Unpaired parts fade out; parts of "into" with no partner do not appear (check_animation lists them).`, { into: { type: "string", minLength: 1, description: "Id of the group to become." } }, ["into"]),
3415
- command("morph", "Morphs each target into the shape (and paint) of another node, which usually stays hidden.", { into: { type: "string", minLength: 1, description: "Id of the node whose shape to take." } }, ["into"]),
3416
- command("follow", "Moves targets along a path node, centre on the path.", {
3892
+ command2("match", `Smart Animate: tweens a group into another group (usually hidden), part by part. Parts pair by name, then by id with the group ids taken out (card-a-title \u2194 card-b-title), then by kind in order; each takes its partner's shape, paint and place. Unpaired parts fade out; parts of "into" with no partner do not appear (check_animation lists them).`, { into: { type: "string", minLength: 1, description: "Id of the group to become." } }, ["into"]),
3893
+ command2("morph", "Morphs each target into the shape (and paint) of another node, which usually stays hidden.", { into: { type: "string", minLength: 1, description: "Id of the node whose shape to take." } }, ["into"]),
3894
+ command2("follow", "Moves targets along a path node, centre on the path.", {
3417
3895
  path: { type: "string", minLength: 1, description: "Id of a path node (it can be hidden)." },
3418
3896
  rotate: { type: "boolean", description: "Turn with the path (default false); moves and shakes made meanwhile turn with it too." },
3419
3897
  from: FRACTION("Where to start along the path (default 0)."),
3420
3898
  to: FRACTION("Where to end along the path (default 1).")
3421
3899
  }, ["path"]),
3422
- command("pulse", "Scales up and back to draw the eye.", { blend: BLEND("add"), amount: { type: "number", description: "Extra scale at the peak (default 0.15)." }, count: { type: "integer", minimum: 1, description: "Pulses (default 1)." } }),
3423
- command("shake", "Shakes side to side (or up and down), settling.", {
3900
+ command2("pulse", "Scales up and back to draw the eye.", { blend: BLEND("add"), amount: { type: "number", description: "Extra scale at the peak (default 0.15)." }, count: { type: "integer", minimum: 1, description: "Pulses (default 1)." } }),
3901
+ command2("shake", "Shakes side to side (or up and down), settling.", {
3424
3902
  blend: BLEND("add"),
3425
3903
  amount: { type: "number", description: "Document units (default 8)." },
3426
3904
  count: { type: "integer", minimum: 1, description: "Shakes (default 3)." },
3427
3905
  axis: { type: "string", enum: ["horizontal", "vertical"], description: "Default horizontal." }
3428
3906
  }),
3429
- command("teeter", "Rocks about the centre either way, settling.", { blend: BLEND("add"), angle: { type: "number", description: "Degrees either way (default 8)." }, count: { type: "integer", minimum: 1, description: "Rocks (default 3)." } }),
3430
- command("wave", "Bobs up and down without settling, like something floating (default 1000 ms).", { blend: BLEND("add"), amount: { type: "number", description: "Height in document units (default 6)." }, count: { type: "integer", minimum: 1, description: "Bobs (default 2)." } }),
3431
- command("wiggle", "Wanders about its place as smooth seeded noise, easing out of and back to rest (default 1500 ms). The same seed always wanders the same way.", {
3907
+ command2("teeter", "Rocks about the centre either way, settling.", { blend: BLEND("add"), angle: { type: "number", description: "Degrees either way (default 8)." }, count: { type: "integer", minimum: 1, description: "Rocks (default 3)." } }),
3908
+ command2("wave", "Bobs up and down without settling, like something floating (default 1000 ms).", { blend: BLEND("add"), amount: { type: "number", description: "Height in document units (default 6)." }, count: { type: "integer", minimum: 1, description: "Bobs (default 2)." } }),
3909
+ command2("wiggle", "Wanders about its place as smooth seeded noise, easing out of and back to rest (default 1500 ms). The same seed always wanders the same way.", {
3432
3910
  blend: BLEND("add"),
3433
3911
  amount: { type: "number", description: "Up to about this far, in document units (default 6)." },
3434
3912
  frequency: { type: "number", exclusiveMinimum: 0, description: "Wanders per second, in Hz (default 3)." },
@@ -3436,12 +3914,12 @@ var MOTION_COMMAND_SCHEMA = {
3436
3914
  rotate: { type: "number", description: "Also turns about the centre by up to about this many degrees (default 0)." },
3437
3915
  seed: { type: "integer", minimum: 0, description: "Default: one picked from the target id, so each target wanders its own way." }
3438
3916
  }),
3439
- command("color-pulse", "Tints solid paint toward a colour and back (mixed in OKLab). On a group it tints every path in it.", {
3917
+ command2("color-pulse", "Tints solid paint toward a colour and back (mixed in OKLab). On a group it tints every path in it.", {
3440
3918
  color: { type: "string", description: 'The tint, a CSS colour (default "#f59e0b").' },
3441
3919
  paint: { type: "string", enum: ["fill", "stroke"], description: "Which paint (default fill)." },
3442
3920
  count: { type: "integer", minimum: 1, description: "Pulses (default 1)." }
3443
3921
  }),
3444
- command("effect", "Tweens a numeric parameter of a live path effect on the targets (twist angle, wiggle time, zig zag size, trim offset\u2026).", {
3922
+ command2("effect", "Tweens a numeric parameter of a live path effect on the targets (twist angle, wiggle time, zig zag size, trim offset\u2026).", {
3445
3923
  effect: { type: "string", minLength: 1, description: `Effect id from get_animation's element "effects".` },
3446
3924
  param: { type: "string", minLength: 1, description: "A numeric parameter of that effect (listed with it)." },
3447
3925
  to: { type: "number", description: "Value at the end." }
@@ -3740,9 +4218,9 @@ function createAnimationEditorToolProvider(controller, options = {}) {
3740
4218
  handler: ({ from = 0, to, limit = 200 }) => {
3741
4219
  const s = scene();
3742
4220
  const timeline = compileMotionScene(s);
3743
- const words = _nullishCoalesce(_optionalChain([s, 'access', _126 => _126.narration, 'optionalAccess', _127 => _127.words]), () => ( []));
4221
+ const words = _nullishCoalesce(_optionalChain([s, 'access', _158 => _158.narration, 'optionalAccess', _159 => _159.words]), () => ( []));
3744
4222
  const end = _nullishCoalesce(to, () => ( Math.max(timeline.duration, words.length ? _nullishCoalesce(words[words.length - 1].end, () => ( words[words.length - 1].start)) : 0)));
3745
- const shotAt = (ms) => _optionalChain([timeline, 'access', _128 => _128.shots, 'access', _129 => _129.find, 'call', _130 => _130((x) => ms >= x.start && ms < x.end), 'optionalAccess', _131 => _131.id]);
4223
+ const shotAt = (ms) => _optionalChain([timeline, 'access', _160 => _160.shots, 'access', _161 => _161.find, 'call', _162 => _162((x) => ms >= x.start && ms < x.end), 'optionalAccess', _163 => _163.id]);
3746
4224
  const beats = [];
3747
4225
  if (s.beat && s.beat.bpm > 0) {
3748
4226
  const first = Math.max(0, Math.ceil(motionBeatAt(s.beat, from).beat - 1e-9));
@@ -3763,6 +4241,21 @@ function createAnimationEditorToolProvider(controller, options = {}) {
3763
4241
  };
3764
4242
  }
3765
4243
  };
4244
+ const getTextLayout = {
4245
+ name: "get_text_layout",
4246
+ title: "Read outlined text as letters, words and lines",
4247
+ description: 'Returns how a group of letter paths (text converted to outlines) reads: its lines, each a list of words, each a list of letter ids, in reading order \u2014 the units a command with "split": "letter" | "word" | "line" animates. Errors if the element is not a group with paths.',
4248
+ inputSchema: object({ group: { type: "string", minLength: 1, description: "Element id of the group of letters." } }, ["group"]),
4249
+ annotations: { title: "Read outlined text as letters, words and lines", ...READ },
4250
+ handler: ({ group }) => {
4251
+ const s = scene();
4252
+ const nodes = indexMotionNodes(s.artwork.nodes);
4253
+ if (!nodes.has(group)) throw new Error(`There is no element ${namedWithSuggestion(group, [...nodes.keys()])}.`);
4254
+ const layout = motionTextLayout(nodes, group);
4255
+ if (!layout) throw new Error(`"${group}" is not a group of paths; convert the text to outlines (one path a letter) and group it.`);
4256
+ return { group, letters: layout.lines.reduce((n, l) => n + l.words.reduce((m, w) => m + w.letters.length, 0), 0), ...layout };
4257
+ }
4258
+ };
3766
4259
  const getMotionPath = {
3767
4260
  name: "get_motion_path",
3768
4261
  title: "Read where a node travels",
@@ -3828,13 +4321,40 @@ function createAnimationEditorToolProvider(controller, options = {}) {
3828
4321
  handler: ({ svg }) => {
3829
4322
  let artwork;
3830
4323
  try {
3831
- artwork = _chunkYJLCBA7Vcjs.parseVectorSvg.call(void 0, svg);
4324
+ artwork = _chunk7MD7C6XQcjs.parseVectorSvg.call(void 0, svg);
3832
4325
  } catch (error) {
3833
4326
  throw new Error(`The SVG could not be read: ${error.message}. Nothing was changed.`);
3834
4327
  }
3835
4328
  return apply({ op: "set_artwork", artwork }, "Replace artwork", (s) => ({ elements: describeMotionElements(s) }));
3836
4329
  }
3837
4330
  };
4331
+ const importPptx = {
4332
+ name: "import_pptx_animation",
4333
+ title: "Import a PowerPoint slide's animation",
4334
+ description: "Replaces the whole animation with a PowerPoint slide (one undo step): its shapes become the artwork and its animation pane becomes shots, a shot per click (with previous / after previous, delays, repeats and acceleration kept; the scene plays on click). Text, pictures and charts are left out and effects with no match fade, with notes. Returns the shots, notes and issues. With dry_run it only reports them.",
4335
+ inputSchema: object(
4336
+ {
4337
+ data: { type: "string", minLength: 1, description: "The .pptx file, base64-encoded." },
4338
+ slide: { type: "integer", minimum: 1, description: "Which slide (1 = first, default 1)." },
4339
+ dry_run: { type: "boolean", description: "Report without replacing anything (default false)." }
4340
+ },
4341
+ ["data"]
4342
+ ),
4343
+ annotations: { title: "Import a PowerPoint slide's animation", ...DESTRUCTIVE },
4344
+ handler: async ({ data, slide, dry_run = false }) => {
4345
+ let result;
4346
+ try {
4347
+ const bytes = Uint8Array.from(atob(data.replace(/\s+/g, "")), (c) => c.charCodeAt(0));
4348
+ result = await fromPptxAnimation(bytes, slide ? { slide } : {});
4349
+ } catch (error) {
4350
+ throw new Error(`The slide could not be imported: ${error.message} Nothing was changed.`);
4351
+ }
4352
+ const report = { slides: result.slides, notes: result.notes, issues: issueSummary(result.issues), shots: describeMotionShots(result.scene).map(({ id, start, end, commands }) => ({ id, start, end, commands: commands.length })) };
4353
+ if (dry_run) return { dry_run: true, ...report, hint: "Nothing was replaced; call again without dry_run to import." };
4354
+ const applied = controller.apply({ op: "replace_scene", scene: result.scene }, "Import PowerPoint slide");
4355
+ return outcome(applied, applied.ok ? { ...report, elements: describeMotionElements(result.scene) } : {});
4356
+ }
4357
+ };
3838
4358
  const importFlairEffect = {
3839
4359
  name: "import_flair_effect",
3840
4360
  title: "Import a Flair effect",
@@ -4022,7 +4542,7 @@ function createAnimationEditorToolProvider(controller, options = {}) {
4022
4542
  inputSchema: object({ shot: SHOT_ID, commands: { type: "array", items: MOTION_COMMAND_SCHEMA, minItems: 1, description: "Commands to add." }, at: { type: "integer", minimum: 0, description: "Index in the run (default: the end)." } }, ["shot", "commands"]),
4023
4543
  annotations: { title: "Add commands to a shot", ...WRITE },
4024
4544
  handler: ({ shot, commands, at }) => {
4025
- const before = _nullishCoalesce(_optionalChain([scene, 'call', _132 => _132(), 'access', _133 => _133.shots, 'access', _134 => _134.find, 'call', _135 => _135((s) => s.id === shot), 'optionalAccess', _136 => _136.run, 'access', _137 => _137.length]), () => ( 0));
4545
+ const before = _nullishCoalesce(_optionalChain([scene, 'call', _164 => _164(), 'access', _165 => _165.shots, 'access', _166 => _166.find, 'call', _167 => _167((s) => s.id === shot), 'optionalAccess', _168 => _168.run, 'access', _169 => _169.length]), () => ( 0));
4026
4546
  const first = _nullishCoalesce(at, () => ( before));
4027
4547
  return apply({ op: "add_commands", shot, commands, at }, "Add commands", (s) => {
4028
4548
  const shots = describeMotionShots(s);
@@ -4045,7 +4565,7 @@ function createAnimationEditorToolProvider(controller, options = {}) {
4045
4565
  ["shot", "command"]
4046
4566
  ),
4047
4567
  annotations: { title: "Change a command", ...WRITE_IDEMPOTENT },
4048
- handler: ({ shot, command: command2, set, remove }) => apply({ op: "update_command", shot, command: command2, set, remove }, "Change command", (s) => ({ command: describeMotionShots(s).find((x) => x.id === shot).commands[command2] }))
4568
+ handler: ({ shot, command: command3, set, remove }) => apply({ op: "update_command", shot, command: command3, set, remove }, "Change command", (s) => ({ command: describeMotionShots(s).find((x) => x.id === shot).commands[command3] }))
4049
4569
  };
4050
4570
  const deleteCommands = {
4051
4571
  name: "delete_commands",
@@ -4068,7 +4588,7 @@ function createAnimationEditorToolProvider(controller, options = {}) {
4068
4588
  ["shot", "command", "to"]
4069
4589
  ),
4070
4590
  annotations: { title: "Reorder a command", ...WRITE },
4071
- handler: ({ shot, command: command2, to }) => apply({ op: "move_command", shot, command: command2, to }, "Move command", (s) => ({ shot, command: to, from: command2, commands: describeMotionShots(s).find((x) => x.id === shot).commands.map((c) => ({ index: c.index, type: c.type, start: c.start, end: c.end })) }))
4591
+ handler: ({ shot, command: command3, to }) => apply({ op: "move_command", shot, command: command3, to }, "Move command", (s) => ({ shot, command: to, from: command3, commands: describeMotionShots(s).find((x) => x.id === shot).commands.map((c) => ({ index: c.index, type: c.type, start: c.start, end: c.end })) }))
4072
4592
  };
4073
4593
  const setSettings = {
4074
4594
  name: "set_animation_settings",
@@ -4153,8 +4673,8 @@ function createAnimationEditorToolProvider(controller, options = {}) {
4153
4673
  return { redone, ...redone ? { label } : {} };
4154
4674
  }
4155
4675
  };
4156
- const reads = [getAnimation, getShot, findElements, getMotionPath, getTimingGrid, checkAnimation, renderFrame, exportFlairMotion, exportFlairEffect, exportLottie, seekAnimation];
4157
- const writes = [setArtworkSvg, setDrivers, setClip, copyAnimation, fitAnimation, importFlairEffect, insertShots, updateShot, deleteShots, moveShot, addCommands, updateCommand, deleteCommands, moveCommand, setSettings, setControls, editAnimation, writeAnimation, undo, redo];
4676
+ const reads = [getAnimation, getShot, findElements, getTextLayout, getMotionPath, getTimingGrid, checkAnimation, renderFrame, exportFlairMotion, exportFlairEffect, exportLottie, seekAnimation];
4677
+ const writes = [setArtworkSvg, importPptx, setDrivers, setClip, copyAnimation, fitAnimation, importFlairEffect, insertShots, updateShot, deleteShots, moveShot, addCommands, updateCommand, deleteCommands, moveCommand, setSettings, setControls, editAnimation, writeAnimation, undo, redo];
4158
4678
  return {
4159
4679
  id: _nullishCoalesce(options.id, () => ( "animation")),
4160
4680
  namespace: options.namespace,
@@ -4186,6 +4706,7 @@ Commands
4186
4706
  - enter (fade, pop, grow, slide, draw) and exit (fade, shrink, slide, undraw) bring things in and out.
4187
4707
  - enter also reveals through a clip over the target's box: wipe (behind an edge travelling "direction", default right), iris (from the centre; "shape": circle or box) and split (from a line through the middle; "orientation": horizontal or vertical). exit wipe hides the same way. Reveals need a width and a height: use draw / undraw for a straight line.
4188
4708
  - draw draws strokes on to a fraction; move, rotate, scale and fade change the pose (about the centre); style blends solid colours and stroke width.
4709
+ - Text: convert it to outlines (a group with a path per letter) and give a command "split": "letter", "word" or "line" (and "cascade") to animate it a unit at a time; get_text_layout shows the units.
4189
4710
  - Drawing a group of strokes: "order": "sequence" draws them one after another (a signature, a diagram), "sortBy": "distance" from "origin" reveals outward, "pace": "speed" keeps one speed; draw with "comet": 0.2 sends a short stroke along a path and off the end.
4190
4711
  - Fast motion reads better with blur: set_animation_settings { "motionBlur": { "shutter": 180 } } (stage and rendered frames only).
4191
4712
  - Tie properties together with set_animation_drivers: parallax layers, a wheel that turns as it rolls, a shadow that fades as something rises. Drivers add to the target's own commands.
@@ -4203,7 +4724,7 @@ Commands
4203
4724
  - effect tweens a numeric parameter of a live path effect already on the node (twist angle, wiggle time, zig zag size).
4204
4725
 
4205
4726
  Working method
4206
- 1. get_animation to learn the element ids (set_animation_artwork_svg first if there is no artwork: give every element you will animate an id; or import_flair_effect to start from a Flair effect and edit it).
4727
+ 1. get_animation to learn the element ids (set_animation_artwork_svg first if there is no artwork: give every element you will animate an id; or import_flair_effect / import_pptx_animation to start from a Flair effect or a PowerPoint slide).
4207
4728
  2. Plan a few shots, each one idea. Add them with insert_shots (add_commands, update_command, move_command and delete_commands refine a shot), or edit_animation to make several edits as one step (dry_run first when unsure).
4208
4729
  - In edit_animation, a later op can use what an earlier one made: "$0.indexes[0]" (the first command op 1 added), "$last.shot".
4209
4730
  3. To hit a length, fit_animation (keep the shots whose pace matters). check_animation and fix every error and warning; render_animation_frame (shot + "end") to look at the result, or with "times" for a contact sheet of several moments; add onion_skin or motion_paths to see the motion in one picture, or get_motion_path for the numbers.
@@ -4217,7 +4738,7 @@ var TIMING = /* @__PURE__ */ new Set(["duration", "delay", "stagger", "with", "e
4217
4738
  var COLORS = /* @__PURE__ */ new Set(["fill", "stroke", "color"]);
4218
4739
  var NODES = /* @__PURE__ */ new Set(["into", "path"]);
4219
4740
  function branch(type) {
4220
- return (_nullishCoalesce(MOTION_COMMAND_SCHEMA.oneOf, () => ( []))).find((b) => _optionalChain([b, 'access', _138 => _138.properties, 'optionalAccess', _139 => _139.type, 'optionalAccess', _140 => _140.const]) === type);
4741
+ return (_nullishCoalesce(MOTION_COMMAND_SCHEMA.oneOf, () => ( []))).find((b) => _optionalChain([b, 'access', _170 => _170.properties, 'optionalAccess', _171 => _171.type, 'optionalAccess', _172 => _172.const]) === type);
4221
4742
  }
4222
4743
  function unitOf(type, name) {
4223
4744
  if (name === "duration" || name === "delay" || name === "stagger") return "ms";
@@ -4252,7 +4773,7 @@ function kindOf(name, schema) {
4252
4773
  }
4253
4774
  function motionCommandFields(type) {
4254
4775
  const schema = branch(type);
4255
- if (!_optionalChain([schema, 'optionalAccess', _141 => _141.properties])) return [];
4776
+ if (!_optionalChain([schema, 'optionalAccess', _173 => _173.properties])) return [];
4256
4777
  const required = new Set(_nullishCoalesce(schema.required, () => ( [])));
4257
4778
  const fields = Object.entries(schema.properties).filter(([name]) => name !== "type").map(([name, s]) => {
4258
4779
  const kind = kindOf(name, s);
@@ -4299,17 +4820,17 @@ function parseMotionFieldInput(field, raw) {
4299
4820
  if (field.kind === "enum" && field.options && !field.options.includes(text)) return { error: `${field.label} must be one of ${field.options.join(", ")}.` };
4300
4821
  return { set: text };
4301
4822
  }
4302
- function motionFieldEdit(shot, command2, field, raw) {
4823
+ function motionFieldEdit(shot, command3, field, raw) {
4303
4824
  const input = parseMotionFieldInput(field, raw);
4304
4825
  if ("error" in input) return input;
4305
- if ("remove" in input) return { op: "update_command", shot, command: command2, remove: [field.name] };
4306
- return { op: "update_command", shot, command: command2, set: { [field.name]: input.set } };
4826
+ if ("remove" in input) return { op: "update_command", shot, command: command3, remove: [field.name] };
4827
+ return { op: "update_command", shot, command: command3, set: { [field.name]: input.set } };
4307
4828
  }
4308
4829
  function defaultMotionCommand(type, targets, scene) {
4309
4830
  if (type === "wait") return { type: "wait", duration: 500 };
4310
4831
  if (!targets.length) return { error: "Pick a target first: click an element on the stage." };
4311
4832
  const nodes = [...indexMotionNodes(scene.artwork.nodes).values()];
4312
- const other = (pred) => _optionalChain([nodes, 'access', _142 => _142.find, 'call', _143 => _143((e) => !targets.includes(e.node.id) && pred(e)), 'optionalAccess', _144 => _144.node, 'access', _145 => _145.id]);
4833
+ const other = (pred) => _optionalChain([nodes, 'access', _174 => _174.find, 'call', _175 => _175((e) => !targets.includes(e.node.id) && pred(e)), 'optionalAccess', _176 => _176.node, 'access', _177 => _177.id]);
4313
4834
  const base = { targets: [...targets] };
4314
4835
  switch (type) {
4315
4836
  case "scale":
@@ -4340,8 +4861,8 @@ function defaultMotionCommand(type, targets, scene) {
4340
4861
  }
4341
4862
  case "effect": {
4342
4863
  for (const id of targets) {
4343
- const node = _optionalChain([nodes, 'access', _146 => _146.find, 'call', _147 => _147((e) => e.node.id === id), 'optionalAccess', _148 => _148.node]);
4344
- for (const effect of _nullishCoalesce(_optionalChain([node, 'optionalAccess', _149 => _149.effects]), () => ( []))) {
4864
+ const node = _optionalChain([nodes, 'access', _178 => _178.find, 'call', _179 => _179((e) => e.node.id === id), 'optionalAccess', _180 => _180.node]);
4865
+ for (const effect of _nullishCoalesce(_optionalChain([node, 'optionalAccess', _181 => _181.effects]), () => ( []))) {
4345
4866
  const param = Object.entries(effect.params).find(([, v]) => typeof v === "number");
4346
4867
  if (param) return { type, ...base, effect: effect.id, param: param[0], to: param[1] + 1 };
4347
4868
  }
@@ -4352,26 +4873,26 @@ function defaultMotionCommand(type, targets, scene) {
4352
4873
  return { type, ...base };
4353
4874
  }
4354
4875
  }
4355
- function motionCommandSummary(command2) {
4356
- const c = command2;
4357
- const what = [command2.type, typeof c.style === "string" ? c.style : ""].filter(Boolean).join(" ");
4358
- if (command2.type === "wait") return `wait ${command2.duration} ms`;
4876
+ function motionCommandSummary(command3) {
4877
+ const c = command3;
4878
+ const what = [command3.type, typeof c.style === "string" ? c.style : ""].filter(Boolean).join(" ");
4879
+ if (command3.type === "wait") return `wait ${command3.duration} ms`;
4359
4880
  const extra = typeof c.into === "string" ? ` \u2192 ${c.into}` : typeof c.path === "string" ? ` along ${c.path}` : "";
4360
- return `${what} \xB7 ${(_nullishCoalesce(command2.targets, () => ( []))).join(", ")}${extra}`;
4881
+ return `${what} \xB7 ${(_nullishCoalesce(command3.targets, () => ( []))).join(", ")}${extra}`;
4361
4882
  }
4362
4883
  function motionCommandRows(scene, shotId) {
4363
4884
  const index = scene.shots.findIndex((s) => s.id === shotId);
4364
4885
  if (index < 0) return [];
4365
4886
  const timeline = compileMotionScene(scene);
4366
- return scene.shots[index].run.map((command2, i) => {
4887
+ return scene.shots[index].run.map((command3, i) => {
4367
4888
  const t = timeline.commands.find((c) => c.shotIndex === index && c.command === i);
4368
- const primitive = motionFlairPrimitive(command2);
4369
- return { index: i, command: command2, summary: motionCommandSummary(command2), start: _nullishCoalesce(_optionalChain([t, 'optionalAccess', _150 => _150.start]), () => ( 0)), end: _nullishCoalesce(_optionalChain([t, 'optionalAccess', _151 => _151.end]), () => ( 0)), ...primitive ? { flairPrimitive: primitive } : {} };
4889
+ const primitive = motionFlairPrimitive(command3);
4890
+ return { index: i, command: command3, summary: motionCommandSummary(command3), start: _nullishCoalesce(_optionalChain([t, 'optionalAccess', _182 => _182.start]), () => ( 0)), end: _nullishCoalesce(_optionalChain([t, 'optionalAccess', _183 => _183.end]), () => ( 0)), ...primitive ? { flairPrimitive: primitive } : {} };
4370
4891
  });
4371
4892
  }
4372
- function duplicateMotionCommandEdit(scene, shot, command2) {
4373
- const run = _nullishCoalesce(_optionalChain([scene, 'access', _152 => _152.shots, 'access', _153 => _153.find, 'call', _154 => _154((s) => s.id === shot), 'optionalAccess', _155 => _155.run]), () => ( []));
4374
- return { op: "add_commands", shot, commands: run[command2] ? [structuredClone(run[command2])] : [], at: command2 + 1 };
4893
+ function duplicateMotionCommandEdit(scene, shot, command3) {
4894
+ const run = _nullishCoalesce(_optionalChain([scene, 'access', _184 => _184.shots, 'access', _185 => _185.find, 'call', _186 => _186((s) => s.id === shot), 'optionalAccess', _187 => _187.run]), () => ( []));
4895
+ return { op: "add_commands", shot, commands: run[command3] ? [structuredClone(run[command3])] : [], at: command3 + 1 };
4375
4896
  }
4376
4897
  var MOTION_EASE_CHOICES = [
4377
4898
  { value: "", label: "Default" },
@@ -4455,7 +4976,7 @@ function motionCueFromKey(key) {
4455
4976
  function motionCueChoices(scene, limit = 256) {
4456
4977
  const choices = [];
4457
4978
  const s = (ms) => `${(ms / 1e3).toFixed(2)} s`;
4458
- const end = Math.max(compileMotionScene(scene).duration, ..._nullishCoalesce(_optionalChain([scene, 'access', _156 => _156.narration, 'optionalAccess', _157 => _157.words, 'access', _158 => _158.map, 'call', _159 => _159((w) => _nullishCoalesce(w.end, () => ( w.start)))]), () => ( [0]))) + 2e3;
4979
+ const end = Math.max(compileMotionScene(scene).duration, ..._nullishCoalesce(_optionalChain([scene, 'access', _188 => _188.narration, 'optionalAccess', _189 => _189.words, 'access', _190 => _190.map, 'call', _191 => _191((w) => _nullishCoalesce(w.end, () => ( w.start)))]), () => ( [0]))) + 2e3;
4459
4980
  if (scene.beat && scene.beat.bpm > 0) {
4460
4981
  for (let beat = 0; choices.length < limit; beat++) {
4461
4982
  const time = motionBeatTime(scene.beat, beat);
@@ -4465,7 +4986,7 @@ function motionCueChoices(scene, limit = 256) {
4465
4986
  choices.push({ value: `beat:${beat}`, label: `Beat ${beat} (bar ${at.bar}.${at.beatInBar}) \xB7 ${s(time)}`, time });
4466
4987
  }
4467
4988
  }
4468
- _optionalChain([scene, 'access', _160 => _160.narration, 'optionalAccess', _161 => _161.words, 'access', _162 => _162.forEach, 'call', _163 => _163((w, i) => {
4989
+ _optionalChain([scene, 'access', _192 => _192.narration, 'optionalAccess', _193 => _193.words, 'access', _194 => _194.forEach, 'call', _195 => _195((w, i) => {
4469
4990
  if (choices.length < limit * 2) choices.push({ value: `word:${i}`, label: `Word ${i} \u201C${w.text}\u201D \xB7 ${s(w.start)}`, time: w.start });
4470
4991
  })]);
4471
4992
  return choices;
@@ -4474,7 +4995,7 @@ function motionShotCueTime(scene, cue) {
4474
4995
  return cue ? motionCueTime(scene, cue) : void 0;
4475
4996
  }
4476
4997
  function formatMotionNarration(narration) {
4477
- return (_nullishCoalesce(_optionalChain([narration, 'optionalAccess', _164 => _164.words]), () => ( []))).map((w) => `${w.start} ${w.text}`).join("\n");
4998
+ return (_nullishCoalesce(_optionalChain([narration, 'optionalAccess', _196 => _196.words]), () => ( []))).map((w) => `${w.start} ${w.text}`).join("\n");
4478
4999
  }
4479
5000
  function parseMotionNarration(text) {
4480
5001
  const lines = text.split("\n").map((l) => l.trim()).filter(Boolean);
@@ -4646,5 +5167,13 @@ function parseMotionDrivers(text) {
4646
5167
 
4647
5168
 
4648
5169
 
4649
- exports.ANIMATION_EDITOR_AGENT_INSTRUCTIONS = ANIMATION_EDITOR_AGENT_INSTRUCTIONS; exports.AnimationEditorController = AnimationEditorController; exports.FLAIR_EASES = FLAIR_EASES; exports.FLAIR_EXPORT_VERSION = FLAIR_EXPORT_VERSION; exports.MOTION_ADDITIVE_PROPERTIES = MOTION_ADDITIVE_PROPERTIES; exports.MOTION_BLENDS = MOTION_BLENDS; exports.MOTION_CAMERA_MS = MOTION_CAMERA_MS; exports.MOTION_CLIP_TARGET = MOTION_CLIP_TARGET; exports.MOTION_COMMAND_SCHEMA = MOTION_COMMAND_SCHEMA; exports.MOTION_COMMAND_TYPES = MOTION_COMMAND_TYPES; exports.MOTION_DEFAULT_DURATIONS = MOTION_DEFAULT_DURATIONS; exports.MOTION_DEFAULT_HOLD = MOTION_DEFAULT_HOLD; exports.MOTION_DIRECTIONS = MOTION_DIRECTIONS; exports.MOTION_DRAW_EFFECT_ID = MOTION_DRAW_EFFECT_ID; exports.MOTION_DRAW_MS_PER_UNIT = MOTION_DRAW_MS_PER_UNIT; exports.MOTION_DRAW_RANGE = MOTION_DRAW_RANGE; exports.MOTION_DRIVEN_PROPERTIES = MOTION_DRIVEN_PROPERTIES; exports.MOTION_EASE_CHOICES = MOTION_EASE_CHOICES; exports.MOTION_EASE_IN_OUT = MOTION_EASE_IN_OUT; exports.MOTION_EASE_OUT = MOTION_EASE_OUT; exports.MOTION_EDIT_OPS = MOTION_EDIT_OPS; exports.MOTION_ENTER_STYLES = MOTION_ENTER_STYLES; exports.MOTION_EXIT_STYLES = MOTION_EXIT_STYLES; exports.MOTION_IRIS_SHAPES = MOTION_IRIS_SHAPES; exports.MOTION_ISSUE_CODES = MOTION_ISSUE_CODES; exports.MOTION_MIN_CHANGE_MS = MOTION_MIN_CHANGE_MS; exports.MOTION_REPEAT_MODES = MOTION_REPEAT_MODES; exports.MOTION_SPEED_RANGE = MOTION_SPEED_RANGE; exports.MOTION_SPLIT_ORIENTATIONS = MOTION_SPLIT_ORIENTATIONS; exports.MOTION_TRIGGERS = MOTION_TRIGGERS; exports.MotionSceneFormatError = MotionSceneFormatError; exports.activeSegment = activeSegment; exports.applyMotionControlValues = applyMotionControlValues; exports.applyMotionDrivers = applyMotionDrivers; exports.applyMotionEdits = applyMotionEdits; exports.checkMotionScene = checkMotionScene; exports.clipsShapeProblems = clipsShapeProblems; exports.commandShapeProblems = commandShapeProblems; exports.compileMotionScene = compileMotionScene; exports.createAnimationEditorToolProvider = createAnimationEditorToolProvider; exports.createMotionScene = createMotionScene; exports.defaultMotionCommand = defaultMotionCommand; exports.describeMotionElements = describeMotionElements; exports.describeMotionShots = describeMotionShots; exports.driversShapeProblems = driversShapeProblems; exports.duplicateMotionCommandEdit = duplicateMotionCommandEdit; exports.fitMotionRect = fitMotionRect; exports.fitMotionScene = fitMotionScene; exports.fitMotionSceneEdits = fitMotionSceneEdits; exports.flairAtLeast = flairAtLeast; exports.flairCurveFor = flairCurveFor; exports.flairEaseFor = flairEaseFor; exports.formatMotionDrivers = formatMotionDrivers; exports.formatMotionNarration = formatMotionNarration; exports.freshShotId = freshShotId; exports.fromFlairSvgMotion = fromFlairSvgMotion; exports.fromFlairVectorEffect = fromFlairVectorEffect; exports.highlightMotionSvg = highlightMotionSvg; exports.indexMotionNodes = indexMotionNodes; exports.interpolateMotionValue = interpolateMotionValue; exports.isMotionCommand = isMotionCommand; exports.isMotionShot = isMotionShot; exports.matchMotionNodes = matchMotionNodes; exports.motionAddTrackKey = motionAddTrackKey; exports.motionBeatAt = motionBeatAt; exports.motionBeatTime = motionBeatTime; exports.motionBlurTimes = motionBlurTimes; exports.motionClipAttrs = motionClipAttrs; exports.motionClipLayout = motionClipLayout; exports.motionCommandDuration = motionCommandDuration; exports.motionCommandFields = motionCommandFields; exports.motionCommandRepeat = motionCommandRepeat; exports.motionCommandRows = motionCommandRows; exports.motionCommandSummary = motionCommandSummary; exports.motionContactSheetSvg = motionContactSheetSvg; exports.motionCueChoices = motionCueChoices; exports.motionCueFromKey = motionCueFromKey; exports.motionCueKey = motionCueKey; exports.motionCueTime = motionCueTime; exports.motionCycleDuration = motionCycleDuration; exports.motionDrivenNodes = motionDrivenNodes; exports.motionDriverSources = motionDriverSources; exports.motionEaseKey = motionEaseKey; exports.motionEasingCurve = motionEasingCurve; exports.motionEasingGuides = motionEasingGuides; exports.motionEasingPath = motionEasingPath; exports.motionFieldEdit = motionFieldEdit; exports.motionFieldLabel = motionFieldLabel; exports.motionFlairPrimitive = motionFlairPrimitive; exports.motionFollowRoute = motionFollowRoute; exports.motionFrameAt = motionFrameAt; exports.motionFrameToSvg = motionFrameToSvg; exports.motionGhosts = motionGhosts; exports.motionIssue = motionIssue; exports.motionIssueKey = motionIssueKey; exports.motionIssueLine = motionIssueLine; exports.motionMovingIds = motionMovingIds; exports.motionNodeAtElement = motionNodeAtElement; exports.motionNodeClips = motionNodeClips; exports.motionNodeOffset = motionNodeOffset; exports.motionPath = motionPath; exports.motionPathLength = motionPathLength; exports.motionPathsSvg = motionPathsSvg; exports.motionPropertyValue = motionPropertyValue; exports.motionRestValue = motionRestValue; exports.motionRevealClip = motionRevealClip; exports.motionRevealPadding = motionRevealPadding; exports.motionRulerTicks = motionRulerTicks; exports.motionSceneShapeProblems = motionSceneShapeProblems; exports.motionSceneToJson = motionSceneToJson; exports.motionShotAt = motionShotAt; exports.motionShotCueTime = motionShotCueTime; exports.motionShotTimeScale = motionShotTimeScale; exports.motionStageSvg = motionStageSvg; exports.motionTrackKey = motionTrackKey; exports.namedWithSuggestion = namedWithSuggestion; exports.parseMotionDrivers = parseMotionDrivers; exports.parseMotionFieldInput = parseMotionFieldInput; exports.parseMotionNarration = parseMotionNarration; exports.parseMotionScene = parseMotionScene; exports.resolveMotionCamera = resolveMotionCamera; exports.resolveMotionEditRefs = resolveMotionEditRefs; exports.segmentProgress = segmentProgress; exports.segmentValue = segmentValue; exports.shotShapeProblems = shotShapeProblems; exports.smoothMotionLegs = smoothMotionLegs; exports.smoothMotionTimes = smoothMotionTimes; exports.suggestName = suggestName; exports.toFlairEffectDocument = toFlairEffectDocument; exports.toFlairSvgMotion = toFlairSvgMotion; exports.toFlairVectorEffect = toFlairVectorEffect; exports.toLottie = toLottie; exports.trackValue = trackValue; exports.withMotionLayers = withMotionLayers; exports.withViewBox = withViewBox;
5170
+
5171
+
5172
+
5173
+
5174
+
5175
+
5176
+
5177
+
5178
+ exports.ANIMATION_EDITOR_AGENT_INSTRUCTIONS = ANIMATION_EDITOR_AGENT_INSTRUCTIONS; exports.AnimationEditorController = AnimationEditorController; exports.FLAIR_EASES = FLAIR_EASES; exports.FLAIR_EXPORT_VERSION = FLAIR_EXPORT_VERSION; exports.MOTION_ADDITIVE_PROPERTIES = MOTION_ADDITIVE_PROPERTIES; exports.MOTION_BLENDS = MOTION_BLENDS; exports.MOTION_CAMERA_MS = MOTION_CAMERA_MS; exports.MOTION_CASCADES = MOTION_CASCADES; exports.MOTION_CLIP_TARGET = MOTION_CLIP_TARGET; exports.MOTION_COMMAND_SCHEMA = MOTION_COMMAND_SCHEMA; exports.MOTION_COMMAND_TYPES = MOTION_COMMAND_TYPES; exports.MOTION_DEFAULT_DURATIONS = MOTION_DEFAULT_DURATIONS; exports.MOTION_DEFAULT_HOLD = MOTION_DEFAULT_HOLD; exports.MOTION_DIRECTIONS = MOTION_DIRECTIONS; exports.MOTION_DRAW_EFFECT_ID = MOTION_DRAW_EFFECT_ID; exports.MOTION_DRAW_MS_PER_UNIT = MOTION_DRAW_MS_PER_UNIT; exports.MOTION_DRAW_RANGE = MOTION_DRAW_RANGE; exports.MOTION_DRIVEN_PROPERTIES = MOTION_DRIVEN_PROPERTIES; exports.MOTION_EASE_CHOICES = MOTION_EASE_CHOICES; exports.MOTION_EASE_IN_OUT = MOTION_EASE_IN_OUT; exports.MOTION_EASE_OUT = MOTION_EASE_OUT; exports.MOTION_EDIT_OPS = MOTION_EDIT_OPS; exports.MOTION_ENTER_STYLES = MOTION_ENTER_STYLES; exports.MOTION_EXIT_STYLES = MOTION_EXIT_STYLES; exports.MOTION_IRIS_SHAPES = MOTION_IRIS_SHAPES; exports.MOTION_ISSUE_CODES = MOTION_ISSUE_CODES; exports.MOTION_MIN_CHANGE_MS = MOTION_MIN_CHANGE_MS; exports.MOTION_REPEAT_MODES = MOTION_REPEAT_MODES; exports.MOTION_SPEED_RANGE = MOTION_SPEED_RANGE; exports.MOTION_SPLIT_ORIENTATIONS = MOTION_SPLIT_ORIENTATIONS; exports.MOTION_TEXT_STAGGER = MOTION_TEXT_STAGGER; exports.MOTION_TEXT_UNITS = MOTION_TEXT_UNITS; exports.MOTION_TRIGGERS = MOTION_TRIGGERS; exports.MotionSceneFormatError = MotionSceneFormatError; exports.activeSegment = activeSegment; exports.applyMotionControlValues = applyMotionControlValues; exports.applyMotionDrivers = applyMotionDrivers; exports.applyMotionEdits = applyMotionEdits; exports.checkMotionScene = checkMotionScene; exports.clipsShapeProblems = clipsShapeProblems; exports.commandShapeProblems = commandShapeProblems; exports.compileMotionScene = compileMotionScene; exports.createAnimationEditorToolProvider = createAnimationEditorToolProvider; exports.createMotionScene = createMotionScene; exports.defaultMotionCommand = defaultMotionCommand; exports.describeMotionElements = describeMotionElements; exports.describeMotionShots = describeMotionShots; exports.driversShapeProblems = driversShapeProblems; exports.duplicateMotionCommandEdit = duplicateMotionCommandEdit; exports.fitMotionRect = fitMotionRect; exports.fitMotionScene = fitMotionScene; exports.fitMotionSceneEdits = fitMotionSceneEdits; exports.flairAtLeast = flairAtLeast; exports.flairCurveFor = flairCurveFor; exports.flairEaseFor = flairEaseFor; exports.formatMotionDrivers = formatMotionDrivers; exports.formatMotionNarration = formatMotionNarration; exports.freshShotId = freshShotId; exports.fromFlairSvgMotion = fromFlairSvgMotion; exports.fromFlairVectorEffect = fromFlairVectorEffect; exports.fromPptxAnimation = fromPptxAnimation; exports.highlightMotionSvg = highlightMotionSvg; exports.indexMotionNodes = indexMotionNodes; exports.interpolateMotionValue = interpolateMotionValue; exports.isMotionCommand = isMotionCommand; exports.isMotionShot = isMotionShot; exports.matchMotionNodes = matchMotionNodes; exports.motionAddTrackKey = motionAddTrackKey; exports.motionBeatAt = motionBeatAt; exports.motionBeatTime = motionBeatTime; exports.motionBlurTimes = motionBlurTimes; exports.motionCascade = motionCascade; exports.motionClipAttrs = motionClipAttrs; exports.motionClipLayout = motionClipLayout; exports.motionCommandDuration = motionCommandDuration; exports.motionCommandFields = motionCommandFields; exports.motionCommandRepeat = motionCommandRepeat; exports.motionCommandRows = motionCommandRows; exports.motionCommandSlots = motionCommandSlots; exports.motionCommandSummary = motionCommandSummary; exports.motionContactSheetSvg = motionContactSheetSvg; exports.motionCueChoices = motionCueChoices; exports.motionCueFromKey = motionCueFromKey; exports.motionCueKey = motionCueKey; exports.motionCueTime = motionCueTime; exports.motionCycleDuration = motionCycleDuration; exports.motionDrivenNodes = motionDrivenNodes; exports.motionDriverSources = motionDriverSources; exports.motionEaseKey = motionEaseKey; exports.motionEasingCurve = motionEasingCurve; exports.motionEasingGuides = motionEasingGuides; exports.motionEasingPath = motionEasingPath; exports.motionFieldEdit = motionFieldEdit; exports.motionFieldLabel = motionFieldLabel; exports.motionFlairPrimitive = motionFlairPrimitive; exports.motionFollowRoute = motionFollowRoute; exports.motionFrameAt = motionFrameAt; exports.motionFrameToSvg = motionFrameToSvg; exports.motionGhosts = motionGhosts; exports.motionIssue = motionIssue; exports.motionIssueKey = motionIssueKey; exports.motionIssueLine = motionIssueLine; exports.motionMovingIds = motionMovingIds; exports.motionNodeAtElement = motionNodeAtElement; exports.motionNodeClips = motionNodeClips; exports.motionNodeOffset = motionNodeOffset; exports.motionPath = motionPath; exports.motionPathLength = motionPathLength; exports.motionPathsSvg = motionPathsSvg; exports.motionPropertyValue = motionPropertyValue; exports.motionRestValue = motionRestValue; exports.motionRevealClip = motionRevealClip; exports.motionRevealPadding = motionRevealPadding; exports.motionRulerTicks = motionRulerTicks; exports.motionSceneShapeProblems = motionSceneShapeProblems; exports.motionSceneToJson = motionSceneToJson; exports.motionShotAt = motionShotAt; exports.motionShotCueTime = motionShotCueTime; exports.motionShotTimeScale = motionShotTimeScale; exports.motionStageSvg = motionStageSvg; exports.motionTextLayout = motionTextLayout; exports.motionTextUnits = motionTextUnits; exports.motionTrackKey = motionTrackKey; exports.namedWithSuggestion = namedWithSuggestion; exports.parseMotionDrivers = parseMotionDrivers; exports.parseMotionFieldInput = parseMotionFieldInput; exports.parseMotionNarration = parseMotionNarration; exports.parseMotionScene = parseMotionScene; exports.resolveMotionCamera = resolveMotionCamera; exports.resolveMotionEditRefs = resolveMotionEditRefs; exports.segmentProgress = segmentProgress; exports.segmentValue = segmentValue; exports.shotShapeProblems = shotShapeProblems; exports.smoothMotionLegs = smoothMotionLegs; exports.smoothMotionTimes = smoothMotionTimes; exports.suggestName = suggestName; exports.toFlairEffectDocument = toFlairEffectDocument; exports.toFlairSvgMotion = toFlairSvgMotion; exports.toFlairVectorEffect = toFlairVectorEffect; exports.toLottie = toLottie; exports.trackValue = trackValue; exports.withMotionLayers = withMotionLayers; exports.withViewBox = withViewBox;
4650
5179
  //# sourceMappingURL=index.cjs.map