@pixerate/editor 0.57.0 → 0.59.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 (53) hide show
  1. package/dist/animation/index.cjs +23 -3
  2. package/dist/animation/index.cjs.map +1 -1
  3. package/dist/animation/index.d.cts +3 -2
  4. package/dist/animation/index.d.ts +3 -2
  5. package/dist/animation/index.js +23 -3
  6. package/dist/animation-editor/index.cjs +836 -158
  7. package/dist/animation-editor/index.cjs.map +1 -1
  8. package/dist/animation-editor/index.d.cts +454 -40
  9. package/dist/animation-editor/index.d.ts +454 -40
  10. package/dist/animation-editor/index.js +790 -112
  11. package/dist/animation-editor/index.js.map +1 -1
  12. package/dist/behavior-Ymbld6jL.d.cts +60 -0
  13. package/dist/behavior-Ymbld6jL.d.ts +60 -0
  14. package/dist/canvas/index.cjs +35 -29
  15. package/dist/canvas/index.cjs.map +1 -1
  16. package/dist/canvas/index.d.cts +4 -4
  17. package/dist/canvas/index.d.ts +4 -4
  18. package/dist/canvas/index.js +9 -3
  19. package/dist/canvas/index.js.map +1 -1
  20. package/dist/{chunk-O4ONBPZW.cjs → chunk-2FGRTYJN.cjs} +4 -4
  21. package/dist/{chunk-O4ONBPZW.cjs.map → chunk-2FGRTYJN.cjs.map} +1 -1
  22. package/dist/{chunk-XZ4L3R4K.cjs → chunk-2FHLHZAL.cjs} +39 -3
  23. package/dist/chunk-2FHLHZAL.cjs.map +1 -0
  24. package/dist/chunk-3NYXDBBW.js +122 -0
  25. package/dist/chunk-3NYXDBBW.js.map +1 -0
  26. package/dist/{chunk-NONDTTH7.js → chunk-3TLL5OID.js} +17 -2
  27. package/dist/{chunk-NONDTTH7.js.map → chunk-3TLL5OID.js.map} +1 -1
  28. package/dist/{chunk-UB2OY7FD.js → chunk-AGR4P4UZ.js} +38 -2
  29. package/dist/chunk-AGR4P4UZ.js.map +1 -0
  30. package/dist/{chunk-SHOVAHUB.js → chunk-JK6M6V7G.js} +2 -2
  31. package/dist/{chunk-XCXQDPID.cjs → chunk-YJLCBA7V.cjs} +25 -10
  32. package/dist/chunk-YJLCBA7V.cjs.map +1 -0
  33. package/dist/chunk-ZCNLBRT6.cjs +122 -0
  34. package/dist/chunk-ZCNLBRT6.cjs.map +1 -0
  35. package/dist/{easing-CNGk-W6d.d.ts → easing-lM0Xo9Ni.d.cts} +14 -2
  36. package/dist/{easing-CNGk-W6d.d.cts → easing-lM0Xo9Ni.d.ts} +14 -2
  37. package/dist/parameters/index.cjs +2 -2
  38. package/dist/parameters/index.d.cts +1 -1
  39. package/dist/parameters/index.d.ts +1 -1
  40. package/dist/parameters/index.js +1 -1
  41. package/dist/{player-W-5DX3Go.d.ts → player-DV4Jujdq.d.ts} +1 -1
  42. package/dist/{player-Dg0FT0UI.d.cts → player-DrcP2xqj.d.cts} +1 -1
  43. package/dist/{svg-CPK82TtU.d.ts → svg-Dh17jCyA.d.ts} +22 -1
  44. package/dist/{svg-CLg2zqZA.d.cts → svg-ymShkNtu.d.cts} +22 -1
  45. package/dist/vector/index.cjs +472 -472
  46. package/dist/vector/index.d.cts +3 -3
  47. package/dist/vector/index.d.ts +3 -3
  48. package/dist/vector/index.js +2 -2
  49. package/package.json +1 -1
  50. package/dist/chunk-UB2OY7FD.js.map +0 -1
  51. package/dist/chunk-XCXQDPID.cjs.map +0 -1
  52. package/dist/chunk-XZ4L3R4K.cjs.map +0 -1
  53. /package/dist/{chunk-SHOVAHUB.js.map → chunk-JK6M6V7G.js.map} +0 -0
@@ -1,7 +1,8 @@
1
- import { b as AnimationEasing } from '../easing-CNGk-W6d.js';
2
- import { b as AnimationLoopMode, c as AnimationPlayer, C as Clock } from '../player-W-5DX3Go.js';
3
- import { V as VectorDocument, a as VectorNode, b as VectorBounds, c as VectorSvgExportOptions } from '../svg-CPK82TtU.js';
4
- import { A as AffineMatrix } from '../bezier-DYBvLgUH.js';
1
+ import { b as AnimationEasing } from '../easing-lM0Xo9Ni.js';
2
+ import { b as AnimationLoopMode, c as AnimationPlayer, C as Clock } from '../player-DV4Jujdq.js';
3
+ import { V as VectorDocument, a as VectorNode, b as VectorBounds, c as VectorSvgExportOptions } from '../svg-Dh17jCyA.js';
4
+ import { a as MotionBehavior } from '../behavior-Ymbld6jL.js';
5
+ import { A as AffineMatrix, B as BezierPath } from '../bezier-DYBvLgUH.js';
5
6
  import { H as HistoryManager } from '../HistoryManager-DWSLd6Tf.js';
6
7
  import { A as AgentToolProvider } from '../registry-7AZFuand.js';
7
8
  import { J as JsonSchema } from '../schema-Di0CS6Pe.js';
@@ -30,6 +31,11 @@ interface MotionScene {
30
31
  loop?: AnimationLoopMode;
31
32
  /** Frame rate used to sample motion that a target format cannot express exactly (default 30). */
32
33
  fps?: number;
34
+ /**
35
+ * Stepped time for every command that gives none: changes hold on a grid of this many frames
36
+ * per second ("on twos" at 12), for a hand-drawn look. Default: smooth.
37
+ */
38
+ steps?: number;
33
39
  /** Wanted length in seconds `[min, max]`; `check` warns outside it. */
34
40
  target?: [number, number];
35
41
  /** Parameters a Flair effect made from this scene exposes (see `toFlairVectorEffect`). */
@@ -54,6 +60,8 @@ type MotionCamera = (MotionRect | {
54
60
  }) & {
55
61
  ease?: AnimationEasing;
56
62
  ms?: number;
63
+ /** Zoom at a steady rate (the size changes by the same factor each moment) instead of linearly. */
64
+ geometric?: boolean;
57
65
  };
58
66
  /** One beat of the animation. */
59
67
  interface MotionShot {
@@ -72,7 +80,7 @@ interface MotionShot {
72
80
  interface MotionCommandBase {
73
81
  /** Node ids from the artwork (groups animate as one). */
74
82
  targets: string[];
75
- /** Length in ms for each target (each type has a default; `draw` follows the path length). */
83
+ /** Length in ms for each target, one cycle when repeating (each type has a default; `draw` follows the path length; a spring `ease` lasts until it settles). */
76
84
  duration?: number;
77
85
  /** Wait this many ms before starting (default 0). */
78
86
  delay?: number;
@@ -80,28 +88,64 @@ interface MotionCommandBase {
80
88
  stagger?: number;
81
89
  /** `after` (default) starts when the commands before it finish; `previous` starts with the command before. */
82
90
  with?: 'after' | 'previous';
91
+ /** An easing; a spring with no `duration` lasts until it settles. */
83
92
  ease?: AnimationEasing;
84
93
  /** Why this command is here (for people and agents reading the scene). */
85
94
  note?: string;
95
+ /**
96
+ * Play the change this many times (a whole number, default 1); `duration` is one cycle. Not
97
+ * for `enter`, `exit` or `wait`.
98
+ */
99
+ repeat?: number;
100
+ /**
101
+ * How repeats follow each other: `loop` (default) starts each cycle afresh, `mirror` plays
102
+ * every other one backwards, `offset` carries on from where the last ended (rotate by 90,
103
+ * repeat 4: a full turn). Colours and shapes offset like `loop`.
104
+ */
105
+ repeatMode?: 'loop' | 'mirror' | 'offset';
106
+ /** Stepped time: hold the change on a grid of this many frames per second (default: the scene's `steps`, else smooth). */
107
+ steps?: number;
86
108
  }
87
- type MotionEnterStyle = 'fade' | 'pop' | 'grow' | 'slide' | 'draw';
88
- type MotionExitStyle = 'fade' | 'shrink' | 'slide' | 'undraw';
109
+ type MotionEnterStyle = 'fade' | 'pop' | 'grow' | 'slide' | 'draw' | 'zoom' | 'rise' | 'wipe' | 'iris' | 'split';
110
+ type MotionExitStyle = 'fade' | 'shrink' | 'slide' | 'undraw' | 'wipe';
89
111
  type MotionDirection = 'up' | 'down' | 'left' | 'right';
90
- /** Brings hidden targets in. Targets entered in a shot are hidden before it. */
112
+ /** The opening an `iris` reveals through. */
113
+ type MotionIrisShape = 'circle' | 'box';
114
+ /** The line a `split` opens from. */
115
+ type MotionSplitOrientation = 'horizontal' | 'vertical';
116
+ /**
117
+ * Brings hidden targets in. Targets entered in a shot are hidden before it. Each style is a
118
+ * Flair motion primitive (`entrance.<style>`) with the same defaults.
119
+ */
91
120
  interface MotionEnterCommand extends MotionCommandBase {
92
121
  type: 'enter';
93
- /** Default `fade`. `draw` draws the outline on, then fades the fill in. */
122
+ /**
123
+ * Default `fade`. `draw` draws the outline on, then fades the fill in. `pop` scales up past
124
+ * full size and settles; `zoom` settles from larger; `rise` rises a short way with a little
125
+ * overshoot. `wipe`, `iris` and `split` reveal the target through a clip over its box (see
126
+ * `MotionFrame.clips`), reaching past it far enough to show its whole stroke.
127
+ */
94
128
  style?: MotionEnterStyle;
95
- /** For `slide`: the direction of travel (default `up`). */
129
+ /**
130
+ * For `slide`: the direction of travel (default `up`). For `wipe`: the way the edge travels
131
+ * (default `right`, which reveals from the left).
132
+ */
96
133
  direction?: MotionDirection;
97
- /** For `slide`: how far in document units (default 40). */
134
+ /** For `slide` (default 40) and `rise` (default 20): how far in document units. */
98
135
  distance?: number;
136
+ /** For `pop` (default 0.5) and `zoom` (default 1.6): the starting scale. */
137
+ scale?: number;
138
+ /** For `iris`: open through a `circle` (default; an ellipse on a box that is not square) or a `box`. */
139
+ shape?: MotionIrisShape;
140
+ /** For `split`: open up and down from a `horizontal` line through the middle (default), or left and right from a `vertical` one. */
141
+ orientation?: MotionSplitOrientation;
99
142
  }
100
143
  /** Takes targets out. */
101
144
  interface MotionExitCommand extends MotionCommandBase {
102
145
  type: 'exit';
103
- /** Default `fade`. */
146
+ /** Default `fade`. `wipe` hides the target behind an edge travelling `direction` (a wipe in, reversed). */
104
147
  style?: MotionExitStyle;
148
+ /** For `slide`: the direction of travel (default `down`). For `wipe`: the way the edge travels (default `right`, which hides from the left). */
105
149
  direction?: MotionDirection;
106
150
  distance?: number;
107
151
  }
@@ -138,6 +182,8 @@ interface MotionScaleCommand extends MotionCommandBase {
138
182
  x: number;
139
183
  y: number;
140
184
  };
185
+ /** Grow at a steady rate (the same factor each moment), which reads as an even zoom. */
186
+ geometric?: boolean;
141
187
  }
142
188
  /** Changes opacity (0–1). */
143
189
  interface MotionFadeCommand extends MotionCommandBase {
@@ -161,7 +207,10 @@ interface MotionFollowCommand extends MotionCommandBase {
161
207
  type: 'follow';
162
208
  /** Id of a path node in the artwork (it can be hidden). */
163
209
  path: string;
164
- /** Turn with the path's direction (default false). */
210
+ /**
211
+ * Turn with the path's direction (default false). Moves, shakes and wiggles made during the
212
+ * follow then turn with it too: they are offsets from the target's place on the path.
213
+ */
165
214
  rotate?: boolean;
166
215
  /** Fractions of the path's length to travel between (default 0 → 1). */
167
216
  from?: number;
@@ -174,12 +223,56 @@ interface MotionPulseCommand extends MotionCommandBase {
174
223
  amount?: number;
175
224
  count?: number;
176
225
  }
177
- /** Shakes side to side, settling. */
226
+ /** Shakes side to side (or up and down), settling. */
178
227
  interface MotionShakeCommand extends MotionCommandBase {
179
228
  type: 'shake';
180
229
  /** Distance in document units (default 8). */
181
230
  amount?: number;
182
231
  count?: number;
232
+ /** Default `horizontal`. */
233
+ axis?: 'horizontal' | 'vertical';
234
+ }
235
+ /** Rocks about the centre by `angle` either way, settling (Flair's `emphasis.teeter`). */
236
+ interface MotionTeeterCommand extends MotionCommandBase {
237
+ type: 'teeter';
238
+ /** Degrees either way (default 8). */
239
+ angle?: number;
240
+ /** Rocks (default 3). */
241
+ count?: number;
242
+ }
243
+ /** Bobs up and down without settling, like something floating (Flair's `emphasis.wave`). */
244
+ interface MotionWaveCommand extends MotionCommandBase {
245
+ type: 'wave';
246
+ /** Height in document units (default 6). */
247
+ amount?: number;
248
+ /** Bobs (default 2). */
249
+ count?: number;
250
+ }
251
+ /**
252
+ * Wanders about its place as smooth seeded noise, easing out of and back to rest (Flair's
253
+ * `emphasis.wiggle`). The same seed always wanders the same way.
254
+ */
255
+ interface MotionWiggleCommand extends MotionCommandBase {
256
+ type: 'wiggle';
257
+ /** Up to about this far, in document units (default 6). */
258
+ amount?: number;
259
+ /** Wanders per second, in Hz (default 3). */
260
+ frequency?: number;
261
+ /** Layers of finer detail (default 2). */
262
+ octaves?: number;
263
+ /** Also turns about the centre by up to about this many degrees (default 0). */
264
+ rotate?: number;
265
+ /** A whole number; default: one picked from the target's id, so each target wanders its own way. */
266
+ seed?: number;
267
+ }
268
+ /** Tints solid paint toward a colour and back, `count` times (Flair's `emphasis.color-pulse`). */
269
+ interface MotionColorPulseCommand extends MotionCommandBase {
270
+ type: 'color-pulse';
271
+ /** The tint (default `#f59e0b`). Mixes in OKLab. */
272
+ color?: string;
273
+ /** Which paint (default `fill`). On a group it tints every path in it. */
274
+ paint?: 'fill' | 'stroke';
275
+ count?: number;
183
276
  }
184
277
  /** Tweens a numeric parameter of a live path effect on the target (twist angle, wiggle time, zig zag size…). */
185
278
  interface MotionEffectCommand extends MotionCommandBase {
@@ -197,11 +290,14 @@ interface MotionWaitCommand {
197
290
  with?: 'after' | 'previous';
198
291
  note?: string;
199
292
  }
200
- type MotionCommand = MotionEnterCommand | MotionExitCommand | MotionDrawCommand | MotionMoveCommand | MotionRotateCommand | MotionScaleCommand | MotionFadeCommand | MotionStyleCommand | MotionMorphCommand | MotionFollowCommand | MotionPulseCommand | MotionShakeCommand | MotionEffectCommand | MotionWaitCommand;
293
+ type MotionCommand = MotionEnterCommand | MotionExitCommand | MotionDrawCommand | MotionMoveCommand | MotionRotateCommand | MotionScaleCommand | MotionFadeCommand | MotionStyleCommand | MotionMorphCommand | MotionFollowCommand | MotionPulseCommand | MotionShakeCommand | MotionTeeterCommand | MotionWaveCommand | MotionWiggleCommand | MotionColorPulseCommand | MotionEffectCommand | MotionWaitCommand;
201
294
  type MotionCommandType = MotionCommand['type'];
202
295
  declare const MOTION_COMMAND_TYPES: readonly MotionCommandType[];
203
296
  declare const MOTION_ENTER_STYLES: readonly MotionEnterStyle[];
297
+ declare const MOTION_REPEAT_MODES: readonly ["loop", "mirror", "offset"];
204
298
  declare const MOTION_EXIT_STYLES: readonly MotionExitStyle[];
299
+ declare const MOTION_IRIS_SHAPES: readonly MotionIrisShape[];
300
+ declare const MOTION_SPLIT_ORIENTATIONS: readonly MotionSplitOrientation[];
205
301
  declare const MOTION_DIRECTIONS: readonly MotionDirection[];
206
302
  /**
207
303
  * A colour parameter of the effect made from the scene: it stands for every use of its
@@ -228,6 +324,12 @@ declare const MOTION_CAMERA_MS = 1000;
228
324
  /** `draw` lasts `length × MOTION_DRAW_MS_PER_UNIT`, clamped to `MOTION_DRAW_RANGE`. */
229
325
  declare const MOTION_DRAW_MS_PER_UNIT = 1.5;
230
326
  declare const MOTION_DRAW_RANGE: readonly [number, number];
327
+ /**
328
+ * The Flair motion primitive (`@pixerate/flair/motion`) a command plays, or undefined when
329
+ * Flair has none (`move`, `scale`, `fade`, `style`, `morph`, `effect`, `wait` are the editor's
330
+ * own). Commands and primitives of the same name take the same parameters and defaults.
331
+ */
332
+ declare function motionFlairPrimitive(command: MotionCommand): string | undefined;
231
333
 
232
334
  /**
233
335
  * Problems with a scene, as data: every issue has a stable `code`, a severity, the shot and
@@ -258,6 +360,10 @@ declare const MOTION_ISSUE_CODES: {
258
360
  readonly severity: "error";
259
361
  readonly wording: "A follow path must be a path node, not a group.";
260
362
  };
363
+ readonly 'reveal-no-box': {
364
+ readonly severity: "warning";
365
+ readonly wording: "A wipe, iris or split cannot clip a node with no width or height (a lone straight line), so it fades instead.";
366
+ };
261
367
  readonly 'unknown-shape': {
262
368
  readonly severity: "error";
263
369
  readonly wording: "A morph names a shape the artwork does not have.";
@@ -284,7 +390,7 @@ declare const MOTION_ISSUE_CODES: {
284
390
  };
285
391
  readonly 'paint-not-color': {
286
392
  readonly severity: "warning";
287
- readonly wording: "A style command recolours a gradient or empty paint, which only switches at the end.";
393
+ readonly wording: "A style command recolours a gradient or empty paint, which only switches at the end, or a colour pulse has no solid paint to tint.";
288
394
  };
289
395
  readonly overlap: {
290
396
  readonly severity: "warning";
@@ -306,6 +412,10 @@ declare const MOTION_ISSUE_CODES: {
306
412
  readonly severity: "warning";
307
413
  readonly wording: "A change is too quick to follow.";
308
414
  };
415
+ readonly 'spring-cut-short': {
416
+ readonly severity: "warning";
417
+ readonly wording: "A spring is given a duration shorter than it takes to settle, so it jumps to its end.";
418
+ };
309
419
  readonly 'scene-too-long': {
310
420
  readonly severity: "warning";
311
421
  readonly wording: "The scene runs past its target length.";
@@ -386,16 +496,12 @@ declare const isMotionCommand: (command: unknown) => command is MotionCommand;
386
496
  * Properties a segment can change. Offsets (`x`, `y`, `rotation`) are relative to the rest
387
497
  * pose, scales multiply it, and turns and scales are about the node's centre at rest.
388
498
  * `effect:<effectId>:<param>` tweens a live path effect's parameter. `camera` (node `""`) is
389
- * the view box `[x, y, width, height]`.
499
+ * the view box `[x, y, width, height]`. `clipRect` (`[x, y, width, height]`) and `clipCircle`
500
+ * (`[cx, cy, r]`) are the reveal clips of `wipe`, `iris` and `split`, in the units of the
501
+ * node's own box (0–1 across it, SVG's `objectBoundingBox`), as Flair's clip reveals are.
390
502
  */
391
- type MotionProperty = 'opacity' | 'x' | 'y' | 'rotation' | 'scaleX' | 'scaleY' | 'fill' | 'stroke' | 'strokeWidth' | 'fillOpacity' | 'drawStart' | 'drawEnd' | 'shape' | 'follow' | 'camera' | `effect:${string}:${string}`;
503
+ type MotionProperty = 'opacity' | 'x' | 'y' | 'rotation' | 'scaleX' | 'scaleY' | 'fill' | 'stroke' | 'strokeWidth' | 'fillOpacity' | 'drawStart' | 'drawEnd' | 'shape' | 'follow' | 'clipRect' | 'clipCircle' | 'camera' | `effect:${string}:${string}`;
392
504
  type MotionValue = number | string | readonly number[];
393
- /** An oscillation laid over a segment (`pulse`: scale up and back; `shake`: side to side, settling). */
394
- interface MotionWave {
395
- kind: 'pulse' | 'shake';
396
- amount: number;
397
- count: number;
398
- }
399
505
  interface MotionSegment {
400
506
  /** Node id (`""` for the camera). */
401
507
  node: string;
@@ -408,7 +514,22 @@ interface MotionSegment {
408
514
  /** The shot and command (index in `run`) it comes from. */
409
515
  shot: string;
410
516
  command: number;
411
- wave?: MotionWave;
517
+ /**
518
+ * How the change gets from `from` to `to` when not straight along its ease (a pulse there and
519
+ * back, a settling oscillation, a wriggle): Flair's `behavior`, exported as is.
520
+ */
521
+ behavior?: MotionBehavior;
522
+ /**
523
+ * Which number of the node's exported value this is (x 0, y 1, rotation 2, matching the
524
+ * transform Flair is given), so a wriggle wanders each on the channel Flair uses. Default 0.
525
+ */
526
+ index?: number;
527
+ /** Played backwards in time (a mirrored repeat): progress runs 1 → 0 through the ease. */
528
+ reverse?: boolean;
529
+ /** Stepped time: progress holds on a grid of this many frames per second from the start. */
530
+ steps?: number;
531
+ /** Numbers change by a steady factor (`from·(to/from)^u`); a camera zooms at a steady rate. */
532
+ geometric?: boolean;
412
533
  /** For `follow`: the path node id, and whether to turn with it. */
413
534
  path?: string;
414
535
  rotate?: boolean;
@@ -457,9 +578,39 @@ interface MotionNodeEntry {
457
578
  };
458
579
  /** Bounds at rest in document space. */
459
580
  bounds: VectorBounds | null;
581
+ /** Bounds at rest in its parent's space (what a reveal clip's box is). */
582
+ box: VectorBounds | null;
460
583
  }
461
584
  /** Every node of the artwork by id (cached per node list). */
462
585
  declare function indexMotionNodes(nodes: readonly VectorNode[]): Map<string, MotionNodeEntry>;
586
+ /**
587
+ * How far past its box (as a share of its width and height) a reveal clip reaches, so it shows
588
+ * the whole stroke: the stroke's reach over the box plus 0.02, at most 1 (Flair's padding). Null
589
+ * when the box has no width or height (a lone straight line), which a clip cannot reveal.
590
+ */
591
+ declare function motionRevealPadding(entry: MotionNodeEntry): readonly [number, number] | null;
592
+ /** What a reveal clip animates: a wipe (in, or out with `hide`), an iris or a split. */
593
+ type MotionReveal = {
594
+ style: 'wipe';
595
+ direction?: MotionDirection;
596
+ hide?: boolean;
597
+ } | {
598
+ style: 'iris';
599
+ shape?: MotionIrisShape;
600
+ } | {
601
+ style: 'split';
602
+ orientation?: MotionSplitOrientation;
603
+ };
604
+ /**
605
+ * The reveal clip a `wipe`, `iris` or `split` animates, from and to (Flair's `entrance.wipe`,
606
+ * `entrance.iris`, `entrance.split` and `exit.wipe`), or null when the node's box has no width
607
+ * or height. An exit wipe is the enter wipe the other way, reversed in time.
608
+ */
609
+ declare function motionRevealClip(entry: MotionNodeEntry, reveal: MotionReveal): {
610
+ property: 'clipRect' | 'clipCircle';
611
+ from: number[];
612
+ to: number[];
613
+ } | null;
463
614
  /** A property's value with no animation. */
464
615
  declare function motionRestValue(entry: MotionNodeEntry | undefined, property: MotionProperty, artwork: {
465
616
  width: number;
@@ -467,17 +618,20 @@ declare function motionRestValue(entry: MotionNodeEntry | undefined, property: M
467
618
  }): MotionValue;
468
619
  /** `from` → `to` at eased progress `u` (numbers extrapolate, colours mix in OKLab, others switch at the end). */
469
620
  declare function interpolateMotionValue(from: MotionValue, to: MotionValue, u: number): MotionValue;
470
- /** Progress (0–1, unclamped by the easing) of a segment at `time`. */
621
+ /** Progress (0–1, unclamped by the easing) of a segment at `time`: stepped, reversed and eased as the segment says. */
471
622
  declare function segmentProgress(segment: MotionSegment, time: number): {
472
623
  u: number;
473
624
  eased: number;
474
625
  };
475
- /** A segment's value at `time`. */
476
- declare function segmentValue(segment: MotionSegment, time: number): MotionValue;
626
+ /**
627
+ * A segment's value at `time`: along its ease, through its behaviour when it has one. `raw`
628
+ * ignores the behaviour (the value it swings towards), as Flair is given it.
629
+ */
630
+ declare function segmentValue(segment: MotionSegment, time: number, raw?: boolean): MotionValue;
477
631
  /** The segment in effect at `time` (the latest to have started), if any. */
478
632
  declare function activeSegment(track: readonly MotionSegment[] | undefined, time: number): MotionSegment | undefined;
479
- /** A track's value at `time`, or `undefined` when nothing animates it. */
480
- declare function trackValue(track: readonly MotionSegment[] | undefined, time: number): MotionValue | undefined;
633
+ /** A track's value at `time`, or `undefined` when nothing animates it. `raw` ignores behaviours (see `segmentValue`). */
634
+ declare function trackValue(track: readonly MotionSegment[] | undefined, time: number, raw?: boolean): MotionValue | undefined;
481
635
  declare const motionTrackKey: (node: string, property: MotionProperty) => string;
482
636
  /** Grows a rectangle to the artwork's aspect ratio (about its centre). */
483
637
  declare function fitMotionRect(rect: MotionRect, aspect: number): MotionRect;
@@ -485,8 +639,12 @@ declare function fitMotionRect(rect: MotionRect, aspect: number): MotionRect;
485
639
  declare function resolveMotionCamera(camera: MotionCamera, scene: MotionScene): MotionRect | null;
486
640
  /** Length of a node's outline (its longest path for a group), in document units. */
487
641
  declare function motionPathLength(node: VectorNode, parentWorld?: AffineMatrix): number;
488
- /** How long one target of a command takes (ms). */
642
+ /** How many times a command plays (1 for `enter`, `exit` and `wait`, which do not repeat). */
643
+ declare function motionCommandRepeat(command: MotionCommand): number;
644
+ /** How long one target of a command takes (ms), every repeat included. */
489
645
  declare function motionCommandDuration(command: MotionCommand, scene: MotionScene, target?: string): number;
646
+ /** How long one cycle of a command takes for one target (ms): its `duration`, a spring's settle time, the line's length for a draw, or the type's default. */
647
+ declare function motionCycleDuration(command: MotionCommand, scene: MotionScene, target?: string): number;
490
648
  /** Cubic ease-out, `1 − (1 − t)³` exactly (Flair's `out`): the default for things arriving. */
491
649
  declare const MOTION_EASE_OUT: AnimationEasing;
492
650
  /** Cubic ease-in-out (close to Flair's `in-out`): the default for moves. */
@@ -500,11 +658,32 @@ declare function compileMotionScene(scene: MotionScene): MotionTimeline;
500
658
  /** The shot playing at `time` (the last one at the end). */
501
659
  declare function motionShotAt(timeline: MotionTimeline, time: number): MotionShotTiming | undefined;
502
660
 
661
+ /**
662
+ * A reveal clip (`wipe`, `iris`, `split`) in the units of the node's own box: 0–1 across it,
663
+ * as SVG's `clipPathUnits="objectBoundingBox"` and Flair's clip reveals are.
664
+ */
665
+ type MotionClipShape = {
666
+ shape: 'rect';
667
+ x: number;
668
+ y: number;
669
+ width: number;
670
+ height: number;
671
+ } | {
672
+ shape: 'circle';
673
+ cx: number;
674
+ cy: number;
675
+ r: number;
676
+ };
503
677
  /** The artwork as it stands at one time, and where the camera looks. */
504
678
  interface MotionFrame {
505
679
  time: number;
506
680
  document: VectorDocument;
507
681
  camera: MotionRect;
682
+ /**
683
+ * Reveal clips by node id, outermost first: draw each of those nodes inside a group clipped
684
+ * to each shape (`motionFrameToSvg` does). The vector model has no clips, so they ride here.
685
+ */
686
+ clips: Record<string, MotionClipShape[]>;
508
687
  }
509
688
  interface MotionFrameOptions {
510
689
  /** Control values (`{ accent: "#0ea5e9" }`): each recolours its control's default colour. */
@@ -518,15 +697,33 @@ interface MotionFrameOptions {
518
697
  declare const MOTION_DRAW_EFFECT_ID = "~draw";
519
698
  /** A copy of the scene with each control's default colour replaced by its value (exact, case-insensitive matches). */
520
699
  declare function applyMotionControlValues(scene: MotionScene, values: Record<string, string> | undefined): MotionScene;
521
- /** The transform a node's offsets, turn, scale and follow add (parent space), or null when none. */
522
- declare function motionNodeOffset(entry: MotionNodeEntry, timeline: MotionTimeline, time: number, nodes?: Map<string, MotionNodeEntry>): {
700
+ /**
701
+ * The path a node follows, in the node's parent's space (live effects left off), or null when
702
+ * `path` is not a path or cannot be measured. Lengths and turns along it are measured there.
703
+ */
704
+ declare function motionFollowRoute(entry: MotionNodeEntry, path: MotionNodeEntry | undefined): BezierPath | null;
705
+ /**
706
+ * The transform a node's offsets, turn, scale and follow add (parent space), or null when none:
707
+ * scaled and turned about its centre, offset by `dx`, `dy`, then (following a path) its centre
708
+ * carried to the point on the path and turned with it, as Flair's `along` carries a wrapper.
709
+ * `raw` ignores behaviours (the pose a pulse or shake swings towards, as Flair is given it);
710
+ * `follow: false` leaves the follow out (`dx` … `scaleY` never include it).
711
+ */
712
+ declare function motionNodeOffset(entry: MotionNodeEntry, timeline: MotionTimeline, time: number, nodes?: Map<string, MotionNodeEntry>, raw?: boolean, follow?: boolean): {
523
713
  matrix: AffineMatrix;
524
714
  dx: number;
525
715
  dy: number;
526
716
  rotation: number;
527
717
  scaleX: number;
528
718
  scaleY: number;
719
+ follow: {
720
+ x: number;
721
+ y: number;
722
+ angle: number;
723
+ } | null;
529
724
  } | null;
725
+ /** A node's reveal clips at `time`, outermost first (rect, then circle), with sizes kept ≥ 0. */
726
+ declare function motionNodeClips(id: string, timeline: MotionTimeline, time: number): MotionClipShape[];
530
727
  /**
531
728
  * The scene at `time` (ms): the artwork with every change applied, and the camera. A pure
532
729
  * function of time, so frames can be drawn in any order (and the same frames come out every time).
@@ -534,7 +731,9 @@ declare function motionNodeOffset(entry: MotionNodeEntry, timeline: MotionTimeli
534
731
  declare function motionFrameAt(scene: MotionScene, time: number, options?: MotionFrameOptions): MotionFrame;
535
732
  interface MotionSvgOptions extends MotionFrameOptions, VectorSvgExportOptions {
536
733
  }
537
- /** The scene at `time` as a standalone SVG (the camera becomes the `viewBox`). */
734
+ /** A clip shape's SVG attributes. */
735
+ declare function motionClipAttrs(clip: MotionClipShape): Record<string, number>;
736
+ /** The scene at `time` as a standalone SVG (the camera becomes the `viewBox`, reveal clips wrap their nodes). */
538
737
  declare function motionFrameToSvg(scene: MotionScene, time: number, options?: MotionSvgOptions): string;
539
738
  /** Replaces the root `viewBox` of an SVG string. */
540
739
  declare function withViewBox(svg: string, box: MotionRect): string;
@@ -560,6 +759,7 @@ type MotionEdit = {
560
759
  description?: string | null;
561
760
  loop?: MotionScene['loop'];
562
761
  fps?: number;
762
+ steps?: number | null;
563
763
  target?: [number, number] | null;
564
764
  } | {
565
765
  op: 'insert_shots';
@@ -631,13 +831,30 @@ declare function createMotionScene(artwork: VectorDocument, init?: Partial<Omit<
631
831
  * Changes are exact where Flair can express them: a change with a linear, cubic-out or
632
832
  * cubic-in-out curve (the editor's defaults) is one change, and so (Flair 0.36 and later) is
633
833
  * one with any cubic-bezier curve, written as `[x1, y1, x2, y2]`. Anything else (springs,
634
- * elastic, `hold`, pulses and shakes, follows, morphs and live-effect tweens) is sampled at the
635
- * scene's `fps` into short linear changes, which Flair plays the same way.
834
+ * elastic, `hold`, morphs and live-effect tweens) is sampled at the
835
+ * scene's `fps` into short linear changes, which Flair plays the same way. Pulses, shakes,
836
+ * teeters, waves, wiggles and colour pulses are one change with a `behavior` (Flair 0.38).
837
+ *
838
+ * Reveals (`wipe`, `iris`, `split`) wrap their node in a group clipped through a rect or circle
839
+ * in the group's own box units (`clipPathUnits="objectBoundingBox"`), as Flair's
840
+ * `entrance.wipe` does, and animate the shape's attributes. A follow wraps its node in a group
841
+ * that Flair's `along` carries along a copy of the path in the node's parent's units (written
842
+ * in `<defs>`, so the path may be hidden or anywhere), turning with it for `orient`, as
843
+ * Flair's `path.follow` does.
636
844
  */
637
845
  /** Flair's easing names. */
638
846
  type FlairEaseName = 'linear' | 'in-out' | 'out';
639
- /** A Flair easing: a name, or (Flair 0.36 and later) a CSS cubic-bezier `[x1, y1, x2, y2]`. */
640
- type FlairEase = FlairEaseName | readonly [number, number, number, number];
847
+ /** Flair's spring ease (Flair 0.39 and later): the editor's spring, number for number. */
848
+ interface FlairSpringEase {
849
+ spring: {
850
+ mass?: number;
851
+ stiffness?: number;
852
+ damping?: number;
853
+ velocity?: number;
854
+ };
855
+ }
856
+ /** A Flair easing: a name, (Flair 0.36 and later) a CSS cubic-bezier `[x1, y1, x2, y2]`, or (0.39) a spring. */
857
+ type FlairEase = FlairEaseName | readonly [number, number, number, number] | FlairSpringEase;
641
858
  /** Flair's curves, for matching. */
642
859
  declare const FLAIR_EASES: Readonly<Record<FlairEaseName, (t: number) => number>>;
643
860
  /** One change of a Flair `SvgMotion` (mirrors `SvgAnimation` in `@pixerate/flair/vector`). */
@@ -649,6 +866,23 @@ interface FlairSvgAnimation {
649
866
  ease?: FlairEase;
650
867
  attrs?: Record<string, readonly [string | number, string | number]>;
651
868
  draw?: boolean | readonly [number, number];
869
+ /** Moves the element along a path (by id), from and to the fractions of its length in `at`. */
870
+ along?: string;
871
+ at?: readonly [number, number];
872
+ /** With `along`: turns the element with the path's direction. */
873
+ orient?: boolean;
874
+ /** Flair 0.38 and later: how the change swings from → to (see `MotionBehavior`). */
875
+ behavior?: MotionBehavior;
876
+ /** Flair 0.39 and later: stepped time, held on a grid of this many frames per second. */
877
+ strobe?: number;
878
+ /** Flair 0.39 and later: whole cycles to play (default 1); with `alternate`, every other one backwards. */
879
+ repeat?: number;
880
+ alternate?: boolean;
881
+ /** Flair features scenes do not have (imports skip them with a note). */
882
+ count?: readonly [number, number];
883
+ write?: string;
884
+ scramble?: string;
885
+ solo?: number;
652
886
  view?: readonly [number, number, number, number] | string;
653
887
  }
654
888
  /** Mirrors `SvgMotion` in `@pixerate/flair/vector`. */
@@ -714,7 +948,23 @@ interface FlairExportOptions {
714
948
  * Flair, and they are sampled into linear steps.
715
949
  */
716
950
  curves?: boolean;
951
+ /**
952
+ * Write pulses, shakes, teeters, waves, wiggles and colour pulses as one change with a Flair
953
+ * `behavior` (default true), so they play exactly. Needs Flair 0.38 or later; set false for
954
+ * older Flair, and they are sampled into linear steps.
955
+ */
956
+ behaviors?: boolean;
957
+ /**
958
+ * The Flair version the export is for (default `0.39`): it decides what is written exactly
959
+ * and what is sampled. 0.36 adds cubic-bezier curves, 0.38 behaviours, 0.39 springs and
960
+ * stepped time (`strobe`). `curves` and `behaviors` override it.
961
+ */
962
+ flair?: string;
717
963
  }
964
+ /** The Flair version exports target by default. */
965
+ declare const FLAIR_EXPORT_VERSION = "0.39";
966
+ /** Whether Flair `version` is at least `min` (dotted numbers). */
967
+ declare function flairAtLeast(version: string, min: string): boolean;
718
968
  /** The Flair easing that matches `ease` (within 1% everywhere), if any. */
719
969
  declare function flairEaseFor(ease: AnimationEasing): FlairEaseName | undefined;
720
970
  /**
@@ -740,6 +990,36 @@ declare function toFlairVectorEffect(scene: MotionScene, options?: FlairEffectOp
740
990
  /** A scene as a document for Flair's `EffectEditor` (`replaceDocument`). */
741
991
  declare function toFlairEffectDocument(scene: MotionScene, options?: FlairExportOptions): FlairEffectDocument;
742
992
 
993
+ /**
994
+ * Import from Flair: a Flair `SvgMotion` (or a catalog `VectorEffect`) becomes a scene, so an
995
+ * effect made in Flair, or taken from its catalog, can be opened and edited here. The reverse
996
+ * of `toFlairSvgMotion`, as far as the scene model goes:
997
+ *
998
+ * - The SVG becomes the artwork (`parseVectorSvg`); `{{name: color …}}` placeholders become
999
+ * colour controls (other placeholders take their defaults).
1000
+ * - Each Flair step becomes a shot, its changes commands timed by their `delay`, all together.
1001
+ * - Opacity, paint, stroke width, transforms (as move, rotate and scale), drawing on, following
1002
+ * a path, camera views and behaviours (pulse, shake, teeter, wave, wiggle, colour pulse) carry
1003
+ * over. Counting, writing text, solo containers and anything else are listed in `notes`.
1004
+ */
1005
+ interface FlairImportResult {
1006
+ scene: MotionScene;
1007
+ /** What could not be carried over exactly, in sentences. */
1008
+ notes: string[];
1009
+ /** `checkMotionScene` of the result. */
1010
+ issues: MotionIssue[];
1011
+ }
1012
+ interface FlairImportOptions {
1013
+ /** The scene's name (default: the effect's, else `from-flair`). */
1014
+ name?: string;
1015
+ /** Hold after the last shot, ms (default: the effect's `hold`, else 600). */
1016
+ hold?: number;
1017
+ }
1018
+ /** A Flair `SvgMotion` as a scene (see the module notes for what carries over). */
1019
+ declare function fromFlairSvgMotion(input: FlairSvgMotion, options?: FlairImportOptions): FlairImportResult;
1020
+ /** A Flair catalog `VectorEffect` as a scene: its motion, name, description, colour controls and presets. */
1021
+ declare function fromFlairVectorEffect(effect: FlairVectorEffect, options?: FlairImportOptions): FlairImportResult;
1022
+
743
1023
  interface AnimationEditorOptions {
744
1024
  /** The scene to open (default: an empty one over a blank 800 × 450 artwork). */
745
1025
  scene?: MotionScene;
@@ -906,12 +1186,16 @@ declare function describeMotionElements(scene: MotionScene): {
906
1186
  /** A scene's shots with their timing, as agents see them. */
907
1187
  declare function describeMotionShots(scene: MotionScene): {
908
1188
  commands: ({
1189
+ flairPrimitive?: string | undefined;
909
1190
  start: number;
910
1191
  end: number;
911
1192
  type: "enter";
912
1193
  style?: MotionEnterStyle;
913
1194
  direction?: MotionDirection;
914
1195
  distance?: number;
1196
+ scale?: number;
1197
+ shape?: MotionIrisShape;
1198
+ orientation?: MotionSplitOrientation;
915
1199
  targets: string[];
916
1200
  duration?: number;
917
1201
  delay?: number;
@@ -919,8 +1203,12 @@ declare function describeMotionShots(scene: MotionScene): {
919
1203
  with?: "after" | "previous";
920
1204
  ease?: AnimationEasing;
921
1205
  note?: string;
1206
+ repeat?: number;
1207
+ repeatMode?: "loop" | "mirror" | "offset";
1208
+ steps?: number;
922
1209
  index: number;
923
1210
  } | {
1211
+ flairPrimitive?: string | undefined;
924
1212
  start: number;
925
1213
  end: number;
926
1214
  type: "exit";
@@ -934,8 +1222,12 @@ declare function describeMotionShots(scene: MotionScene): {
934
1222
  with?: "after" | "previous";
935
1223
  ease?: AnimationEasing;
936
1224
  note?: string;
1225
+ repeat?: number;
1226
+ repeatMode?: "loop" | "mirror" | "offset";
1227
+ steps?: number;
937
1228
  index: number;
938
1229
  } | {
1230
+ flairPrimitive?: string | undefined;
939
1231
  start: number;
940
1232
  end: number;
941
1233
  type: "draw";
@@ -948,8 +1240,12 @@ declare function describeMotionShots(scene: MotionScene): {
948
1240
  with?: "after" | "previous";
949
1241
  ease?: AnimationEasing;
950
1242
  note?: string;
1243
+ repeat?: number;
1244
+ repeatMode?: "loop" | "mirror" | "offset";
1245
+ steps?: number;
951
1246
  index: number;
952
1247
  } | {
1248
+ flairPrimitive?: string | undefined;
953
1249
  start: number;
954
1250
  end: number;
955
1251
  type: "move";
@@ -968,8 +1264,12 @@ declare function describeMotionShots(scene: MotionScene): {
968
1264
  with?: "after" | "previous";
969
1265
  ease?: AnimationEasing;
970
1266
  note?: string;
1267
+ repeat?: number;
1268
+ repeatMode?: "loop" | "mirror" | "offset";
1269
+ steps?: number;
971
1270
  index: number;
972
1271
  } | {
1272
+ flairPrimitive?: string | undefined;
973
1273
  start: number;
974
1274
  end: number;
975
1275
  type: "rotate";
@@ -982,8 +1282,12 @@ declare function describeMotionShots(scene: MotionScene): {
982
1282
  with?: "after" | "previous";
983
1283
  ease?: AnimationEasing;
984
1284
  note?: string;
1285
+ repeat?: number;
1286
+ repeatMode?: "loop" | "mirror" | "offset";
1287
+ steps?: number;
985
1288
  index: number;
986
1289
  } | {
1290
+ flairPrimitive?: string | undefined;
987
1291
  start: number;
988
1292
  end: number;
989
1293
  type: "scale";
@@ -991,6 +1295,7 @@ declare function describeMotionShots(scene: MotionScene): {
991
1295
  x: number;
992
1296
  y: number;
993
1297
  };
1298
+ geometric?: boolean;
994
1299
  targets: string[];
995
1300
  duration?: number;
996
1301
  delay?: number;
@@ -998,8 +1303,12 @@ declare function describeMotionShots(scene: MotionScene): {
998
1303
  with?: "after" | "previous";
999
1304
  ease?: AnimationEasing;
1000
1305
  note?: string;
1306
+ repeat?: number;
1307
+ repeatMode?: "loop" | "mirror" | "offset";
1308
+ steps?: number;
1001
1309
  index: number;
1002
1310
  } | {
1311
+ flairPrimitive?: string | undefined;
1003
1312
  start: number;
1004
1313
  end: number;
1005
1314
  type: "fade";
@@ -1011,8 +1320,12 @@ declare function describeMotionShots(scene: MotionScene): {
1011
1320
  with?: "after" | "previous";
1012
1321
  ease?: AnimationEasing;
1013
1322
  note?: string;
1323
+ repeat?: number;
1324
+ repeatMode?: "loop" | "mirror" | "offset";
1325
+ steps?: number;
1014
1326
  index: number;
1015
1327
  } | {
1328
+ flairPrimitive?: string | undefined;
1016
1329
  start: number;
1017
1330
  end: number;
1018
1331
  type: "style";
@@ -1026,8 +1339,12 @@ declare function describeMotionShots(scene: MotionScene): {
1026
1339
  with?: "after" | "previous";
1027
1340
  ease?: AnimationEasing;
1028
1341
  note?: string;
1342
+ repeat?: number;
1343
+ repeatMode?: "loop" | "mirror" | "offset";
1344
+ steps?: number;
1029
1345
  index: number;
1030
1346
  } | {
1347
+ flairPrimitive?: string | undefined;
1031
1348
  start: number;
1032
1349
  end: number;
1033
1350
  type: "morph";
@@ -1039,8 +1356,12 @@ declare function describeMotionShots(scene: MotionScene): {
1039
1356
  with?: "after" | "previous";
1040
1357
  ease?: AnimationEasing;
1041
1358
  note?: string;
1359
+ repeat?: number;
1360
+ repeatMode?: "loop" | "mirror" | "offset";
1361
+ steps?: number;
1042
1362
  index: number;
1043
1363
  } | {
1364
+ flairPrimitive?: string | undefined;
1044
1365
  start: number;
1045
1366
  end: number;
1046
1367
  type: "follow";
@@ -1055,8 +1376,12 @@ declare function describeMotionShots(scene: MotionScene): {
1055
1376
  with?: "after" | "previous";
1056
1377
  ease?: AnimationEasing;
1057
1378
  note?: string;
1379
+ repeat?: number;
1380
+ repeatMode?: "loop" | "mirror" | "offset";
1381
+ steps?: number;
1058
1382
  index: number;
1059
1383
  } | {
1384
+ flairPrimitive?: string | undefined;
1060
1385
  start: number;
1061
1386
  end: number;
1062
1387
  type: "pulse";
@@ -1069,13 +1394,94 @@ declare function describeMotionShots(scene: MotionScene): {
1069
1394
  with?: "after" | "previous";
1070
1395
  ease?: AnimationEasing;
1071
1396
  note?: string;
1397
+ repeat?: number;
1398
+ repeatMode?: "loop" | "mirror" | "offset";
1399
+ steps?: number;
1072
1400
  index: number;
1073
1401
  } | {
1402
+ flairPrimitive?: string | undefined;
1074
1403
  start: number;
1075
1404
  end: number;
1076
1405
  type: "shake";
1077
1406
  amount?: number;
1078
1407
  count?: number;
1408
+ axis?: "horizontal" | "vertical";
1409
+ targets: string[];
1410
+ duration?: number;
1411
+ delay?: number;
1412
+ stagger?: number;
1413
+ with?: "after" | "previous";
1414
+ ease?: AnimationEasing;
1415
+ note?: string;
1416
+ repeat?: number;
1417
+ repeatMode?: "loop" | "mirror" | "offset";
1418
+ steps?: number;
1419
+ index: number;
1420
+ } | {
1421
+ flairPrimitive?: string | undefined;
1422
+ start: number;
1423
+ end: number;
1424
+ type: "teeter";
1425
+ angle?: number;
1426
+ count?: number;
1427
+ targets: string[];
1428
+ duration?: number;
1429
+ delay?: number;
1430
+ stagger?: number;
1431
+ with?: "after" | "previous";
1432
+ ease?: AnimationEasing;
1433
+ note?: string;
1434
+ repeat?: number;
1435
+ repeatMode?: "loop" | "mirror" | "offset";
1436
+ steps?: number;
1437
+ index: number;
1438
+ } | {
1439
+ flairPrimitive?: string | undefined;
1440
+ start: number;
1441
+ end: number;
1442
+ type: "wave";
1443
+ amount?: number;
1444
+ count?: number;
1445
+ targets: string[];
1446
+ duration?: number;
1447
+ delay?: number;
1448
+ stagger?: number;
1449
+ with?: "after" | "previous";
1450
+ ease?: AnimationEasing;
1451
+ note?: string;
1452
+ repeat?: number;
1453
+ repeatMode?: "loop" | "mirror" | "offset";
1454
+ steps?: number;
1455
+ index: number;
1456
+ } | {
1457
+ flairPrimitive?: string | undefined;
1458
+ start: number;
1459
+ end: number;
1460
+ type: "wiggle";
1461
+ amount?: number;
1462
+ frequency?: number;
1463
+ octaves?: number;
1464
+ rotate?: number;
1465
+ seed?: number;
1466
+ targets: string[];
1467
+ duration?: number;
1468
+ delay?: number;
1469
+ stagger?: number;
1470
+ with?: "after" | "previous";
1471
+ ease?: AnimationEasing;
1472
+ note?: string;
1473
+ repeat?: number;
1474
+ repeatMode?: "loop" | "mirror" | "offset";
1475
+ steps?: number;
1476
+ index: number;
1477
+ } | {
1478
+ flairPrimitive?: string | undefined;
1479
+ start: number;
1480
+ end: number;
1481
+ type: "color-pulse";
1482
+ color?: string;
1483
+ paint?: "fill" | "stroke";
1484
+ count?: number;
1079
1485
  targets: string[];
1080
1486
  duration?: number;
1081
1487
  delay?: number;
@@ -1083,8 +1489,12 @@ declare function describeMotionShots(scene: MotionScene): {
1083
1489
  with?: "after" | "previous";
1084
1490
  ease?: AnimationEasing;
1085
1491
  note?: string;
1492
+ repeat?: number;
1493
+ repeatMode?: "loop" | "mirror" | "offset";
1494
+ steps?: number;
1086
1495
  index: number;
1087
1496
  } | {
1497
+ flairPrimitive?: string | undefined;
1088
1498
  start: number;
1089
1499
  end: number;
1090
1500
  type: "effect";
@@ -1098,8 +1508,12 @@ declare function describeMotionShots(scene: MotionScene): {
1098
1508
  with?: "after" | "previous";
1099
1509
  ease?: AnimationEasing;
1100
1510
  note?: string;
1511
+ repeat?: number;
1512
+ repeatMode?: "loop" | "mirror" | "offset";
1513
+ steps?: number;
1101
1514
  index: number;
1102
1515
  } | {
1516
+ flairPrimitive?: string | undefined;
1103
1517
  start: number;
1104
1518
  end: number;
1105
1519
  type: "wait";
@@ -1128,6 +1542,6 @@ declare function createAnimationEditorToolProvider(controller: AnimationEditorCo
1128
1542
  * A short authoring guide for a model writing vector animations with the animation editor's
1129
1543
  * tools. Static, so it caches well as part of a system prompt.
1130
1544
  */
1131
- declare const ANIMATION_EDITOR_AGENT_INSTRUCTIONS = "You animate vector artwork. A scene is artwork (vector paths and groups, each with an id) plus shots.\n\nHow it plays\n- Shots play in order. A shot optionally moves the camera, plays its commands, then holds (default 600 ms).\n- Commands in a shot play one after another; give \"with\": \"previous\" to start one together with the command before, \"delay\" to wait, \"stagger\" to offset several targets.\n- Timing comes from the content: each command has a default length (enter 600 ms, move 700 ms, draw follows the path length). Only set \"duration\" to change the pace.\n- Anything that enters later is hidden until it enters. Things that never enter are visible from the start.\n- Every frame is a pure function of time: the same scene always plays the same frames.\n\nCommands\n- enter (fade, pop, grow, slide, draw) and exit (fade, shrink, slide, undraw) bring things in and out.\n- 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.\n- morph turns a shape into another node's shape (keep the target hidden); follow moves along a path node; pulse and shake draw the eye.\n- effect tweens a numeric parameter of a live path effect already on the node (twist angle, wiggle time, zig zag size).\n\nWorking method\n1. 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).\n2. Plan a few shots, each one idea. Add them with insert_shots, or edit_animation to make several edits as one step (dry_run first when unsure).\n3. check_animation and fix every error and warning; render_animation_frame (shot + \"end\") to look at the result.\n4. To make a reusable Flair effect, set_animation_controls (colour controls stand for every use of their default colour) and export_flair_effect.\n\nKeep motion purposeful: one thing moving at a time reads best, ease-out for arrivals, ease-in for exits, and hold long enough to see each result.";
1545
+ declare const ANIMATION_EDITOR_AGENT_INSTRUCTIONS = "You animate vector artwork. A scene is artwork (vector paths and groups, each with an id) plus shots.\n\nHow it plays\n- Shots play in order. A shot optionally moves the camera, plays its commands, then holds (default 600 ms).\n- Commands in a shot play one after another; give \"with\": \"previous\" to start one together with the command before, \"delay\" to wait, \"stagger\" to offset several targets.\n- Timing comes from the content: each command has a default length (enter 600 ms, move 700 ms, draw follows the path length). Only set \"duration\" to change the pace.\n- Anything that enters later is hidden until it enters. Things that never enter are visible from the start.\n- Every frame is a pure function of time: the same scene always plays the same frames.\n\nCommands\n- enter (fade, pop, grow, slide, draw) and exit (fade, shrink, slide, undraw) bring things in and out.\n- 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.\n- 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.\n- enter also has zoom (settles from larger) and rise (rises a short way, overshooting a touch); pop and zoom take a starting \"scale\".\n- morph turns a shape into another node's shape (keep the target hidden); follow moves along a path node (with \"rotate\", moves made meanwhile turn with the path).\n- Emphasis draws the eye and returns to rest: pulse (swell), shake (side to side, or \"axis\": \"vertical\"), teeter (rock), wave (bob, no settling), wiggle (seeded wander; the same seed wanders the same way) and color-pulse (tint and back).\n- Any command but enter and exit can \"repeat\" (loop, mirror back and forth, or offset to carry on: rotate by 90, repeat 4 is a full turn); \"duration\" is one cycle.\n- Springs (\"spring-gentle\", \"spring-quick\", \"spring-bouncy\", \"spring-slow\" or { \"type\": \"spring\", \u2026 }) feel physical; leave \"duration\" out and the spring sets it.\n- \"steps\": 12 (on a command, or for the whole scene with set_animation_settings) holds poses \"on twos\" for a hand-drawn look. \"geometric\": true on scale or a camera zooms at an even rate.\n- Commands share their names and defaults with Flair's motion primitives (get_animation shows each one's \"flairPrimitive\"), and emphasis, reveals and follows export to Flair exactly.\n- effect tweens a numeric parameter of a live path effect already on the node (twist angle, wiggle time, zig zag size).\n\nWorking method\n1. 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).\n2. Plan a few shots, each one idea. Add them with insert_shots, or edit_animation to make several edits as one step (dry_run first when unsure).\n3. check_animation and fix every error and warning; render_animation_frame (shot + \"end\") to look at the result.\n4. To make a reusable Flair effect, set_animation_controls (colour controls stand for every use of their default colour) and export_flair_effect.\n\nKeep motion purposeful: one thing moving at a time reads best, ease-out for arrivals, ease-in for exits, and hold long enough to see each result.";
1132
1546
 
1133
- export { ANIMATION_EDITOR_AGENT_INSTRUCTIONS, AnimationEditorController, type AnimationEditorOptions, type AnimationEditorSelection, type AnimationEditorState, type AnimationEditorToolProviderOptions, FLAIR_EASES, type FlairEase, type FlairEaseName, type FlairEffectDocument, type FlairEffectOptions, type FlairExportOptions, type FlairMotionExport, type FlairSvgAnimation, type FlairSvgMotion, type FlairVectorEffect, MOTION_CAMERA_MS, MOTION_COMMAND_SCHEMA, MOTION_COMMAND_TYPES, MOTION_DEFAULT_DURATIONS, MOTION_DEFAULT_HOLD, MOTION_DIRECTIONS, MOTION_DRAW_EFFECT_ID, MOTION_DRAW_MS_PER_UNIT, MOTION_DRAW_RANGE, MOTION_EASE_IN_OUT, MOTION_EASE_OUT, MOTION_EDIT_OPS, MOTION_ENTER_STYLES, MOTION_EXIT_STYLES, MOTION_ISSUE_CODES, MOTION_MIN_CHANGE_MS, type MotionCamera, type MotionCommand, type MotionCommandBase, type MotionCommandTiming, type MotionCommandType, type MotionControl, type MotionDirection, type MotionDrawCommand, type MotionEdit, type MotionEditOp, type MotionEditResult, type MotionEffectCommand, type MotionEnterCommand, type MotionEnterStyle, type MotionExitCommand, type MotionExitStyle, type MotionFadeCommand, type MotionFollowCommand, type MotionFrame, type MotionFrameOptions, type MotionIssue, type MotionIssueCode, type MotionIssueSeverity, type MotionMorphCommand, type MotionMoveCommand, type MotionNodeEntry, type MotionPreset, type MotionProperty, type MotionPulseCommand, type MotionRect, type MotionRotateCommand, type MotionScaleCommand, type MotionScene, MotionSceneFormatError, type MotionSegment, type MotionShakeCommand, type MotionShot, type MotionShotTiming, type MotionStyleCommand, type MotionSvgOptions, type MotionTimeline, type MotionValue, type MotionWaitCommand, type MotionWave, activeSegment, applyMotionControlValues, applyMotionEdits, checkMotionScene, commandShapeProblems, compileMotionScene, createAnimationEditorToolProvider, createMotionScene, describeMotionElements, describeMotionShots, fitMotionRect, flairCurveFor, flairEaseFor, freshShotId, indexMotionNodes, interpolateMotionValue, isMotionCommand, isMotionShot, motionCommandDuration, motionFrameAt, motionFrameToSvg, motionIssue, motionIssueKey, motionIssueLine, motionNodeOffset, motionPathLength, motionRestValue, motionSceneShapeProblems, motionSceneToJson, motionShotAt, motionTrackKey, namedWithSuggestion, parseMotionScene, resolveMotionCamera, segmentProgress, segmentValue, shotShapeProblems, suggestName, toFlairEffectDocument, toFlairSvgMotion, toFlairVectorEffect, trackValue, withViewBox };
1547
+ export { ANIMATION_EDITOR_AGENT_INSTRUCTIONS, AnimationEditorController, type AnimationEditorOptions, type AnimationEditorSelection, type AnimationEditorState, type AnimationEditorToolProviderOptions, FLAIR_EASES, FLAIR_EXPORT_VERSION, type FlairEase, type FlairEaseName, type FlairEffectDocument, type FlairEffectOptions, type FlairExportOptions, type FlairImportOptions, type FlairImportResult, type FlairMotionExport, type FlairSpringEase, type FlairSvgAnimation, type FlairSvgMotion, type FlairVectorEffect, MOTION_CAMERA_MS, MOTION_COMMAND_SCHEMA, MOTION_COMMAND_TYPES, MOTION_DEFAULT_DURATIONS, MOTION_DEFAULT_HOLD, MOTION_DIRECTIONS, MOTION_DRAW_EFFECT_ID, MOTION_DRAW_MS_PER_UNIT, MOTION_DRAW_RANGE, MOTION_EASE_IN_OUT, MOTION_EASE_OUT, MOTION_EDIT_OPS, MOTION_ENTER_STYLES, MOTION_EXIT_STYLES, MOTION_IRIS_SHAPES, MOTION_ISSUE_CODES, MOTION_MIN_CHANGE_MS, MOTION_REPEAT_MODES, MOTION_SPLIT_ORIENTATIONS, type MotionCamera, type MotionClipShape, type MotionColorPulseCommand, type MotionCommand, type MotionCommandBase, type MotionCommandTiming, type MotionCommandType, type MotionControl, type MotionDirection, type MotionDrawCommand, type MotionEdit, type MotionEditOp, type MotionEditResult, type MotionEffectCommand, type MotionEnterCommand, type MotionEnterStyle, type MotionExitCommand, type MotionExitStyle, type MotionFadeCommand, type MotionFollowCommand, type MotionFrame, type MotionFrameOptions, type MotionIrisShape, type MotionIssue, type MotionIssueCode, type MotionIssueSeverity, type MotionMorphCommand, type MotionMoveCommand, type MotionNodeEntry, type MotionPreset, type MotionProperty, type MotionPulseCommand, type MotionRect, type MotionReveal, type MotionRotateCommand, type MotionScaleCommand, type MotionScene, MotionSceneFormatError, type MotionSegment, type MotionShakeCommand, type MotionShot, type MotionShotTiming, type MotionSplitOrientation, type MotionStyleCommand, type MotionSvgOptions, type MotionTeeterCommand, type MotionTimeline, type MotionValue, type MotionWaitCommand, type MotionWaveCommand, type MotionWiggleCommand, activeSegment, applyMotionControlValues, applyMotionEdits, checkMotionScene, commandShapeProblems, compileMotionScene, createAnimationEditorToolProvider, createMotionScene, describeMotionElements, describeMotionShots, fitMotionRect, flairAtLeast, flairCurveFor, flairEaseFor, freshShotId, fromFlairSvgMotion, fromFlairVectorEffect, indexMotionNodes, interpolateMotionValue, isMotionCommand, isMotionShot, motionClipAttrs, motionCommandDuration, motionCommandRepeat, motionCycleDuration, motionFlairPrimitive, motionFollowRoute, motionFrameAt, motionFrameToSvg, motionIssue, motionIssueKey, motionIssueLine, motionNodeClips, motionNodeOffset, motionPathLength, motionRestValue, motionRevealClip, motionRevealPadding, motionSceneShapeProblems, motionSceneToJson, motionShotAt, motionTrackKey, namedWithSuggestion, parseMotionScene, resolveMotionCamera, segmentProgress, segmentValue, shotShapeProblems, suggestName, toFlairEffectDocument, toFlairSvgMotion, toFlairVectorEffect, trackValue, withViewBox };