@vosjs/cli 0.10.0 → 0.11.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.
@@ -1609,30 +1609,107 @@ async function renderAnimation(browser, opts) {
1609
1609
  }
1610
1610
  }
1611
1611
 
1612
- // src/plugin/docOverride.ts
1612
+ // src/plugin/backdrops.ts
1613
+ async function fetchBackdropSet(origin) {
1614
+ const base = origin.replace(/\/+$/, "");
1615
+ const res = await fetch(`${base}/api/backdrops`);
1616
+ if (!res.ok)
1617
+ throw new Error(`GET ${base}/api/backdrops answered ${res.status}`);
1618
+ const body = await res.json();
1619
+ return Array.isArray(body.backdrops) ? body.backdrops : [];
1620
+ }
1621
+ function rowBackdrop(row) {
1622
+ const key = row.urls["1080p"];
1623
+ if (!key) return null;
1624
+ return {
1625
+ slug: row.slug,
1626
+ key,
1627
+ kind: "video",
1628
+ duration: row.duration,
1629
+ ...row.urls.poster ? { poster: row.urls.poster } : {},
1630
+ ...row.ground ? { ground: row.ground } : {}
1631
+ };
1632
+ }
1633
+ function houseBackdrop(rows) {
1634
+ for (const row of rows) {
1635
+ const b = rowBackdrop(row);
1636
+ if (b) return b;
1637
+ }
1638
+ return null;
1639
+ }
1640
+ function backdropBySlug(rows, slug) {
1641
+ const row = rows.find((r) => r.slug === slug);
1642
+ return row ? rowBackdrop(row) : null;
1643
+ }
1613
1644
  function isBackdropSlug(value) {
1614
1645
  return /^[a-z0-9][a-z0-9-]{0,49}$/.test(value) && value !== "none";
1615
1646
  }
1647
+ var VIDEO_EXT = /\.(webm|mp4|mov|m4v)(\?|#|$)/i;
1648
+ var IMAGE_EXT = /\.(jpe?g|png|webp|gif|avif)(\?|#|$)/i;
1649
+ function inferBackgroundKind(url) {
1650
+ if (IMAGE_EXT.test(url)) return "image";
1651
+ if (VIDEO_EXT.test(url)) return "video";
1652
+ return "video";
1653
+ }
1654
+ var BACKGROUND_DEFAULT_DURATION = 10;
1655
+ function backdropFromKey(key, duration) {
1656
+ const kind = inferBackgroundKind(key);
1657
+ return kind === "video" ? { key, kind, duration: duration ?? BACKGROUND_DEFAULT_DURATION } : { key, kind };
1658
+ }
1659
+ async function openingBackdrop(background, origin) {
1660
+ if (background === "none" || background === "") return { backdrop: null };
1661
+ if (background !== void 0 && !isBackdropSlug(background)) {
1662
+ return { backdrop: backdropFromKey(background) };
1663
+ }
1664
+ let rows;
1665
+ try {
1666
+ rows = await fetchBackdropSet(origin);
1667
+ } catch (err) {
1668
+ if (background !== void 0) {
1669
+ throw new UsageError(
1670
+ `--background "${background}" \u2014 could not read the backdrop set from ${origin}/api/backdrops (${err.message}); pass a URL instead`
1671
+ );
1672
+ }
1673
+ return {
1674
+ backdrop: null,
1675
+ note: `could not read the backdrop set from ${origin}/api/backdrops; the take opens on a flat ground (--background <slug|url> picks one later)`
1676
+ };
1677
+ }
1678
+ if (background !== void 0) {
1679
+ const hit = backdropBySlug(rows, background);
1680
+ if (!hit) {
1681
+ throw new UsageError(
1682
+ `--background "${background}" is not in the set (${rows.map((b) => b.slug).join(" | ") || "empty"}); GET ${origin}/api/backdrops lists it, or pass a URL`
1683
+ );
1684
+ }
1685
+ return { backdrop: hit };
1686
+ }
1687
+ const house = houseBackdrop(rows);
1688
+ return house ? { backdrop: house } : {
1689
+ backdrop: null,
1690
+ note: `the backdrop set at ${origin}/api/backdrops has no ready loop; the take opens on a flat ground`
1691
+ };
1692
+ }
1693
+
1694
+ // src/plugin/docOverride.ts
1616
1695
  async function resolveBackdropSlug(o) {
1617
1696
  if (!o.background || !isBackdropSlug(o.background)) return;
1618
1697
  const origin = (o.origin ?? "https://vos.so").replace(/\/+$/, "");
1619
- let list = [];
1698
+ let list;
1620
1699
  try {
1621
- const res = await fetch(`${origin}/api/backdrops`);
1622
- if (res.ok)
1623
- list = (await res.json()).backdrops;
1700
+ list = await fetchBackdropSet(origin);
1624
1701
  } catch {
1625
1702
  throw new UsageError(
1626
1703
  `--background "${o.background}" \u2014 could not read the backdrop set from ${origin}/api/backdrops; pass a URL instead`
1627
1704
  );
1628
1705
  }
1629
- const hit = list.find((b) => b.slug === o.background && b.urls["1080p"]);
1630
- if (!hit?.urls["1080p"]) {
1706
+ const hit = backdropBySlug(list, o.background);
1707
+ if (!hit) {
1631
1708
  throw new UsageError(
1632
1709
  `--background "${o.background}" is not in the set (${list.map((b) => b.slug).join(" | ") || "empty"}); GET ${origin}/api/backdrops lists it, or pass a URL`
1633
1710
  );
1634
1711
  }
1635
- o.background = hit.urls["1080p"];
1712
+ o.background = hit.key;
1636
1713
  o.backgroundDuration = hit.duration;
1637
1714
  }
1638
1715
  function hasOverrides(o) {
@@ -1650,9 +1727,6 @@ var FRAME_KINDS = {
1650
1727
  none: "none",
1651
1728
  hidden: "none"
1652
1729
  };
1653
- var BACKGROUND_DEFAULT_DURATION = 10;
1654
- var VIDEO_EXT = /\.(webm|mp4|mov|m4v)(\?|#|$)/i;
1655
- var IMAGE_EXT = /\.(jpe?g|png|webp|gif|avif)(\?|#|$)/i;
1656
1730
  function coerceValue(raw) {
1657
1731
  try {
1658
1732
  return JSON.parse(raw);
@@ -1712,11 +1786,6 @@ function setPath(root, path, value) {
1712
1786
  ;
1713
1787
  node[segs[segs.length - 1].key] = value;
1714
1788
  }
1715
- function inferBackgroundKind(url) {
1716
- if (IMAGE_EXT.test(url)) return "image";
1717
- if (VIDEO_EXT.test(url)) return "video";
1718
- return "video";
1719
- }
1720
1789
  function applyDocOverrides(doc, o) {
1721
1790
  const applied = [];
1722
1791
  const d = doc;
@@ -3673,12 +3742,14 @@ import { existsSync as existsSync6 } from "fs";
3673
3742
  import { readFile as readFile6 } from "fs/promises";
3674
3743
  import { join as join9 } from "path";
3675
3744
  import {
3745
+ DEFAULT_FRAME_STYLE,
3676
3746
  STYLE_FIELDS,
3677
3747
  copyStyle,
3678
3748
  isRejected,
3679
3749
  planAutoSpeed,
3680
3750
  planAutoZoom,
3681
- projectFromArtifact
3751
+ projectFromArtifact,
3752
+ withBackdrop
3682
3753
  } from "@vosjs/studio-core";
3683
3754
 
3684
3755
  // src/plugin/reuse.ts
@@ -3875,6 +3946,7 @@ async function planTake(dir, opts = {}) {
3875
3946
  const take = await loadTake(dir);
3876
3947
  const { meta, cursor } = take;
3877
3948
  const activity = await readDigestActivity(dir);
3949
+ const ingest = opts.backdrop ? { frame: withBackdrop(DEFAULT_FRAME_STYLE, opts.backdrop) } : {};
3878
3950
  let doc;
3879
3951
  let fresh;
3880
3952
  if (opts.reuse) {
@@ -3884,7 +3956,7 @@ async function planTake(dir, opts = {}) {
3884
3956
  cursor,
3885
3957
  meta
3886
3958
  };
3887
- doc = projectFromArtifact(artifact, RECORDING_NAME).doc;
3959
+ doc = projectFromArtifact(artifact, RECORDING_NAME, ingest).doc;
3888
3960
  doc = copyStyle(prev, doc);
3889
3961
  const rt = retimeCut(prev, meta.steps ?? [], meta.durationMs);
3890
3962
  doc.segments = rt.segments;
@@ -3943,7 +4015,7 @@ async function planTake(dir, opts = {}) {
3943
4015
  cursor,
3944
4016
  meta
3945
4017
  };
3946
- doc = projectFromArtifact(artifact, RECORDING_NAME).doc;
4018
+ doc = projectFromArtifact(artifact, RECORDING_NAME, ingest).doc;
3947
4019
  if (opts.style) doc = copyStyle(opts.style.doc, doc);
3948
4020
  doc.zoom = planAutoZoom(doc.source.cursor, {
3949
4021
  width: doc.source.meta.width,
@@ -3965,6 +4037,7 @@ async function planTake(dir, opts = {}) {
3965
4037
  zoomManual: doc.zoom.filter((z) => z.source === "manual").length,
3966
4038
  cursorKept: doc.source.cursor.length > 0,
3967
4039
  fresh,
4040
+ ...opts.backdrop ? { backdrop: doc.frame.backgroundMedia?.key ?? "none" } : {},
3968
4041
  ...opts.style ? {
3969
4042
  styleFrom: opts.style.from,
3970
4043
  styleFields: STYLE_FIELDS.filter(
@@ -6394,9 +6467,9 @@ var MULTI_FLAGS2 = /* @__PURE__ */ new Set(["set", "override"]);
6394
6467
  var HELP = `vos \u2014 record a browser flow, plan effects, render a product video; sync with vos.so
6395
6468
 
6396
6469
  Take pipeline
6397
- vos create --actions actions.json [--url <url>] [--out take] [out.webm] [--strict] [--max-duration <s>] [render flags] [--json]
6398
- vos record --actions actions.json [--url <url>] [--out take] [--strict] [--max-duration <s>] [--json]
6399
- vos plan <take> [--fresh] [--reuse [--from <doc.json>]] [--style <doc.json|vosId>] [--json]
6470
+ vos create --actions actions.json [--url <url>] [--out take] [out.webm] [--strict] [--max-duration <s>] [--background <slug|url|none>] [render flags] [--json]
6471
+ vos record --actions actions.json [--url <url>] [--out take] [--strict] [--max-duration <s>] [--background <slug|url|none>] [--json]
6472
+ vos plan <take> [--fresh] [--reuse [--from <doc.json>]] [--style <doc.json|vosId>] [--background <slug|url|none>] [--json]
6400
6473
  vos render <take> [out.webm] [--width] [--height] [--fps] [--format webm|mp4] [--parallel N] [--range a..b] [--draft] [--frame <kind>] [--background <url|slug>] [--set <path=value>]... [--json]
6401
6474
  vos frames <take> [--times 0,25%,50%,75%,100%] [--frame <t>] [--at-zooms] [--at-moments] [--size WxH] [--out dir] [--background <url|slug>] [--set <path=value>]... [--json]
6402
6475
  vos deliver <take> --to cws,producthunt,x,linkedin,og,github,youtube (or all) [--poster <config.json|vosId>] [--shot-time <t>] [--poster-time <t>] [--composed] [--set path=value] [--release v2.1] [--out dir] [--times a,b] [--range a..b] [--parallel N] [--json]
@@ -6563,7 +6636,9 @@ lint-gated, so a bad override fails like a bad doc.json):
6563
6636
  --set frame.padding=120 --set frame.browserBar.kind=mac-light)
6564
6637
  --frame <kind> (render) browser frame: macos|mac-dark|windows|windows-dark|minimal|none
6565
6638
  --background <url|slug> background media: a URL (kind inferred), or a backdrop slug from GET /api/backdrops
6566
- (.webm/.mp4 = video @10s loop, image otherwise); "none" clears
6639
+ (.webm/.mp4 = video @10s loop, image otherwise); "none" clears.
6640
+ On create/record/plan it is the frame a FRESH take opens on; absent, the set's
6641
+ first ready loop (the house backdrop the studio opens on), or a flat ground offline
6567
6642
  `;
6568
6643
  async function loadActions(file) {
6569
6644
  const raw = JSON.parse(await readFile9(file, "utf8"));
@@ -6584,6 +6659,17 @@ function strictReason(rec) {
6584
6659
  if (rec.capped) parts.push("stopped at --max-duration");
6585
6660
  return parts.join(" + ");
6586
6661
  }
6662
+ async function takeBackdrop(flags, r) {
6663
+ const { backdrop, note } = await openingBackdrop(
6664
+ strFlag(flags, "background"),
6665
+ platformOrigin({
6666
+ origin: strFlag(flags, "origin"),
6667
+ api: strFlag(flags, "api")
6668
+ })
6669
+ );
6670
+ if (note) r.log(`note: ${note}`);
6671
+ return backdrop;
6672
+ }
6587
6673
  async function cmdRecord(argv) {
6588
6674
  const { positionals, flags } = parseArgs(argv, BOOLEAN_FLAGS5);
6589
6675
  const actionsPath = strFlag(flags, "actions") ?? positionals[0];
@@ -6597,6 +6683,7 @@ async function cmdRecord(argv) {
6597
6683
  if (!url)
6598
6684
  throw new UsageError('no URL \u2014 set "url" in the actions file or pass --url');
6599
6685
  const outDir = resolve6(strFlag(flags, "out") ?? "take");
6686
+ const backdrop = await takeBackdrop(flags, r);
6600
6687
  if (existsSync10(join14(outDir, "meta.json"))) {
6601
6688
  const { prevDoc, kept } = await prepareReRecord(outDir);
6602
6689
  r.log(
@@ -6620,7 +6707,7 @@ async function cmdRecord(argv) {
6620
6707
  (p) => r.event({ event: "progress", phase: "encode", fraction: p })
6621
6708
  );
6622
6709
  r.event({ event: "phase", phase: "plan" });
6623
- const plan = await planTake(outDir);
6710
+ const plan = await planTake(outDir, { backdrop });
6624
6711
  const clicks = rec.events.filter((e) => e.type === "down").length;
6625
6712
  const strict = flags.strict === true;
6626
6713
  const strictFail = strict && (rec.skipped.length > 0 || rec.navTimeout || rec.capped);
@@ -6678,6 +6765,7 @@ async function cmdCreate2(argv) {
6678
6765
  if (!Number.isInteger(parallel) || parallel < 1 || parallel > 16) {
6679
6766
  throw new UsageError("--parallel expects an integer between 1 and 16");
6680
6767
  }
6768
+ const backdrop = await takeBackdrop(flags, r);
6681
6769
  if (existsSync10(join14(outDir, "meta.json"))) {
6682
6770
  const { prevDoc, kept } = await prepareReRecord(outDir);
6683
6771
  r.log(
@@ -6701,7 +6789,7 @@ async function cmdCreate2(argv) {
6701
6789
  (p) => r.event({ event: "progress", phase: "encode", fraction: p })
6702
6790
  );
6703
6791
  r.event({ event: "phase", phase: "plan" });
6704
- await planTake(outDir);
6792
+ await planTake(outDir, { backdrop });
6705
6793
  if (flags.strict === true && (rec.skipped.length > 0 || rec.navTimeout || rec.capped)) {
6706
6794
  r.done(
6707
6795
  {
@@ -6782,9 +6870,11 @@ async function cmdPlan(argv) {
6782
6870
  };
6783
6871
  }
6784
6872
  const style = await resolveStyleRef(flags);
6873
+ const backdrop = existsSync10(join14(dir, "doc.json")) ? null : await takeBackdrop(flags, r);
6785
6874
  const s = await planTake(dir, {
6786
6875
  ...style ? { style } : {},
6787
- ...reuse ? { reuse } : {}
6876
+ ...reuse ? { reuse } : {},
6877
+ backdrop
6788
6878
  });
6789
6879
  const reuseLines = s.reuse ? `
6790
6880
  reused ${s.reuse.from}: ${s.reuse.anchored} anchored + ${s.reuse.mapped} mapped span(s)` + (s.reuse.flagged.length ? `
@@ -6798,6 +6888,7 @@ async function cmdPlan(argv) {
6798
6888
  zoomAuto: s.zoomAuto,
6799
6889
  zoomManual: s.zoomManual,
6800
6890
  duration: s.doc.source.meta.durationMs / 1e3,
6891
+ ...s.backdrop !== void 0 ? { backdrop: s.backdrop } : {},
6801
6892
  ...s.styleFrom ? { styleFrom: s.styleFrom, styleFields: s.styleFields } : {},
6802
6893
  ...s.reuse ? { reuse: s.reuse } : {}
6803
6894
  },
@@ -7474,4 +7565,4 @@ export {
7474
7565
  convertAgentBrowser,
7475
7566
  run
7476
7567
  };
7477
- //# sourceMappingURL=chunk-CJPWZVHQ.js.map
7568
+ //# sourceMappingURL=chunk-3W3XD6HL.js.map