@vosjs/cli 0.22.0 → 0.23.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; 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.
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 END CARD when `endCard` is on or absent: the official `End card` template on vos.so (the platform's `Templates` project, promoted; any official template resolves by its title, `--with "End card"`, `--style "Split cover, landscape"`), laid at the end with the release's words and BRAND.md's mark, stamped `from: endcard`; offline, the house clips stand in (a freeze of the last frame for the card's seconds, the card receding over it, the headline, the release line, the wordmark and the mark as clips over the freeze), 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
 
@@ -9,7 +9,11 @@ import {
9
9
  import { mkdir as mkdir9, readFile as readFile14, rm as rm4 } from "fs/promises";
10
10
  import { existsSync as existsSync14 } from "fs";
11
11
  import { join as join17, resolve as resolve9 } from "path";
12
- import { docOutputDuration as docOutputDuration3, migrateHostedDoc as migrateHostedDoc5 } from "@vosjs/studio-core";
12
+ import {
13
+ END_CARD_FROM as END_CARD_FROM2,
14
+ docOutputDuration as docOutputDuration3,
15
+ migrateHostedDoc as migrateHostedDoc5
16
+ } from "@vosjs/studio-core";
13
17
 
14
18
  // src/plugin/args.ts
15
19
  var UsageError = class extends Error {
@@ -3518,9 +3522,15 @@ function proposeMotion(input, opts) {
3518
3522
  if (Object.keys(rest).length) doc.frame.anim = rest;
3519
3523
  else delete doc.frame.anim;
3520
3524
  }
3525
+ const endCardRole = launch.endCard;
3526
+ doc = dropTemplate(doc, END_CARD_FROM);
3527
+ const templates = (opts.templates ?? []).filter(
3528
+ (t) => !(t.from === END_CARD_FROM && off(endCardRole))
3529
+ );
3530
+ const namedEndCard = templates.some((t) => t.from === END_CARD_FROM);
3521
3531
  const tWords = templateWords(words);
3522
3532
  const keys = opts.mark ? { "stage-mark": opts.mark.key, "endcard-markimg": opts.mark.key } : void 0;
3523
- for (const t of opts.templates ?? []) {
3533
+ for (const t of templates) {
3524
3534
  const at2 = resolveAnchor(doc, t.at);
3525
3535
  if (at2 === null) {
3526
3536
  skipped.push(`${t.from}: ${anchorWord(t.at)} was not recorded`);
@@ -3536,15 +3546,13 @@ function proposeMotion(input, opts) {
3536
3546
  notes.push(`${t.from} at ${anchorWord(t.at)}`);
3537
3547
  for (const n of applied.notes) skipped.push(`${t.from}: ${n}`);
3538
3548
  }
3539
- const endCardRole = launch.endCard;
3540
- doc = dropTemplate(doc, END_CARD_FROM);
3541
3549
  if (off(endCardRole)) {
3542
3550
  if (doc.frame.anim?.exit !== void 0) {
3543
3551
  const { exit: _exit, ...rest } = doc.frame.anim;
3544
3552
  if (Object.keys(rest).length) doc.frame.anim = rest;
3545
3553
  else delete doc.frame.anim;
3546
3554
  }
3547
- } else if (onWord(endCardRole)) {
3555
+ } else if (onWord(endCardRole) && !namedEndCard) {
3548
3556
  const headline = (words.headline ?? "").trim();
3549
3557
  const brand = (words.brand ?? "").trim();
3550
3558
  const sub = [brand, (words.release ?? "").trim()].filter(Boolean).join(" ");
@@ -4572,6 +4580,21 @@ async function fetchBrandMarks(takeDir, roles) {
4572
4580
  return out;
4573
4581
  }
4574
4582
 
4583
+ // src/plugin/officialTemplates.ts
4584
+ var OFFICIAL_END_CARD_TITLE = "End card";
4585
+ function templateKey(s) {
4586
+ return s.toLowerCase().replace(/[^a-z0-9]+/g, "");
4587
+ }
4588
+ function findOfficialByRef(rows, ref) {
4589
+ const key = templateKey(ref);
4590
+ if (!key) return null;
4591
+ for (const row of rows) {
4592
+ if (templateKey(row.title) === key) return row;
4593
+ if (row.slug && templateKey(row.slug) === key) return row;
4594
+ }
4595
+ return null;
4596
+ }
4597
+
4575
4598
  // src/plugin/music.ts
4576
4599
  async function fetchMusicCatalog(origin) {
4577
4600
  const base = origin.replace(/\/+$/, "");
@@ -8951,17 +8974,18 @@ patched in from LAUNCH.md's headline and kicker roles or --headline and
8951
8974
  The cut's MOTION is the document's too, in ONE vocabulary: every visual
8952
8975
  thing (the card, a text, image or video clip, a prop) carries anim.enter,
8953
8976
  anim.exit and anim.idle; the output lasts until the last clip ends, and
8954
- past its footage the card holds its last frame at the pose its exit
8955
- settled into. There is no end-card field and no entrance field: a
8977
+ the card leaves with its clip (a freeze keeps it under what follows).
8978
+ There is no end-card field and no entrance field: a
8956
8979
  COMPONENT is a TEMPLATE, a plain take on a shelf whose clips carry stable
8957
8980
  ids, laid onto the take at an anchor (LAUNCH.md with: <ref>[@end|@start|
8958
8981
  @step:<id>|@<s>], comma list; --with the same, repeatable; endCard: <ref>
8959
8982
  names one at the end), its clips stamped with where they came from
8960
8983
  (from). plan proposes on a fresh plan (--motion re-proposes, replacing
8961
8984
  only its own work): the card's ENTER (tilt-in by default), the templates
8962
- named, the house END CARD when endCard is on or absent (clips after the
8963
- footage: the headline, the release line, the wordmark, the mark from
8964
- BRAND.md logoUrl, over a card that recedes; from: endcard), a CAPTION per
8985
+ named, the END CARD when endCard is on or absent (the official End card
8986
+ template on vos.so, laid at the end with the release's words, the mark
8987
+ from BRAND.md logoUrl and a freeze of the last frame under it, from:
8988
+ endcard; offline, the house clips stand in), a CAPTION per
8965
8989
  actions.json step at the step's moment, a music BED from LAUNCH.md's music
8966
8990
  role (a catalog slug or a mood) and a click sound on every press when the
8967
8991
  take has no mic. LAUNCH.md's entrance, endCard, captions, music and clicks
@@ -9440,6 +9464,24 @@ async function releaseInputs(dir, flags, r, withRefs = []) {
9440
9464
  templates.push({ from: found.from, doc: found.doc, at: at2 });
9441
9465
  r.log(`template: ${found.from}`);
9442
9466
  }
9467
+ if (!endCardRole || /^(on|yes|true)$/i.test(endCardRole.trim())) {
9468
+ const origin = platformOrigin({
9469
+ origin: strFlag(flags, "origin"),
9470
+ api: strFlag(flags, "api")
9471
+ });
9472
+ const official = await officialTemplate(origin, OFFICIAL_END_CARD_TITLE);
9473
+ if (official) {
9474
+ try {
9475
+ const found = await resolveDocRef(official.id, flags, "endCard");
9476
+ templates.push({ from: END_CARD_FROM2, doc: found.doc, at: "end" });
9477
+ r.log(`template: ${found.from} (the official end card)`);
9478
+ } catch {
9479
+ r.log(
9480
+ "end card: the official template is unreadable here; the house clips stand in"
9481
+ );
9482
+ }
9483
+ }
9484
+ }
9443
9485
  return {
9444
9486
  words,
9445
9487
  launchRoles,
@@ -9730,10 +9772,14 @@ async function resolveDocRef(ref, flags, what) {
9730
9772
  const key = resolveCredential(strFlag(flags, "key"));
9731
9773
  const meta = await apiJson(origin, `/api/vos/${styleRef}`, { key });
9732
9774
  const head = meta.body.vos?.currentVersionId;
9733
- if (meta.status !== 200 || !head)
9775
+ if (meta.status !== 200 || !head) {
9776
+ const official = await officialTemplate(origin, styleRef);
9777
+ if (official && official.id !== styleRef)
9778
+ return resolveDocRef(official.id, flags, what);
9734
9779
  throw new UsageError(
9735
- `${what}: ${styleRef} is neither a doc.json nor a vos I can read`
9780
+ `${what}: ${styleRef} is neither a doc.json, a vos I can read, nor an official template's title`
9736
9781
  );
9782
+ }
9737
9783
  const doc = await apiJson(
9738
9784
  origin,
9739
9785
  `/api/vos/${styleRef}/versions/${head}/doc`,
@@ -9746,6 +9792,16 @@ async function resolveDocRef(ref, flags, what) {
9746
9792
  doc: migrateHostedDoc5(doc.body)
9747
9793
  };
9748
9794
  }
9795
+ async function officialTemplate(origin, ref) {
9796
+ try {
9797
+ const list = await apiJson(origin, "/api/vos/official", {});
9798
+ if (list.status !== 200) return null;
9799
+ const rows = list.body.voses ?? [];
9800
+ return findOfficialByRef(rows, ref);
9801
+ } catch {
9802
+ return null;
9803
+ }
9804
+ }
9749
9805
  async function cmdDigest(argv) {
9750
9806
  const { positionals, flags } = parseArgs(argv, BOOLEAN_FLAGS5);
9751
9807
  const dir = positionals[0];
@@ -10164,4 +10220,4 @@ export {
10164
10220
  convertAgentBrowser,
10165
10221
  run
10166
10222
  };
10167
- //# sourceMappingURL=chunk-CRZTGWF3.js.map
10223
+ //# sourceMappingURL=chunk-SLCQCRNI.js.map