@vosjs/cli 0.18.0 → 0.20.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.
@@ -30,10 +30,7 @@
30
30
  "description": "Kept SOURCE-time footage spans; the output timeline is their concatenation. Trim dead time by shrinking spans. Empty array = untrimmed. Never carry `rate` here (only lowered/derived segments do).",
31
31
  "items": {
32
32
  "type": "object",
33
- "required": [
34
- "in",
35
- "out"
36
- ],
33
+ "required": ["in", "out"],
37
34
  "properties": {
38
35
  "in": {
39
36
  "type": "number",
@@ -63,14 +60,7 @@
63
60
  "description": "Zoom regions (SOURCE time, footage-anchored, non-overlapping). The camera ramps in around `in`, holds until `out`, ramps out — or pans straight to the next span when the gap is short. Planner-made spans have source:'auto' (replaced on re-plan); set source:'manual' on any span you add or edit so it survives.",
64
61
  "items": {
65
62
  "type": "object",
66
- "required": [
67
- "id",
68
- "in",
69
- "out",
70
- "level",
71
- "cx",
72
- "cy"
73
- ],
63
+ "required": ["id", "in", "out", "level", "cx", "cy"],
74
64
  "properties": {
75
65
  "id": {
76
66
  "type": "string",
@@ -108,47 +98,28 @@
108
98
  "description": "arrival ease (@vosjs/timeline EASINGS name). Absent = the style's default ramp."
109
99
  },
110
100
  "transition": {
111
- "enum": [
112
- "instant",
113
- "fast",
114
- "smooth",
115
- "slow"
116
- ],
101
+ "enum": ["instant", "fast", "smooth", "slow"],
117
102
  "description": "transition SPEED for this span's ramps (in, out, and a chained pan arriving here). Absent = 'smooth' (the camera style's stock motion); 'instant' = hard cut; 'fast' ≈ half, 'slow' ≈ 1.6×."
118
103
  },
119
104
  "focusMode": {
120
- "enum": [
121
- "manual",
122
- "auto"
123
- ],
105
+ "enum": ["manual", "auto"],
124
106
  "description": "'auto' = camera follows the cursor through the span (needs a cursor track); absent/'manual' = fixed cx/cy"
125
107
  },
126
108
  "source": {
127
- "enum": [
128
- "auto",
129
- "manual"
130
- ],
109
+ "enum": ["auto", "manual"],
131
110
  "description": "'auto' = planner suggestion, replaced by re-plan; 'manual' = user/agent work, always preserved"
132
111
  },
133
112
  "anchor": {
134
113
  "type": "object",
135
- "required": [
136
- "step"
137
- ],
114
+ "required": ["step"],
138
115
  "additionalProperties": false,
139
116
  "properties": {
140
117
  "step": {
141
- "type": [
142
- "string",
143
- "integer"
144
- ],
118
+ "type": ["string", "integer"],
145
119
  "description": "the actions.json step: its `id` when it has one, else its record-time index (meta.steps)"
146
120
  },
147
121
  "at": {
148
- "enum": [
149
- "start",
150
- "end"
151
- ],
122
+ "enum": ["start", "end"],
152
123
  "description": "which edge of the step the span's `in` is measured from (default start)"
153
124
  },
154
125
  "offset": {
@@ -190,13 +161,7 @@
190
161
  "description": "Card tilt regions (SOURCE time, footage-anchored, non-overlapping). While a span is active the 3D card leans to its pose; between spans it returns to FLAT (there is no static card pose: a lean is a moment on the timeline). Ramps ~0.9s in / ~0.8s out; spans ≤ ~1.35s apart swing pose-to-pose. Direction: +rx brings the TOP edge toward the camera, +ry the LEFT edge — to lean toward a right-side focus use NEGATIVE ry. Wand-made spans have source:'auto' (replaced when Dynamic tilt re-runs); set source:'manual' on spans you add or edit.",
191
162
  "items": {
192
163
  "type": "object",
193
- "required": [
194
- "id",
195
- "in",
196
- "out",
197
- "rx",
198
- "ry"
199
- ],
164
+ "required": ["id", "in", "out", "rx", "ry"],
200
165
  "properties": {
201
166
  "id": {
202
167
  "type": "string",
@@ -228,40 +193,24 @@
228
193
  "description": "arrival ease (@vosjs/timeline EASINGS name). Absent = the house tilt ease."
229
194
  },
230
195
  "transition": {
231
- "enum": [
232
- "instant",
233
- "fast",
234
- "smooth",
235
- "slow"
236
- ],
196
+ "enum": ["instant", "fast", "smooth", "slow"],
237
197
  "description": "transition SPEED for this span's ramps. Absent = 'smooth' (~0.9s in / ~0.8s out); 'instant' snaps the card to the pose."
238
198
  },
239
199
  "source": {
240
- "enum": [
241
- "auto",
242
- "manual"
243
- ],
200
+ "enum": ["auto", "manual"],
244
201
  "description": "'auto' = Dynamic-tilt suggestion, replaced by re-plan; 'manual' = user/agent work, always preserved"
245
202
  },
246
203
  "anchor": {
247
204
  "type": "object",
248
- "required": [
249
- "step"
250
- ],
205
+ "required": ["step"],
251
206
  "additionalProperties": false,
252
207
  "properties": {
253
208
  "step": {
254
- "type": [
255
- "string",
256
- "integer"
257
- ],
209
+ "type": ["string", "integer"],
258
210
  "description": "the actions.json step: its `id` when it has one, else its record-time index (meta.steps)"
259
211
  },
260
212
  "at": {
261
- "enum": [
262
- "start",
263
- "end"
264
- ],
213
+ "enum": ["start", "end"],
265
214
  "description": "which edge of the step the span's `in` is measured from (default start)"
266
215
  },
267
216
  "offset": {
@@ -276,12 +225,7 @@
276
225
  }
277
226
  },
278
227
  "tiltStyle": {
279
- "enum": [
280
- "off",
281
- "subtle",
282
- "medium",
283
- "strong"
284
- ],
228
+ "enum": ["off", "subtle", "medium", "strong"],
285
229
  "description": "Dynamic-tilt wand intensity: derives source:'auto' tilt spans FROM the zoom spans, leaning toward each zoom's focus (max 5/9/14° per axis). 'off'/absent = no auto tilt; manual spans work either way."
286
230
  },
287
231
  "rejected": {
@@ -289,23 +233,14 @@
289
233
  "description": "Deleted planner proposals, kept so no re-plan proposes them again (\"not this one\"). Each entry is the lane and the SOURCE extent of an auto span you deleted; a fresh proposal on that lane overlapping the extent by half of the shorter span is dropped by vos plan, by plan --reuse (which re-times these like manual spans) and by the studio's re-plans. Manual spans never need this: a re-plan keeps them by contract. The renderer never reads it.",
290
234
  "items": {
291
235
  "type": "object",
292
- "required": [
293
- "id",
294
- "lane",
295
- "in",
296
- "out"
297
- ],
236
+ "required": ["id", "lane", "in", "out"],
298
237
  "properties": {
299
238
  "id": {
300
239
  "type": "string",
301
240
  "description": "stable identity (`r{n}`)"
302
241
  },
303
242
  "lane": {
304
- "enum": [
305
- "zoom",
306
- "tilt",
307
- "speed"
308
- ],
243
+ "enum": ["zoom", "tilt", "speed"],
309
244
  "description": "which planner's proposals this rejects"
310
245
  },
311
246
  "in": {
@@ -320,23 +255,15 @@
320
255
  "anchor": {
321
256
  "type": "object",
322
257
  "description": "the deleted span's step anchor, when it had one, so a re-record re-times the rejection the same way",
323
- "required": [
324
- "step"
325
- ],
258
+ "required": ["step"],
326
259
  "additionalProperties": false,
327
260
  "properties": {
328
261
  "step": {
329
- "type": [
330
- "string",
331
- "integer"
332
- ],
262
+ "type": ["string", "integer"],
333
263
  "description": "the actions.json step: its `id` when it has one, else its record-time index (meta.steps)"
334
264
  },
335
265
  "at": {
336
- "enum": [
337
- "start",
338
- "end"
339
- ],
266
+ "enum": ["start", "end"],
340
267
  "description": "which edge of the step the span's `in` is measured from (default start)"
341
268
  },
342
269
  "offset": {
@@ -383,11 +310,7 @@
383
310
  "description": "Animated cam layout (SOURCE time, footage-anchored, non-overlapping): while a span is active the webcam bubble holds its pose; outside spans it rests at doc.cam; spans ≤ ~1.2s apart in output time morph pose-to-pose. Ramps ~0.65s, settled at span start (a cam move frames what follows). Absent pose fields inherit the rest pose, so a span may move without resizing. Only renders when the take has a cam track (source.camKey).",
384
311
  "items": {
385
312
  "type": "object",
386
- "required": [
387
- "id",
388
- "in",
389
- "out"
390
- ],
313
+ "required": ["id", "in", "out"],
391
314
  "properties": {
392
315
  "id": {
393
316
  "type": "string",
@@ -425,20 +348,12 @@
425
348
  "description": "arrival ease (@vosjs/timeline EASINGS name, e.g. 'power2.out'). Absent = the house settle ease."
426
349
  },
427
350
  "transition": {
428
- "enum": [
429
- "instant",
430
- "fast",
431
- "smooth",
432
- "slow"
433
- ],
351
+ "enum": ["instant", "fast", "smooth", "slow"],
434
352
  "description": "transition SPEED for this span's morphs. Absent = 'smooth' (~0.65s); 'instant' jump-cuts the bubble to its pose (the layout-cut)."
435
353
  },
436
354
  "source": {
437
355
  "type": "string",
438
- "enum": [
439
- "auto",
440
- "manual"
441
- ],
356
+ "enum": ["auto", "manual"],
442
357
  "description": "reserved for a future planner; spans you add or edit are 'manual'"
443
358
  }
444
359
  }
@@ -454,22 +369,12 @@
454
369
  "description": "CSS background (gradient/color) — the underlay/fallback, always painted under backgroundMedia"
455
370
  },
456
371
  "backgroundMedia": {
457
- "type": [
458
- "object",
459
- "null"
460
- ],
372
+ "type": ["object", "null"],
461
373
  "description": "Optional media layer drawn OVER the CSS background and UNDER the card: a vos animation baked to a seamless loop, or a still image. Video time is OUTPUT-anchored modulo the loop (bgT = t % duration) — trims/speed never retime it. Draws outside the zoom transform. `key` may be a file inside the take dir (e.g. \"/bg.webm\"), a public https://assets.vos.so/... loop, or an /api/... URL. Fail-open: if it can't load, the CSS background still shows.",
462
- "required": [
463
- "kind",
464
- "key",
465
- "dim"
466
- ],
374
+ "required": ["kind", "key", "dim"],
467
375
  "properties": {
468
376
  "kind": {
469
- "enum": [
470
- "video",
471
- "image"
472
- ]
377
+ "enum": ["video", "image"]
473
378
  },
474
379
  "key": {
475
380
  "type": "string",
@@ -583,10 +488,7 @@
583
488
  },
584
489
  "fit": {
585
490
  "type": "string",
586
- "enum": [
587
- "contain",
588
- "cover"
589
- ],
491
+ "enum": ["contain", "cover"],
590
492
  "description": "How footage meets an off-ratio frame. contain (default) letterboxes the card onto the background; cover makes the padded area the card and fills it with footage, cropped around `focus` — what a 440x280 store tile or a 2.5:1 marquee demands"
591
493
  },
592
494
  "focus": {
@@ -616,20 +518,17 @@
616
518
  }
617
519
  }
618
520
  },
521
+ "anim": {
522
+ "$ref": "#/definitions/anim",
523
+ "description": "how the card ENTERS at t = 0 (tilt-in | pull-out | rise | fade) and LEAVES when its footage ends (recede settles it smaller and dimmer behind whatever plays after it; fade takes it out). Past its footage the card holds its last frame at the exit's end pose. Absent = neither"
524
+ },
619
525
  "entrance": {
620
526
  "type": "object",
621
527
  "additionalProperties": false,
622
- "required": [
623
- "kind"
624
- ],
528
+ "required": ["kind"],
625
529
  "properties": {
626
530
  "kind": {
627
- "enum": [
628
- "tilt-in",
629
- "pull-out",
630
- "rise",
631
- "none"
632
- ]
531
+ "enum": ["tilt-in", "pull-out", "rise", "none"]
633
532
  },
634
533
  "seconds": {
635
534
  "type": "number",
@@ -637,13 +536,11 @@
637
536
  "maximum": 3
638
537
  }
639
538
  },
640
- "description": "how the card ENTERS at t = 0: tilt-in swings in from a perspective pose while the card settles up and in; pull-out opens zoomed in and pulls out; rise settles up and in flat. Lowered into the tilt or zoom track's head plus a card-pose track. Absent = none"
539
+ "description": "DEPRECATED: the card's anim.enter (migrated on read). How the card ENTERS at t = 0: tilt-in swings in from a perspective pose while the card settles up and in; pull-out opens zoomed in and pulls out; rise settles up and in flat"
641
540
  },
642
541
  "focusFollow": {
643
542
  "type": "string",
644
- "enum": [
645
- "camera"
646
- ],
543
+ "enum": ["camera"],
647
544
  "description": "under fit: cover, the crop follows the zoom track's focus (a 9:16 cut of a 16:9 take keeps the affordance in frame). Absent = focus, or the centre"
648
545
  }
649
546
  }
@@ -694,9 +591,7 @@
694
591
  "title": "Program document",
695
592
  "description": "A program plus the shared layers. `program.config` is THE user's config (functions as strings), untouched; the platform stores the COMPOSED config (the config plus the studio stack entry) as what plays. In a program DIRECTORY the CLI keeps `program.config` in config.json and omits it here. Minted only when a program gains a layer, a tween edit, a speed span or its own length; a bare program has no doc.",
696
593
  "type": "object",
697
- "required": [
698
- "program"
699
- ],
594
+ "required": ["program"],
700
595
  "additionalProperties": true,
701
596
  "properties": {
702
597
  "program": {
@@ -741,29 +636,90 @@
741
636
  }
742
637
  ],
743
638
  "definitions": {
639
+ "animStep": {
640
+ "oneOf": [
641
+ {
642
+ "enum": [
643
+ "none",
644
+ "fade",
645
+ "rise",
646
+ "pop",
647
+ "blur",
648
+ "typewriter",
649
+ "tilt-in",
650
+ "pull-out",
651
+ "recede"
652
+ ]
653
+ },
654
+ {
655
+ "type": "object",
656
+ "required": ["kind"],
657
+ "additionalProperties": false,
658
+ "properties": {
659
+ "kind": {
660
+ "enum": [
661
+ "none",
662
+ "fade",
663
+ "rise",
664
+ "pop",
665
+ "blur",
666
+ "typewriter",
667
+ "tilt-in",
668
+ "pull-out",
669
+ "recede"
670
+ ]
671
+ },
672
+ "seconds": {
673
+ "type": "number",
674
+ "minimum": 0.05,
675
+ "maximum": 3,
676
+ "description": "seconds the step takes (the house length when absent)"
677
+ },
678
+ "unit": {
679
+ "enum": ["block", "line", "word", "char"],
680
+ "description": "words only: the unit the step animates by"
681
+ },
682
+ "direction": {
683
+ "enum": ["forward", "reverse", "center"],
684
+ "description": "words only: the order units animate in"
685
+ },
686
+ "stagger": {
687
+ "type": "number",
688
+ "minimum": 0,
689
+ "maximum": 2,
690
+ "description": "words only: seconds between one unit's start and the next"
691
+ }
692
+ }
693
+ }
694
+ ],
695
+ "description": "one step of an animation: a bare kind is the house motion; an object adds its seconds and, for words, the per-unit grammar (a fade or rise that names its unit, stagger or seconds animates per unit)"
696
+ },
697
+ "anim": {
698
+ "type": "object",
699
+ "additionalProperties": false,
700
+ "properties": {
701
+ "enter": { "$ref": "#/definitions/animStep" },
702
+ "exit": { "$ref": "#/definitions/animStep" },
703
+ "idle": {
704
+ "enum": ["spin", "float", null],
705
+ "description": "props only: what it does while it stays"
706
+ }
707
+ },
708
+ "description": "THE animation vocabulary, one for every visual primitive. The card enters by tilt-in | pull-out | rise | fade and leaves by recede | fade; words enter by none | fade | rise | pop | blur | typewriter and leave by none | fade | rise; an image or video clip enters and leaves by none | fade | rise; a prop idles by spin | float. Absent = the house default; none = an explicit nothing"
709
+ },
744
710
  "overlays": {
745
711
  "type": "array",
746
712
  "description": "Compositor v2 (V1): screen-space text clips drawn ABOVE the card, outside the zoom transform. OUTPUT-anchored (start is final-cut seconds — trims/speed never retime a title). Optional; absent = none.",
747
713
  "items": {
748
714
  "type": "object",
749
- "required": [
750
- "id",
751
- "kind",
752
- "start",
753
- "duration",
754
- "transform"
755
- ],
715
+ "required": ["id", "kind", "start", "duration", "transform"],
756
716
  "additionalProperties": true,
757
717
  "properties": {
758
718
  "id": {
759
719
  "type": "string"
760
720
  },
761
721
  "kind": {
762
- "enum": [
763
- "text",
764
- "image",
765
- "video"
766
- ]
722
+ "enum": ["text", "image", "video"]
767
723
  },
768
724
  "start": {
769
725
  "type": "number",
@@ -779,11 +735,7 @@
779
735
  "description": "content; \\n breaks lines"
780
736
  },
781
737
  "preset": {
782
- "enum": [
783
- "title",
784
- "caption",
785
- "label"
786
- ],
738
+ "enum": ["title", "caption", "label"],
787
739
  "description": "house style: title = Lexend 600 64px, caption = Lexend 400 32px, label = JetBrains Mono 22px"
788
740
  },
789
741
  "size": {
@@ -811,11 +763,7 @@
811
763
  "description": "synthesized oblique (no italic files are hosted)"
812
764
  },
813
765
  "align": {
814
- "enum": [
815
- "left",
816
- "center",
817
- "right"
818
- ],
766
+ "enum": ["left", "center", "right"],
819
767
  "description": "multi-line alignment within the block (default center)"
820
768
  },
821
769
  "letterSpacing": {
@@ -832,10 +780,7 @@
832
780
  },
833
781
  "stroke": {
834
782
  "type": "object",
835
- "required": [
836
- "color",
837
- "width"
838
- ],
783
+ "required": ["color", "width"],
839
784
  "additionalProperties": false,
840
785
  "description": "text outline drawn under the fill",
841
786
  "properties": {
@@ -852,9 +797,7 @@
852
797
  },
853
798
  "box": {
854
799
  "type": "object",
855
- "required": [
856
- "color"
857
- ],
800
+ "required": ["color"],
858
801
  "additionalProperties": false,
859
802
  "description": "background pill behind the text block (text only; absent = none). Paddings/radius are EMs of the resolved font size",
860
803
  "properties": {
@@ -889,10 +832,7 @@
889
832
  },
890
833
  "transform": {
891
834
  "type": "object",
892
- "required": [
893
- "x",
894
- "y"
895
- ],
835
+ "required": ["x", "y"],
896
836
  "properties": {
897
837
  "x": {
898
838
  "type": "number",
@@ -927,11 +867,7 @@
927
867
  "shadow": {
928
868
  "oneOf": [
929
869
  {
930
- "enum": [
931
- "none",
932
- "soft",
933
- "strong"
934
- ]
870
+ "enum": ["none", "soft", "strong"]
935
871
  },
936
872
  {
937
873
  "type": "number",
@@ -943,10 +879,7 @@
943
879
  },
944
880
  "border": {
945
881
  "type": "object",
946
- "required": [
947
- "color",
948
- "width"
949
- ],
882
+ "required": ["color", "width"],
950
883
  "properties": {
951
884
  "color": {
952
885
  "type": "string",
@@ -961,30 +894,28 @@
961
894
  },
962
895
  "description": "media kinds: an outline stroke over the clipped media edge. Absent = none"
963
896
  },
897
+ "anim": {
898
+ "$ref": "#/definitions/anim",
899
+ "description": "how the clip enters and leaves. Words: enter by none | fade | rise (the block transition), or per unit by pop | blur | typewriter, or a fade | rise that names its unit, stagger or seconds; leave by none | fade | rise. Image and video clips: none | fade | rise both ways. Absent = a rise in, a fade out"
900
+ },
901
+ "from": {
902
+ "type": "string",
903
+ "description": "the template that placed this clip (its vos id): provenance the lowering never reads. A loop drops a template's clips, a re-apply replaces one template's and leaves the maker's"
904
+ },
964
905
  "enter": {
965
- "enum": [
966
- "none",
967
- "fade",
968
- "rise"
969
- ],
970
- "description": "default rise (~0.35s); IGNORED while fx is set (fx owns the entrance)"
906
+ "enum": ["none", "fade", "rise"],
907
+ "description": "DEPRECATED: anim.enter (migrated on read)"
971
908
  },
972
909
  "exit": {
973
- "enum": [
974
- "none",
975
- "fade",
976
- "rise"
977
- ],
978
- "description": "default fade (~0.35s); stays clip-level even with fx"
910
+ "enum": ["none", "fade", "rise"],
911
+ "description": "DEPRECATED: anim.exit (migrated on read)"
979
912
  },
980
913
  "motion": {
981
914
  "type": "array",
982
915
  "description": "Pose keyframes (MO): the clip's transform animated over CLIP-LOCAL time. Values interpolate across the gap between poses (~symmetric in-out ease; a hold is two identical poses); the base transform is the value before the first pose, and absent pose fields inherit it. Pure f(t) — scrub, export and chunked server renders agree.",
983
916
  "items": {
984
917
  "type": "object",
985
- "required": [
986
- "at"
987
- ],
918
+ "required": ["at"],
988
919
  "properties": {
989
920
  "at": {
990
921
  "type": "number",
@@ -1030,30 +961,15 @@
1030
961
  "description": "text kind only: entrance animation evaluated per unit — pure f(t), segmentation baked at lowering (scrub/seek/server chunks agree by construction)",
1031
962
  "properties": {
1032
963
  "fx": {
1033
- "enum": [
1034
- "fade",
1035
- "rise",
1036
- "pop",
1037
- "blur",
1038
- "typewriter"
1039
- ],
964
+ "enum": ["fade", "rise", "pop", "blur", "typewriter"],
1040
965
  "description": "the entrance; typewriter is a step reveal by unit count"
1041
966
  },
1042
967
  "unit": {
1043
- "enum": [
1044
- "block",
1045
- "line",
1046
- "word",
1047
- "char"
1048
- ],
968
+ "enum": ["block", "line", "word", "char"],
1049
969
  "description": "what animates as one thing (default block — the whole text; char is grapheme-safe)"
1050
970
  },
1051
971
  "direction": {
1052
- "enum": [
1053
- "forward",
1054
- "reverse",
1055
- "center"
1056
- ],
972
+ "enum": ["forward", "reverse", "center"],
1057
973
  "description": "unit start order (default forward; center ripples outward)"
1058
974
  },
1059
975
  "stagger": {
@@ -1069,9 +985,7 @@
1069
985
  "description": "per-unit seconds (default 0.35); typewriter ignores it"
1070
986
  }
1071
987
  },
1072
- "required": [
1073
- "fx"
1074
- ],
988
+ "required": ["fx"],
1075
989
  "additionalProperties": false
1076
990
  },
1077
991
  "key": {
@@ -1107,11 +1021,7 @@
1107
1021
  "description": "Compositor v2 (V3): world-space 3D props between the card and the overlays — the drafted V4 engine spec. Curated primitives, GLB models by key, and extruded 3D text (TX7).",
1108
1022
  "items": {
1109
1023
  "type": "object",
1110
- "required": [
1111
- "id",
1112
- "asset",
1113
- "transform3d"
1114
- ],
1024
+ "required": ["id", "asset", "transform3d"],
1115
1025
  "additionalProperties": true,
1116
1026
  "properties": {
1117
1027
  "id": {
@@ -1119,24 +1029,13 @@
1119
1029
  },
1120
1030
  "asset": {
1121
1031
  "type": "object",
1122
- "required": [
1123
- "kind"
1124
- ],
1032
+ "required": ["kind"],
1125
1033
  "properties": {
1126
1034
  "kind": {
1127
- "enum": [
1128
- "primitive",
1129
- "gltf",
1130
- "text3d"
1131
- ]
1035
+ "enum": ["primitive", "gltf", "text3d"]
1132
1036
  },
1133
1037
  "shape": {
1134
- "enum": [
1135
- "cube",
1136
- "sphere",
1137
- "torus",
1138
- "knot"
1139
- ],
1038
+ "enum": ["cube", "sphere", "torus", "knot"],
1140
1039
  "description": "primitive kind"
1141
1040
  },
1142
1041
  "color": {
@@ -1157,12 +1056,7 @@
1157
1056
  "description": "text3d kind: a 3D typeface catalog slug or family name (e.g. \"bebas-neue\", \"Playfair Display\"); unknown names fall back to the house face"
1158
1057
  },
1159
1058
  "material": {
1160
- "enum": [
1161
- "standard",
1162
- "metal",
1163
- "glass",
1164
- "neon"
1165
- ],
1059
+ "enum": ["standard", "metal", "glass", "neon"],
1166
1060
  "description": "text3d kind: fleet-audited material preset (single-sided, no dispersion; default standard)"
1167
1061
  },
1168
1062
  "depth": {
@@ -1194,10 +1088,7 @@
1194
1088
  },
1195
1089
  "transform3d": {
1196
1090
  "type": "object",
1197
- "required": [
1198
- "x",
1199
- "y"
1200
- ],
1091
+ "required": ["x", "y"],
1201
1092
  "properties": {
1202
1093
  "x": {
1203
1094
  "type": "number",
@@ -1231,22 +1122,24 @@
1231
1122
  },
1232
1123
  "additionalProperties": true
1233
1124
  },
1125
+ "anim": {
1126
+ "$ref": "#/definitions/anim",
1127
+ "description": "what the prop does while it stays (idle: spin | float, composing ADDITIVELY on top of motion poses)"
1128
+ },
1129
+ "from": {
1130
+ "type": "string",
1131
+ "description": "the template that placed this prop (see the overlay clip's from)"
1132
+ },
1234
1133
  "animation": {
1235
- "enum": [
1236
- "spin",
1237
- "float",
1238
- null
1239
- ],
1240
- "description": "curated deterministic motion — composes ADDITIVELY on top of motion poses"
1134
+ "enum": ["spin", "float", null],
1135
+ "description": "DEPRECATED: anim.idle (migrated on read)"
1241
1136
  },
1242
1137
  "motion": {
1243
1138
  "type": "array",
1244
1139
  "description": "Pose keyframes (MO) over transform3d, CLIP-LOCAL time (seconds from span.start; 0 when span-less). Values interpolate across the gap between poses; absent pose fields inherit transform3d; spin/float presets compose additively. Pure f(t).",
1245
1140
  "items": {
1246
1141
  "type": "object",
1247
- "required": [
1248
- "at"
1249
- ],
1142
+ "required": ["at"],
1250
1143
  "properties": {
1251
1144
  "at": {
1252
1145
  "type": "number",
@@ -1320,6 +1213,10 @@
1320
1213
  "type": "string",
1321
1214
  "description": "audio file URL (blob or asset URL)"
1322
1215
  },
1216
+ "from": {
1217
+ "type": "string",
1218
+ "description": "the template that placed this clip (see the overlay clip's from)"
1219
+ },
1323
1220
  "name": {
1324
1221
  "type": "string"
1325
1222
  },
@@ -1370,31 +1267,17 @@
1370
1267
  },
1371
1268
  "export": {
1372
1269
  "type": "object",
1373
- "required": [
1374
- "resolution",
1375
- "fps",
1376
- "format"
1377
- ],
1270
+ "required": ["resolution", "fps", "format"],
1378
1271
  "properties": {
1379
1272
  "resolution": {
1380
- "enum": [
1381
- "720p",
1382
- "1080p",
1383
- "2k",
1384
- "4k"
1385
- ],
1273
+ "enum": ["720p", "1080p", "2k", "4k"],
1386
1274
  "description": "names the SHORT edge. Presets above the footage's capture width upscale (validate warns)."
1387
1275
  },
1388
1276
  "fps": {
1389
- "enum": [
1390
- 30,
1391
- 60
1392
- ]
1277
+ "enum": [30, 60]
1393
1278
  },
1394
1279
  "format": {
1395
- "enum": [
1396
- "mp4"
1397
- ],
1280
+ "enum": ["mp4"],
1398
1281
  "description": "doc-level default; the CLI render's --format flag (webm|mp4) overrides"
1399
1282
  }
1400
1283
  },
@@ -1405,12 +1288,7 @@
1405
1288
  "description": "Speed-change spans (SOURCE time, footage-anchored, non-overlapping). Absent = all 1×. The planner proposes source:'auto' spans for typing passages, scroll runs and idle gaps (ids s0…); set source:'manual' on spans you add or edit so they survive a re-plan (absent source also counts as manual).",
1406
1289
  "items": {
1407
1290
  "type": "object",
1408
- "required": [
1409
- "id",
1410
- "in",
1411
- "out",
1412
- "rate"
1413
- ],
1291
+ "required": ["id", "in", "out", "rate"],
1414
1292
  "properties": {
1415
1293
  "id": {
1416
1294
  "type": "string"
@@ -1429,31 +1307,20 @@
1429
1307
  "description": "playback rate: 2 = twice as fast"
1430
1308
  },
1431
1309
  "source": {
1432
- "enum": [
1433
- "auto",
1434
- "manual"
1435
- ],
1310
+ "enum": ["auto", "manual"],
1436
1311
  "description": "'auto' = planner suggestion (typing/scroll/idle), replaced by re-plan; 'manual' (or absent) = user/agent work, always preserved"
1437
1312
  },
1438
1313
  "anchor": {
1439
1314
  "type": "object",
1440
- "required": [
1441
- "step"
1442
- ],
1315
+ "required": ["step"],
1443
1316
  "additionalProperties": false,
1444
1317
  "properties": {
1445
1318
  "step": {
1446
- "type": [
1447
- "string",
1448
- "integer"
1449
- ],
1319
+ "type": ["string", "integer"],
1450
1320
  "description": "the actions.json step: its `id` when it has one, else its record-time index (meta.steps)"
1451
1321
  },
1452
1322
  "at": {
1453
- "enum": [
1454
- "start",
1455
- "end"
1456
- ],
1323
+ "enum": ["start", "end"],
1457
1324
  "description": "which edge of the step the span's `in` is measured from (default start)"
1458
1325
  },
1459
1326
  "offset": {