@vosjs/cli 0.21.0 → 0.22.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/README.md CHANGED
@@ -91,7 +91,7 @@ vos plan take --reuse # re-time that cut onto
91
91
 
92
92
  **A series shares its look by data.** `vos plan <take> --style <seed doc.json | vosId>` copies the seed's `zoomStyle`, `zoomParams`, `speedParams`, `tiltStyle`, `frame`, `cursor`, `cam` and `export` onto a new take (never its spans, overlays or audio) and re-plans the automatic spans under them. A seed that is a POSTER carries its layout too: its card placement, its `stage-*` clips by id (with the release's words from LAUNCH.md's `headline` and `kicker` roles, BRAND.md's `wordmark`, or `--headline` and `--kicker` patched in; the brand's mark from BRAND.md `logoUrl` fetched into `<take>/brand/` for `stage-mark`), its rest lean, its trailing freeze. What could not follow is said in words.
93
93
 
94
- **The cut's motion is the document's, in one vocabulary.** Every visual thing carries `anim` (`enter`, `exit`, `idle`; a kind, or a step with its seconds and, for words, `unit`, `direction`, `stagger`): the card (`frame.anim`, entering by `tilt-in`, `pull-out`, `rise` or `fade` and leaving by `recede` or `fade`), a text, image or video clip, a prop. The output lasts until the last clip ends; past its footage the card holds its last frame at the pose its exit settled into. A FREEZE (`freeze: [{id, at, seconds}]`, a source moment held for output seconds, beside the speed spans on the retime lane) freezes the footage anywhere, the middle of the take included; a segment's older `hold` is read as a freeze at its end. There is no end-card field and no entrance field. A COMPONENT is a TEMPLATE: a plain take on a shelf whose clips carry stable ids, laid onto a take at an anchor and stamped `from` with where its clips came from. A fresh `vos plan` proposes the card's enter, the templates the recipe names (`LAUNCH.md` `with: <ref>[@end|@start|@step:<id>|@<seconds>], …` or `--with`, repeatable; `endCard: <ref>` names one at the end), the house end card when `endCard` is on or absent (the headline, the release line, the wordmark and BRAND.md's mark as clips after the footage over a receding card, `from: endcard`), a caption per `actions.json` step, a music bed and click sounds, from LAUNCH.md's `entrance`, `endCard`, `with`, `captions`, `music` and `clicks` roles (or the flags), as data in doc.json with stable ids (`bed`, `click-<n>`, `caption-<step>`), so the studio shows what the kit will render; a refresh never re-proposes (a deleted clip stays deleted) and `--motion` re-proposes on purpose, replacing only its own work. A document written in the older spellings (`frame.entrance`, `endCard`, a clip's `enter`/`exit`/`fx`, a prop's `animation`) is read into the vocabulary and `vos validate` says so.
94
+ **The cut's motion is the document's, in one vocabulary.** Every visual thing carries `anim` (`enter`, `exit`, `idle`; a kind, or a step with its seconds and, for words, `unit`, `direction`, `stagger`): the card (`frame.anim`, entering by `tilt-in`, `pull-out`, `rise` or `fade` and leaving by `recede` or `fade`), a text, image or video clip, a prop. The output lasts until the last clip ends; the card is on screen exactly while its clip runs (the footage, freezes included) and leaves at its end, so its `exit` plays over the clip's last seconds like every clip's; past the footage the clips play over the ground alone. A FREEZE (`freeze: [{id, at, seconds}]`, a source moment held for output seconds, beside the speed spans on the retime lane) freezes the footage anywhere, the middle of the take included; a segment's older `hold` is read as a freeze at its end. There is no end-card field and no entrance field. A COMPONENT is a TEMPLATE: a plain take on a shelf whose clips carry stable ids, laid onto a take at an anchor and stamped `from` with where its clips came from. A fresh `vos plan` proposes the card's enter, the templates the recipe names (`LAUNCH.md` `with: <ref>[@end|@start|@step:<id>|@<seconds>], …` or `--with`, repeatable; `endCard: <ref>` names one at the end), the house end card when `endCard` is on or absent (a freeze of the last frame for the card's seconds, the card receding over it, and the headline, the release line, the wordmark and BRAND.md's mark as clips over the freeze, every one `from: endcard`), a caption per `actions.json` step, a music bed and click sounds, from LAUNCH.md's `entrance`, `endCard`, `with`, `captions`, `music` and `clicks` roles (or the flags), as data in doc.json with stable ids (`bed`, `click-<n>`, `caption-<step>`), so the studio shows what the kit will render; a refresh never re-proposes (a deleted clip stays deleted) and `--motion` re-proposes on purpose, replacing only its own work. A document written in the older spellings (`frame.entrance`, `endCard`, a clip's `enter`/`exit`/`fx`, a prop's `animation`) is read into the vocabulary and `vos validate` says so.
95
95
 
96
96
  **A fresh take opens on a backdrop.** `create`, `record` and `plan` put the first ready loop from `GET /api/backdrops` behind the card (its ground colour as `frame.background`); `--background <slug|url|none>` overrides it, and offline the frame stays bare with a note.
97
97
 
@@ -3429,7 +3429,6 @@ function pickMoments(measured, opts = {}) {
3429
3429
  // src/plugin/motionPlan.ts
3430
3430
  import {
3431
3431
  END_CARD_FROM,
3432
- END_CARD_RECEDE,
3433
3432
  END_CARD_SECONDS,
3434
3433
  applyTemplate,
3435
3434
  docOutputDuration,
@@ -3556,13 +3555,32 @@ function proposeMotion(input, opts) {
3556
3555
  if (sub && sub !== headline) card.sub = sub;
3557
3556
  if (brand) card.wordmark = brand;
3558
3557
  if (opts.mark) card.mark = opts.mark;
3558
+ const endStart = outputLength(doc);
3559
+ const lastSeg = doc.segments.at(-1);
3560
+ if (lastSeg) {
3561
+ const own = (doc.freeze ?? []).filter(
3562
+ (f) => Math.abs(f.at - lastSeg.out) > 1e-9
3563
+ );
3564
+ const taken = new Set(own.map((f) => f.id));
3565
+ let n = 0;
3566
+ while (taken.has(`f${n}`)) n++;
3567
+ doc.freeze = [
3568
+ ...own,
3569
+ {
3570
+ id: `f${n}`,
3571
+ at: lastSeg.out,
3572
+ seconds: END_CARD_SECONDS,
3573
+ from: END_CARD_FROM
3574
+ }
3575
+ ].sort((a, b) => a.at - b.at);
3576
+ }
3559
3577
  doc.overlays = [
3560
3578
  ...doc.overlays ?? [],
3561
- ...endCardClips(card, doc, outputLength(doc))
3579
+ ...endCardClips(card, doc, endStart)
3562
3580
  ];
3563
3581
  doc.frame.anim = {
3564
3582
  ...doc.frame.anim ?? {},
3565
- exit: { kind: "recede", seconds: END_CARD_RECEDE }
3583
+ exit: { kind: "recede", seconds: END_CARD_SECONDS }
3566
3584
  };
3567
3585
  notes.push("end card");
3568
3586
  } else {
@@ -3623,7 +3641,7 @@ function proposeMotion(input, opts) {
3623
3641
  fadeIn: 0.6,
3624
3642
  fadeOut,
3625
3643
  loop: track.duration < length,
3626
- loopLen: track.duration < length ? length : void 0,
3644
+ loopLen: track.duration < length ? Math.round(length * 1e3) / 1e3 : void 0,
3627
3645
  duck: hasMic
3628
3646
  });
3629
3647
  notes.push(`bed ${track.slug}`);
@@ -3679,6 +3697,11 @@ function destinationMechanics(d, doc) {
3679
3697
  notes.push(loop ? "no template clips, no captions" : "no captions");
3680
3698
  }
3681
3699
  }
3700
+ if (loop) {
3701
+ const keptFreezes = (doc.freeze ?? []).filter((f) => !f.from);
3702
+ if (keptFreezes.length !== (doc.freeze ?? []).length)
3703
+ set.push(`freeze=${JSON.stringify(keptFreezes)}`);
3704
+ }
3682
3705
  if (portrait) {
3683
3706
  set.push("frame.fit=cover");
3684
3707
  set.push('frame.inset={"left":0.06,"right":0.06,"top":0.17,"bottom":0.17}');
@@ -3709,11 +3732,13 @@ function isLightHexGround(hex2) {
3709
3732
  import { existsSync as existsSync5 } from "fs";
3710
3733
  import { readFile as readFile5 } from "fs/promises";
3711
3734
  import { isAbsolute, join as join6, resolve as resolve3 } from "path";
3735
+ import { totalDuration } from "@vosjs/timeline";
3712
3736
  import {
3713
3737
  computeCardLayout,
3714
3738
  docRestTime,
3715
3739
  migrateHostedDoc,
3716
3740
  overlayRect,
3741
+ ratedSegments as ratedSegments5,
3717
3742
  resolveOverlayStyle
3718
3743
  } from "@vosjs/studio-core";
3719
3744
 
@@ -3955,7 +3980,9 @@ async function findPosterDocs(takeDir, launchRoles) {
3955
3980
  function posterStillTime(doc, duration) {
3956
3981
  const rest = docRestTime(doc);
3957
3982
  if (rest != null) return rest;
3958
- return Math.max(0, duration - 1 / 30);
3983
+ const footage = totalDuration(ratedSegments5(doc));
3984
+ const end = footage > 0 ? Math.min(duration, footage) : duration;
3985
+ return Math.max(0, end - 1 / 30);
3959
3986
  }
3960
3987
  var DESIGN_H = 1080;
3961
3988
  function designFrame(px) {
@@ -10137,4 +10164,4 @@ export {
10137
10164
  convertAgentBrowser,
10138
10165
  run
10139
10166
  };
10140
- //# sourceMappingURL=chunk-4IWHZBIY.js.map
10167
+ //# sourceMappingURL=chunk-CRZTGWF3.js.map