@vosjs/cli 0.37.0 → 0.39.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 +5 -2
- package/dist/{chunk-NHKHTIDR.js → chunk-FACYY7VV.js} +98 -6
- package/dist/chunk-FACYY7VV.js.map +1 -0
- package/dist/{chunk-DD6OXVFJ.js → chunk-RECQEBXN.js} +249 -59
- package/dist/chunk-RECQEBXN.js.map +1 -0
- package/dist/cli.js +22 -4
- package/dist/cli.js.map +1 -1
- package/dist/index.d.ts +24 -1
- package/dist/index.js +2 -2
- package/dist/run-BTBEJLWL.js +15 -0
- package/package.json +3 -3
- package/dist/chunk-DD6OXVFJ.js.map +0 -1
- package/dist/chunk-NHKHTIDR.js.map +0 -1
- package/dist/run-6T4NK5P2.js +0 -9
- /package/dist/{run-6T4NK5P2.js.map → run-BTBEJLWL.js.map} +0 -0
|
@@ -2,8 +2,10 @@
|
|
|
2
2
|
import {
|
|
3
3
|
BrowserUnavailableError,
|
|
4
4
|
directoryKind,
|
|
5
|
-
launchBrowser
|
|
6
|
-
|
|
5
|
+
launchBrowser,
|
|
6
|
+
noteGivenFlag,
|
|
7
|
+
trackFlags
|
|
8
|
+
} from "./chunk-FACYY7VV.js";
|
|
7
9
|
|
|
8
10
|
// src/plugin/run.ts
|
|
9
11
|
import { mkdir as mkdir10, readFile as readFile15, rm as rm5 } from "fs/promises";
|
|
@@ -52,7 +54,9 @@ function parseArgs(argv, booleanFlags, multiFlags = /* @__PURE__ */ new Set()) {
|
|
|
52
54
|
}
|
|
53
55
|
positionals.push(arg);
|
|
54
56
|
}
|
|
55
|
-
|
|
57
|
+
for (const name of Object.keys(flags)) noteGivenFlag(name);
|
|
58
|
+
for (const name of Object.keys(multi)) noteGivenFlag(name);
|
|
59
|
+
return { positionals, flags: trackFlags(flags), multi: trackFlags(multi) };
|
|
56
60
|
}
|
|
57
61
|
function numFlag(flags, name, fallback) {
|
|
58
62
|
const v = flags[name];
|
|
@@ -943,6 +947,17 @@ function lintDoc(docIn) {
|
|
|
943
947
|
}
|
|
944
948
|
const TRANSITIONS = ["none", "fade", "rise"];
|
|
945
949
|
const PRESETS = ["title", "caption", "label"];
|
|
950
|
+
let footageEnd = null;
|
|
951
|
+
if (recording && Array.isArray(doc.segments)) {
|
|
952
|
+
try {
|
|
953
|
+
footageEnd = ratedSegments(doc).reduce(
|
|
954
|
+
(a, s) => a + (s.out - s.in) / (s.rate ?? 1),
|
|
955
|
+
0
|
|
956
|
+
);
|
|
957
|
+
} catch {
|
|
958
|
+
footageEnd = null;
|
|
959
|
+
}
|
|
960
|
+
}
|
|
946
961
|
for (let i = 0; i < (overlays?.length ?? 0); i++) {
|
|
947
962
|
const o = overlays[i];
|
|
948
963
|
const name = `overlays[${i}]`;
|
|
@@ -960,6 +975,14 @@ function lintDoc(docIn) {
|
|
|
960
975
|
if (!isNum(o.duration) || o.duration <= 0) {
|
|
961
976
|
problems.push(`${name}.duration must be > 0 (seconds)`);
|
|
962
977
|
}
|
|
978
|
+
if (footageEnd !== null && footageEnd > 0 && isNum(o.start) && isNum(o.duration)) {
|
|
979
|
+
const over = o.start + o.duration - footageEnd;
|
|
980
|
+
if (over > 0.05) {
|
|
981
|
+
warnings.push(
|
|
982
|
+
o.start >= footageEnd ? `${name} starts at ${o.start.toFixed(2)}s, after the footage ends (${footageEnd.toFixed(2)}s): it plays on bare backdrop and lengthens the output by ${over.toFixed(2)}s. A trim moves the footage, never an OUTPUT-anchored layer: re-place it (vos callout with the same --id), or put a freeze under it` : `${name} ends ${over.toFixed(2)}s after the footage (${footageEnd.toFixed(2)}s): the output extends over bare backdrop. Shorten it, or put a freeze under it`
|
|
983
|
+
);
|
|
984
|
+
}
|
|
985
|
+
}
|
|
963
986
|
checkPin(o, name, docIn, recording, problems, warnings);
|
|
964
987
|
if (o.kind === "text") {
|
|
965
988
|
if (typeof o.text !== "string")
|
|
@@ -1635,7 +1658,7 @@ function framingWarnings(docIn, doc, zoom, overlays, warnings) {
|
|
|
1635
1658
|
if (pinned) {
|
|
1636
1659
|
if (pinned.clamped > 4) {
|
|
1637
1660
|
warnings.push(
|
|
1638
|
-
`overlays[${i}] is pinned ${pinned.side} of its referent but the frame has no room there \u2014 it sits ${Math.round(pinned.clamped)} design px off; a smaller box, another side, or a lower zoom level keeps it beside what it names`
|
|
1661
|
+
`overlays[${i}] is pinned ${pinned.side} of its referent but the frame has no room there \u2014 it sits ${Math.round(pinned.clamped)} design px off; a smaller box, another side, or a lower zoom level keeps it beside what it names. A pin resolves THROUGH the camera, so author the zoom first: a callout composed before it is measured against a frame the viewer never sees`
|
|
1639
1662
|
);
|
|
1640
1663
|
}
|
|
1641
1664
|
return;
|
|
@@ -1798,7 +1821,7 @@ import { pipeline } from "stream/promises";
|
|
|
1798
1821
|
|
|
1799
1822
|
// src/plugin/take.ts
|
|
1800
1823
|
import { mkdir, readFile, rename, rm, writeFile } from "fs/promises";
|
|
1801
|
-
import { existsSync } from "fs";
|
|
1824
|
+
import { existsSync, readdirSync, statSync } from "fs";
|
|
1802
1825
|
import { basename, join } from "path";
|
|
1803
1826
|
var RECORDING_NAME = "recording.webm";
|
|
1804
1827
|
function takeMediaFiles(dir, stem) {
|
|
@@ -1829,6 +1852,20 @@ async function ensureTakeDir(dir) {
|
|
|
1829
1852
|
await mkdir(p.framesDir, { recursive: true });
|
|
1830
1853
|
return p;
|
|
1831
1854
|
}
|
|
1855
|
+
async function dropScreencastFrames(p) {
|
|
1856
|
+
if (!existsSync(p.recording) || statSync(p.recording).size === 0) return 0;
|
|
1857
|
+
if (!existsSync(p.framesDir)) return 0;
|
|
1858
|
+
let freed = 0;
|
|
1859
|
+
for (const name of readdirSync(p.framesDir)) {
|
|
1860
|
+
try {
|
|
1861
|
+
freed += statSync(join(p.framesDir, name)).size;
|
|
1862
|
+
} catch {
|
|
1863
|
+
}
|
|
1864
|
+
}
|
|
1865
|
+
await rm(p.framesDir, { recursive: true, force: true });
|
|
1866
|
+
await rm(p.framesIndex, { force: true });
|
|
1867
|
+
return freed;
|
|
1868
|
+
}
|
|
1832
1869
|
async function prepareReRecord(dir) {
|
|
1833
1870
|
const p = takePaths(dir);
|
|
1834
1871
|
const kept = [];
|
|
@@ -2154,7 +2191,7 @@ import {
|
|
|
2154
2191
|
|
|
2155
2192
|
// src/plugin/server.ts
|
|
2156
2193
|
import { createServer } from "http";
|
|
2157
|
-
import { existsSync as existsSync3, readFileSync, statSync } from "fs";
|
|
2194
|
+
import { existsSync as existsSync3, readFileSync, statSync as statSync2 } from "fs";
|
|
2158
2195
|
import { writeFile as writeFile3 } from "fs/promises";
|
|
2159
2196
|
import { extname, join as join3 } from "path";
|
|
2160
2197
|
var MIME = {
|
|
@@ -2203,8 +2240,8 @@ function startTakeServer(rootDir, pages) {
|
|
|
2203
2240
|
return;
|
|
2204
2241
|
}
|
|
2205
2242
|
const file = join3(rootDir, decodeURIComponent(url.pathname));
|
|
2206
|
-
if (existsSync3(file) &&
|
|
2207
|
-
const size =
|
|
2243
|
+
if (existsSync3(file) && statSync2(file).isFile()) {
|
|
2244
|
+
const size = statSync2(file).size;
|
|
2208
2245
|
const buf = readFileSync(file);
|
|
2209
2246
|
const type = sniffMediaType(buf.subarray(0, MEDIA_HEAD_BYTES)) ?? MIME[extname(file)] ?? "application/octet-stream";
|
|
2210
2247
|
const range = req.headers.range;
|
|
@@ -5659,6 +5696,19 @@ function winRate(verdicts) {
|
|
|
5659
5696
|
};
|
|
5660
5697
|
}
|
|
5661
5698
|
|
|
5699
|
+
// src/plugin/fontName.ts
|
|
5700
|
+
import { findFontFamily as findFontFamily2 } from "@vosjs/shared";
|
|
5701
|
+
function catalogFamily(family) {
|
|
5702
|
+
const name = family.trim();
|
|
5703
|
+
if (findFontFamily2(name)) return name;
|
|
5704
|
+
const base = name.replace(/\s+Variable$/i, "");
|
|
5705
|
+
if (base !== name) {
|
|
5706
|
+
const hit = findFontFamily2(base);
|
|
5707
|
+
if (hit) return hit.family;
|
|
5708
|
+
}
|
|
5709
|
+
return family;
|
|
5710
|
+
}
|
|
5711
|
+
|
|
5662
5712
|
// src/plugin/callout.ts
|
|
5663
5713
|
import { existsSync as existsSync10 } from "fs";
|
|
5664
5714
|
import { readFile as readFile9 } from "fs/promises";
|
|
@@ -5668,6 +5718,7 @@ import {
|
|
|
5668
5718
|
CALLOUT_DEFAULT_BODY_PX,
|
|
5669
5719
|
CALLOUT_SHAPES,
|
|
5670
5720
|
calloutClip,
|
|
5721
|
+
calloutLook,
|
|
5671
5722
|
calloutGroundOf,
|
|
5672
5723
|
cameraModel as cameraModel2,
|
|
5673
5724
|
deltaE,
|
|
@@ -5686,10 +5737,11 @@ var CALLOUT_DELTA_E_PROBLEM = 8;
|
|
|
5686
5737
|
var CALLOUT_DELTA_E_WARNING = 16;
|
|
5687
5738
|
function registerFrom(roles, flags) {
|
|
5688
5739
|
const ground = flags.ground ?? roles?.bgA;
|
|
5689
|
-
const accent = flags.accent ?? roles?.accent;
|
|
5740
|
+
const accent = flags.accent ?? roles?.callout ?? roles?.accent;
|
|
5690
5741
|
if (!ground || !accent) return null;
|
|
5691
5742
|
if (!hexToRgb(ground) || !hexToRgb(accent)) return null;
|
|
5692
|
-
const
|
|
5743
|
+
const named = flags.face ?? roles?.fontBody;
|
|
5744
|
+
const face = named ? catalogFamily(named) : named;
|
|
5693
5745
|
return {
|
|
5694
5746
|
ground,
|
|
5695
5747
|
accent,
|
|
@@ -5755,16 +5807,28 @@ function composeCallout(doc, register, ask) {
|
|
|
5755
5807
|
let n = 2;
|
|
5756
5808
|
while (taken.has(id) && !ask.id)
|
|
5757
5809
|
id = `${ask.shape}-${ask.step ?? Math.round(window2.start * 10)}-${n++}`;
|
|
5810
|
+
const seen = appScaleAt(doc, window2.start + 0.35);
|
|
5811
|
+
const scale = ask.bodyPxGiven ? 1 : seen;
|
|
5812
|
+
const look = calloutLook(register, scale);
|
|
5758
5813
|
const clip = calloutClip(ask.shape, register, ask.words, {
|
|
5759
5814
|
id,
|
|
5760
5815
|
start: window2.start,
|
|
5761
5816
|
duration: window2.duration,
|
|
5762
|
-
scale
|
|
5817
|
+
scale,
|
|
5763
5818
|
pin,
|
|
5764
5819
|
// The fallback place, in the margin: the lower third.
|
|
5765
5820
|
at: { x: 0.5, y: 0.82 }
|
|
5766
5821
|
});
|
|
5767
|
-
return {
|
|
5822
|
+
return {
|
|
5823
|
+
clip,
|
|
5824
|
+
window: window2,
|
|
5825
|
+
sizes: {
|
|
5826
|
+
scale: +seen.toFixed(3),
|
|
5827
|
+
kickerPx: look.kickerPx,
|
|
5828
|
+
titlePx: look.titlePx,
|
|
5829
|
+
bodyPx: look.bodyPx
|
|
5830
|
+
}
|
|
5831
|
+
};
|
|
5768
5832
|
}
|
|
5769
5833
|
function isCalloutShape(s) {
|
|
5770
5834
|
return CALLOUT_SHAPES.includes(s);
|
|
@@ -6565,7 +6629,8 @@ function paceLine(r) {
|
|
|
6565
6629
|
}
|
|
6566
6630
|
|
|
6567
6631
|
// src/plugin/recorder.ts
|
|
6568
|
-
var
|
|
6632
|
+
var realSleep = (ms) => new Promise((r) => setTimeout(r, ms));
|
|
6633
|
+
var DRY_PAUSE_MS = 120;
|
|
6569
6634
|
function jpegDims(buf) {
|
|
6570
6635
|
let i = 2;
|
|
6571
6636
|
while (i < buf.length - 9) {
|
|
@@ -6580,12 +6645,15 @@ function jpegDims(buf) {
|
|
|
6580
6645
|
return null;
|
|
6581
6646
|
}
|
|
6582
6647
|
async function recordTake(browser, url, actions, paths, log, opts = {}) {
|
|
6583
|
-
const
|
|
6648
|
+
const dry = opts.dryRun === true;
|
|
6649
|
+
const sleep = dry ? (ms) => realSleep(Math.min(ms, DRY_PAUSE_MS)) : realSleep;
|
|
6650
|
+
const maxSeconds = dry ? Infinity : opts.maxDurationSeconds ?? Infinity;
|
|
6584
6651
|
const vw = actions.viewport?.width ?? 1280;
|
|
6585
6652
|
const vh = actions.viewport?.height ?? 720;
|
|
6586
6653
|
const context = await browser.newContext({
|
|
6587
6654
|
viewport: { width: vw, height: vh },
|
|
6588
|
-
deviceScaleFactor: 1
|
|
6655
|
+
deviceScaleFactor: 1,
|
|
6656
|
+
...opts.storageState ? { storageState: opts.storageState } : {}
|
|
6589
6657
|
});
|
|
6590
6658
|
const page = await context.newPage();
|
|
6591
6659
|
page.on("console", (m) => {
|
|
@@ -6602,22 +6670,24 @@ async function recordTake(browser, url, actions, paths, log, opts = {}) {
|
|
|
6602
6670
|
const frames = [];
|
|
6603
6671
|
let frameIdx = 0;
|
|
6604
6672
|
let t0 = 0;
|
|
6605
|
-
|
|
6606
|
-
|
|
6607
|
-
|
|
6608
|
-
|
|
6609
|
-
|
|
6610
|
-
|
|
6673
|
+
if (!dry)
|
|
6674
|
+
cdp.on("Page.screencastFrame", (ev) => {
|
|
6675
|
+
const tsMs = ev.metadata.timestamp ? ev.metadata.timestamp * 1e3 : Date.now();
|
|
6676
|
+
const file = `frame-${String(frameIdx++).padStart(5, "0")}.jpg`;
|
|
6677
|
+
writeFileSync2(join13(paths.framesDir, file), Buffer.from(ev.data, "base64"));
|
|
6678
|
+
frames.push({ file, tMs: Math.max(0, Math.round(tsMs - t0)) });
|
|
6679
|
+
cdp.send("Page.screencastFrameAck", { sessionId: ev.sessionId }).catch(() => {
|
|
6680
|
+
});
|
|
6611
6681
|
});
|
|
6612
|
-
});
|
|
6613
6682
|
t0 = Date.now();
|
|
6614
|
-
|
|
6615
|
-
|
|
6616
|
-
|
|
6617
|
-
|
|
6618
|
-
|
|
6619
|
-
|
|
6620
|
-
|
|
6683
|
+
if (!dry)
|
|
6684
|
+
await cdp.send("Page.startScreencast", {
|
|
6685
|
+
format: "jpeg",
|
|
6686
|
+
quality: 90,
|
|
6687
|
+
maxWidth: vw,
|
|
6688
|
+
maxHeight: vh,
|
|
6689
|
+
everyNthFrame: 1
|
|
6690
|
+
});
|
|
6621
6691
|
const events = [];
|
|
6622
6692
|
const cur = { x: 48, y: 48 };
|
|
6623
6693
|
const now = () => Date.now() - t0;
|
|
@@ -6629,7 +6699,7 @@ async function recordTake(browser, url, actions, paths, log, opts = {}) {
|
|
|
6629
6699
|
const dist = Math.hypot(tx - cur.x, ty - cur.y);
|
|
6630
6700
|
const from = { ...cur };
|
|
6631
6701
|
await clockMotion(
|
|
6632
|
-
pointerTravelMs(dist),
|
|
6702
|
+
dry ? 0 : pointerTravelMs(dist),
|
|
6633
6703
|
async (u) => {
|
|
6634
6704
|
cur.x = from.x + (tx - from.x) * u;
|
|
6635
6705
|
cur.y = from.y + (ty - from.y) * u;
|
|
@@ -6904,12 +6974,14 @@ async function recordTake(browser, url, actions, paths, log, opts = {}) {
|
|
|
6904
6974
|
` WARNING: ${freezePct}% of the take is visually frozen (${freezes.length} freezes, longest ${Math.max(...freezes.map((f) => f.ms))}ms) \u2014 keep motion in frame or trim these spans in doc.json`
|
|
6905
6975
|
);
|
|
6906
6976
|
}
|
|
6907
|
-
await writeJson(paths.cursor, events);
|
|
6908
|
-
await writeJson(paths.meta, meta, true);
|
|
6909
|
-
await writeJson(paths.framesIndex, frames);
|
|
6910
|
-
await writeJson(paths.actions, { ...actions, url }, true);
|
|
6911
6977
|
const pace = paceReport(paces);
|
|
6912
|
-
|
|
6978
|
+
if (!dry) {
|
|
6979
|
+
await writeJson(paths.cursor, events);
|
|
6980
|
+
await writeJson(paths.meta, meta, true);
|
|
6981
|
+
await writeJson(paths.framesIndex, frames);
|
|
6982
|
+
await writeJson(paths.actions, { ...actions, url }, true);
|
|
6983
|
+
log(` ${paceLine(pace)}`);
|
|
6984
|
+
}
|
|
6913
6985
|
return {
|
|
6914
6986
|
events,
|
|
6915
6987
|
frames,
|
|
@@ -7890,6 +7962,15 @@ function printChanges(r, changes, protectedIds) {
|
|
|
7890
7962
|
r.event({ event: "protected", nodes: protectedIds });
|
|
7891
7963
|
}
|
|
7892
7964
|
}
|
|
7965
|
+
function takePushRefusal(ask) {
|
|
7966
|
+
if (ask.programOnly.length) {
|
|
7967
|
+
const list = ask.programOnly.map((f) => `--${f}`).join(", ");
|
|
7968
|
+
return `${list} ${ask.programOnly.length > 1 ? "belong" : "belongs"} to a PROGRAM push (a config.json); a take push reads --vos, --title, --label, --note, --folder, --override and --yes`;
|
|
7969
|
+
}
|
|
7970
|
+
if (ask.vos !== void 0 && ask.trackedVosId !== null && ask.trackedVosId !== ask.vos)
|
|
7971
|
+
return `this take tracks vos ${ask.trackedVosId} (its vos.json), not ${ask.vos}. Drop --vos to push there, or remove vos.json to adopt ${ask.vos} instead`;
|
|
7972
|
+
return null;
|
|
7973
|
+
}
|
|
7893
7974
|
async function pushTake(dir, flags, r) {
|
|
7894
7975
|
const take = await loadTake(dir);
|
|
7895
7976
|
if (!take.doc) {
|
|
@@ -7906,7 +7987,22 @@ async function pushTake(dir, flags, r) {
|
|
|
7906
7987
|
);
|
|
7907
7988
|
}
|
|
7908
7989
|
const ctx = apiContext(flags);
|
|
7909
|
-
const
|
|
7990
|
+
const tracked = readSyncState(dir);
|
|
7991
|
+
const firstPush = tracked === null;
|
|
7992
|
+
let state = tracked;
|
|
7993
|
+
if (!state && flags.vos) {
|
|
7994
|
+
const meta = await api(ctx, `/vos/${flags.vos}`);
|
|
7995
|
+
const head = meta.json.vos?.currentVersionId;
|
|
7996
|
+
if (meta.status !== 200 || !head) {
|
|
7997
|
+
throw new Error(
|
|
7998
|
+
`--vos ${flags.vos}: cannot adopt it (${meta.status}${meta.json.error ? `: ${String(meta.json.error)}` : ""}) \u2014 it must be a vos this credential can read, with a version to build on`
|
|
7999
|
+
);
|
|
8000
|
+
}
|
|
8001
|
+
r.log(
|
|
8002
|
+
`adopting vos ${flags.vos}: this take becomes the next version after its head (${head})`
|
|
8003
|
+
);
|
|
8004
|
+
state = { vosId: flags.vos, versionId: head };
|
|
8005
|
+
}
|
|
7910
8006
|
let folderId;
|
|
7911
8007
|
if (flags.folder) {
|
|
7912
8008
|
if (state) {
|
|
@@ -7919,7 +8015,7 @@ async function pushTake(dir, flags, r) {
|
|
|
7919
8015
|
flags.folder
|
|
7920
8016
|
).id;
|
|
7921
8017
|
}
|
|
7922
|
-
if (
|
|
8018
|
+
if (firstPush && !flags.yes) {
|
|
7923
8019
|
if (!process.stdin.isTTY) {
|
|
7924
8020
|
throw new Error(
|
|
7925
8021
|
"first push of this take uploads its recording \u2014 pass --yes to consent headlessly"
|
|
@@ -8256,12 +8352,12 @@ async function browserLogin(origin, r, opts) {
|
|
|
8256
8352
|
r.log(`Sign in at ${verifyUrl}`);
|
|
8257
8353
|
if (opts.openBrowser) void launchDefaultBrowser(verifyUrl);
|
|
8258
8354
|
r.log("Waiting for approval\u2026 (Ctrl-C to cancel)");
|
|
8259
|
-
const
|
|
8355
|
+
const sleep = opts.sleep ?? ((ms) => new Promise((res) => setTimeout(res, ms)));
|
|
8260
8356
|
let intervalMs = baseIntervalMs;
|
|
8261
8357
|
let failures = 0;
|
|
8262
8358
|
let capNoted = false;
|
|
8263
8359
|
while (Date.now() < expiresAt) {
|
|
8264
|
-
await
|
|
8360
|
+
await sleep(intervalMs);
|
|
8265
8361
|
let res;
|
|
8266
8362
|
try {
|
|
8267
8363
|
res = await apiJson(origin, "/api/cli/login/poll", {
|
|
@@ -9311,10 +9407,12 @@ function composeBrand(input) {
|
|
|
9311
9407
|
const bgC = mixHex(bgA, accent, 0.14);
|
|
9312
9408
|
p.bgC = `bgA tinted 14% toward the accent (a highlight ground)`;
|
|
9313
9409
|
p.ink = w.h1 ? `the h1's colour` : `the body's colour`;
|
|
9314
|
-
const fontDisplay =
|
|
9410
|
+
const fontDisplay = catalogFamily(
|
|
9411
|
+
design?.fonts[0] ?? (w.h1 ? firstFamily(w.h1.fontFamily) : firstFamily(w.body.fontFamily))
|
|
9412
|
+
);
|
|
9315
9413
|
p.fontDisplay = design?.fonts[0] ? `named in design.md` : w.h1 ? `the h1's computed face` : `the body's computed face (no h1)`;
|
|
9316
9414
|
const designBody = design?.fonts.slice(1).find((f) => !/\bmono\b/i.test(f));
|
|
9317
|
-
const fontBody = designBody ?? firstFamily(w.body.fontFamily);
|
|
9415
|
+
const fontBody = catalogFamily(designBody ?? firstFamily(w.body.fontFamily));
|
|
9318
9416
|
p.fontBody = designBody ? `named in design.md` : `the body's computed face`;
|
|
9319
9417
|
const isIcon = (u) => /favicon|apple-touch|icon/i.test(u);
|
|
9320
9418
|
const onDark = (u) => /on-dark|white/i.test(u);
|
|
@@ -10016,14 +10114,16 @@ var BOOLEAN_FLAGS5 = /* @__PURE__ */ new Set([
|
|
|
10016
10114
|
"composed",
|
|
10017
10115
|
"check",
|
|
10018
10116
|
"motion",
|
|
10019
|
-
"leader"
|
|
10117
|
+
"leader",
|
|
10118
|
+
"keep-frames",
|
|
10119
|
+
"dry-run"
|
|
10020
10120
|
]);
|
|
10021
|
-
var MULTI_FLAGS2 = /* @__PURE__ */ new Set(["set", "override"]);
|
|
10121
|
+
var MULTI_FLAGS2 = /* @__PURE__ */ new Set(["set", "override", "browser-arg"]);
|
|
10022
10122
|
var HELP = `vos \u2014 record a browser flow, plan effects, render a product video; sync with vos.so
|
|
10023
10123
|
|
|
10024
10124
|
Take pipeline
|
|
10025
|
-
vos create --actions actions.json [--url <url>] [--out take] [out.webm] [--strict] [--max-duration <s>] [--background <slug|url|none>] [render flags] [--json]
|
|
10026
|
-
vos record --actions actions.json [--url <url>] [--out take] [--strict] [--max-duration <s>] [--background <slug|url|none>] [--json]
|
|
10125
|
+
vos create --actions actions.json [--url <url>] [--out take] [out.webm] [--strict] [--keep-frames] [--max-duration <s>] [--storage-state <file>] [--browser-arg=<switch>]... [--background <slug|url|none>] [render flags] [--json]
|
|
10126
|
+
vos record --actions actions.json [--url <url>] [--out take] [--strict] [--dry-run] [--keep-frames] [--max-duration <s>] [--storage-state <file>] [--browser-arg=<switch>]... [--background <slug|url|none>] [--json]
|
|
10027
10127
|
vos plan <take> [--fresh] [--reuse [--from <doc.json>]] [--style <doc.json|vosId>] [--with <doc.json|vosId>[@end|@start|@step:<id>|@<s>]]... [--background <slug|url|none>] [--motion] [--headline "\u2026"] [--kicker "\u2026"] [--launch LAUNCH.md] [--brand BRAND.md] [--music <slug|mood|none>] [--entrance tilt-in|pull-out|rise|fade|slide|none] [--transitions slide|fade|scale|none] [--end-card on|none|<doc.json|vosId>] [--captions none] [--clicks none] [--still <t>] [--release v2.1] [--json]
|
|
10028
10128
|
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]
|
|
10029
10129
|
vos frames <take> [--times 0,25%,50%,75%,100%] [--frame <t>] [--at-zooms] [--at-moments] [--at-still] [--size WxH] [--out dir] [--background <url|slug>] [--set <path=value>]... [--json]
|
|
@@ -10031,7 +10131,7 @@ Take pipeline
|
|
|
10031
10131
|
vos digest <take> [--out dir] [--full 960] [--crop 640] [--no-frames] [--transcript <file.json>] [--style <doc.json|vosId>] [--json]
|
|
10032
10132
|
vos brand <url> [--out BRAND.md] [--json]
|
|
10033
10133
|
vos open <take> [--studio <url>] [--print]
|
|
10034
|
-
vos callout <take> <note|tag|code> --step <id> [--title "\u2026"] [--body "\u2026"] [--kicker "\u2026"] [--code "\u2026"|--code-file f] [--seconds 3] [--side auto|right|left|below|above] [--mark ring|underline|none] [--leader] [--color #hex] [--brand BRAND.md] [--ground #hex] [--accent #hex] [--font "\u2026"] [--body-px
|
|
10134
|
+
vos callout <take> <note|tag|code> --step <id> [--title "\u2026"] [--body "\u2026"] [--kicker "\u2026"] [--code "\u2026"|--code-file f] [--seconds 3] [--side auto|right|left|below|above] [--mark ring|underline|none] [--leader] [--color #hex (the mark and leader; the kicker is --accent)] [--brand BRAND.md] [--ground #hex] [--accent #hex] [--font "\u2026"] [--body-px <n> (the body size on the delivered frame)] [--id x] [--print] [--json]
|
|
10035
10135
|
vos validate <actions.json|take|kit.json> [--picture] [--json]
|
|
10036
10136
|
vos judge <kit.json> --against <MANIFEST.json> [--out dir] [--json]
|
|
10037
10137
|
vos actions from-agent-browser <steps.jsonl> [--out actions.json] [--url <url>] [--viewport WxH] [--json]
|
|
@@ -10051,7 +10151,10 @@ Platform (vos.so) \u2014 fetch, edit, push, pull, repeat
|
|
|
10051
10151
|
a take DIRECTORY (doc.json) pushes recording + doc; a config.json
|
|
10052
10152
|
pushes the program. No --vos: create a PRIVATE vos; with --vos:
|
|
10053
10153
|
add a version against the tracked base \u2014 a stale push 409s WITH
|
|
10054
|
-
the platform's typed changelog.
|
|
10154
|
+
the platform's typed changelog. A TAKE pushes to the vos in its
|
|
10155
|
+
vos.json; --vos on a take with none ADOPTS that vos (the take
|
|
10156
|
+
becomes its next version, built on its head), and --vos that
|
|
10157
|
+
disagrees with vos.json is refused before anything uploads. --claimable (programs only, NO
|
|
10055
10158
|
credential): creates a 72h claim link instead \u2014 hand it to the
|
|
10056
10159
|
user and nowhere else; unclaimed work is deleted after 72h.
|
|
10057
10160
|
--override consents to touching
|
|
@@ -10298,6 +10401,18 @@ function strictReason(rec) {
|
|
|
10298
10401
|
if (rec.capped) parts.push("stopped at --max-duration");
|
|
10299
10402
|
return parts.join(" + ");
|
|
10300
10403
|
}
|
|
10404
|
+
function takeBrowserArgs(multi) {
|
|
10405
|
+
const given = multi["browser-arg"];
|
|
10406
|
+
return (given ?? []).filter(Boolean);
|
|
10407
|
+
}
|
|
10408
|
+
function takeStorageState(flags) {
|
|
10409
|
+
const raw = strFlag(flags, "storage-state");
|
|
10410
|
+
if (!raw) return void 0;
|
|
10411
|
+
const path = resolve9(raw);
|
|
10412
|
+
if (!existsSync15(path))
|
|
10413
|
+
throw new UsageError(`--storage-state: no such file: ${path}`);
|
|
10414
|
+
return path;
|
|
10415
|
+
}
|
|
10301
10416
|
async function takeBackdrop(flags, r) {
|
|
10302
10417
|
const { backdrop, note } = await openingBackdrop(
|
|
10303
10418
|
strFlag(flags, "background"),
|
|
@@ -10309,8 +10424,20 @@ async function takeBackdrop(flags, r) {
|
|
|
10309
10424
|
if (note) r.log(`note: ${note}`);
|
|
10310
10425
|
return backdrop;
|
|
10311
10426
|
}
|
|
10427
|
+
async function releaseFrames(paths, flags, log) {
|
|
10428
|
+
if (flags["keep-frames"] === true) return;
|
|
10429
|
+
const freed = await dropScreencastFrames(paths);
|
|
10430
|
+
if (freed >= 1e6)
|
|
10431
|
+
log(
|
|
10432
|
+
`released ${Math.round(freed / 1e6)} MB of screencast frames (the recording holds them now; --keep-frames keeps them)`
|
|
10433
|
+
);
|
|
10434
|
+
}
|
|
10312
10435
|
async function cmdRecord(argv) {
|
|
10313
|
-
const { positionals, flags } = parseArgs(
|
|
10436
|
+
const { positionals, flags, multi } = parseArgs(
|
|
10437
|
+
argv,
|
|
10438
|
+
BOOLEAN_FLAGS5,
|
|
10439
|
+
MULTI_FLAGS2
|
|
10440
|
+
);
|
|
10314
10441
|
const actionsPath = strFlag(flags, "actions") ?? positionals[0];
|
|
10315
10442
|
if (!actionsPath)
|
|
10316
10443
|
throw new UsageError(
|
|
@@ -10324,6 +10451,49 @@ async function cmdRecord(argv) {
|
|
|
10324
10451
|
const outDir = resolve9(strFlag(flags, "out") ?? "take");
|
|
10325
10452
|
const backdrop = await takeBackdrop(flags, r);
|
|
10326
10453
|
const maxDurationSeconds = await maxDuration(flags, r);
|
|
10454
|
+
const storageState = takeStorageState(flags);
|
|
10455
|
+
const browserArgs = takeBrowserArgs(multi);
|
|
10456
|
+
if (flags["dry-run"] === true) {
|
|
10457
|
+
void flags.strict;
|
|
10458
|
+
void flags["keep-frames"];
|
|
10459
|
+
const browser2 = await launchBrowser(browserArgs);
|
|
10460
|
+
try {
|
|
10461
|
+
r.log("rehearsing (nothing is captured or written)\u2026");
|
|
10462
|
+
r.event({ event: "phase", phase: "rehearse" });
|
|
10463
|
+
const started = Date.now();
|
|
10464
|
+
const rec = await recordTake(
|
|
10465
|
+
browser2,
|
|
10466
|
+
url,
|
|
10467
|
+
actions,
|
|
10468
|
+
takePaths(outDir),
|
|
10469
|
+
r.log,
|
|
10470
|
+
{ storageState, dryRun: true }
|
|
10471
|
+
);
|
|
10472
|
+
const steps = rec.meta.steps ?? [];
|
|
10473
|
+
const lines = steps.map((s) => {
|
|
10474
|
+
const rect = s.rect ? ` rect ${s.rect.x},${s.rect.y} ${s.rect.w}\xD7${s.rect.h}` : "";
|
|
10475
|
+
const what = s.selector ? ` ${s.selector}` : "";
|
|
10476
|
+
return ` ${s.skipped ? "\u2717" : "\u2713"} #${s.step}${s.id ? ` (${s.id})` : ""} ${s.do}${what}${s.skipped ? " NOT FOUND" : rect}${s.navigated ? " \u2192 navigated" : ""}`;
|
|
10477
|
+
});
|
|
10478
|
+
const failed2 = rec.skipped.length > 0 || rec.navTimeout;
|
|
10479
|
+
r.done(
|
|
10480
|
+
{
|
|
10481
|
+
dryRun: true,
|
|
10482
|
+
ok: !failed2,
|
|
10483
|
+
seconds: +((Date.now() - started) / 1e3).toFixed(1),
|
|
10484
|
+
steps,
|
|
10485
|
+
skipped: rec.skipped,
|
|
10486
|
+
navTimeout: rec.navTimeout
|
|
10487
|
+
},
|
|
10488
|
+
`${failed2 ? "REHEARSAL FAILED" : "Rehearsal passed"}: ${steps.length - rec.skipped.length}/${steps.length} steps resolved in ${((Date.now() - started) / 1e3).toFixed(1)}s${rec.navTimeout ? " (the first load never reached networkidle)" : ""}
|
|
10489
|
+
${lines.join("\n")}
|
|
10490
|
+
${failed2 ? "Fix the script, rehearse again, then record." : `Rects are capture px (the step rects a pin or a callout reads). Next: vos record --actions ${actionsPath} --out ${outDir} --strict`}`
|
|
10491
|
+
);
|
|
10492
|
+
return failed2 ? EXIT_USAGE : EXIT_OK;
|
|
10493
|
+
} finally {
|
|
10494
|
+
await browser2.close();
|
|
10495
|
+
}
|
|
10496
|
+
}
|
|
10327
10497
|
if (existsSync15(join18(outDir, "meta.json"))) {
|
|
10328
10498
|
const { prevDoc, kept } = await prepareReRecord(outDir);
|
|
10329
10499
|
r.log(
|
|
@@ -10332,12 +10502,13 @@ async function cmdRecord(argv) {
|
|
|
10332
10502
|
}
|
|
10333
10503
|
await mkdir10(outDir, { recursive: true });
|
|
10334
10504
|
const paths = await ensureTakeDir(outDir);
|
|
10335
|
-
const browser = await launchBrowser();
|
|
10505
|
+
const browser = await launchBrowser(browserArgs);
|
|
10336
10506
|
try {
|
|
10337
10507
|
r.log("recording\u2026");
|
|
10338
10508
|
r.event({ event: "phase", phase: "record" });
|
|
10339
10509
|
const rec = await recordTake(browser, url, actions, paths, r.log, {
|
|
10340
|
-
maxDurationSeconds
|
|
10510
|
+
maxDurationSeconds,
|
|
10511
|
+
storageState
|
|
10341
10512
|
});
|
|
10342
10513
|
r.event({ event: "phase", phase: "encode" });
|
|
10343
10514
|
r.log("encoding\u2026");
|
|
@@ -10346,6 +10517,7 @@ async function cmdRecord(argv) {
|
|
|
10346
10517
|
outDir,
|
|
10347
10518
|
(p) => r.event({ event: "progress", phase: "encode", fraction: p })
|
|
10348
10519
|
);
|
|
10520
|
+
await releaseFrames(paths, flags, r.log);
|
|
10349
10521
|
r.event({ event: "phase", phase: "plan" });
|
|
10350
10522
|
const plan = await planTake(outDir, { backdrop });
|
|
10351
10523
|
const clicks = rec.events.filter((e) => e.type === "down").length;
|
|
@@ -10408,6 +10580,8 @@ async function cmdCreate2(argv) {
|
|
|
10408
10580
|
}
|
|
10409
10581
|
const backdrop = await takeBackdrop(flags, r);
|
|
10410
10582
|
const maxDurationSeconds = await maxDuration(flags, r);
|
|
10583
|
+
const storageState = takeStorageState(flags);
|
|
10584
|
+
const browserArgs = takeBrowserArgs(multi);
|
|
10411
10585
|
if (existsSync15(join18(outDir, "meta.json"))) {
|
|
10412
10586
|
const { prevDoc, kept } = await prepareReRecord(outDir);
|
|
10413
10587
|
r.log(
|
|
@@ -10416,12 +10590,13 @@ async function cmdCreate2(argv) {
|
|
|
10416
10590
|
}
|
|
10417
10591
|
await mkdir10(outDir, { recursive: true });
|
|
10418
10592
|
const paths = await ensureTakeDir(outDir);
|
|
10419
|
-
const browser = await launchBrowser();
|
|
10593
|
+
const browser = await launchBrowser(browserArgs);
|
|
10420
10594
|
try {
|
|
10421
10595
|
r.log("recording\u2026");
|
|
10422
10596
|
r.event({ event: "phase", phase: "record" });
|
|
10423
10597
|
const rec = await recordTake(browser, url, actions, paths, r.log, {
|
|
10424
|
-
maxDurationSeconds
|
|
10598
|
+
maxDurationSeconds,
|
|
10599
|
+
storageState
|
|
10425
10600
|
});
|
|
10426
10601
|
r.event({ event: "phase", phase: "encode" });
|
|
10427
10602
|
r.log("encoding\u2026");
|
|
@@ -10430,6 +10605,7 @@ async function cmdCreate2(argv) {
|
|
|
10430
10605
|
outDir,
|
|
10431
10606
|
(p) => r.event({ event: "progress", phase: "encode", fraction: p })
|
|
10432
10607
|
);
|
|
10608
|
+
await releaseFrames(paths, flags, r.log);
|
|
10433
10609
|
r.event({ event: "phase", phase: "plan" });
|
|
10434
10610
|
await planTake(outDir, { backdrop });
|
|
10435
10611
|
if (flags.strict === true && (rec.skipped.length > 0 || rec.navTimeout || rec.capped)) {
|
|
@@ -11129,7 +11305,7 @@ async function cmdCallout(argv) {
|
|
|
11129
11305
|
const shape = positionals[1];
|
|
11130
11306
|
if (!dir || !shape || !isCalloutShape(shape))
|
|
11131
11307
|
throw new UsageError(
|
|
11132
|
-
'vos callout <take> <note|tag|code> --step <id> [--title "\u2026"] [--body "\u2026"] [--kicker "\u2026"] [--code "\u2026"|--code-file f] [--seconds 3] [--side \u2026] [--mark ring|underline|none] [--leader] [--color #hex] [--brand BRAND.md] [--ground #hex --accent #hex] [--font "\u2026"] [--body-px
|
|
11308
|
+
'vos callout <take> <note|tag|code> --step <id> [--title "\u2026"] [--body "\u2026"] [--kicker "\u2026"] [--code "\u2026"|--code-file f] [--seconds 3] [--side \u2026] [--mark ring|underline|none] [--leader] [--color #hex (the mark and leader; the kicker is --accent)] [--brand BRAND.md] [--ground #hex --accent #hex] [--font "\u2026"] [--body-px <n> (the body size on the delivered frame)] [--id x] [--print] [--json]'
|
|
11133
11309
|
);
|
|
11134
11310
|
const r = createReporter(flags.json === true);
|
|
11135
11311
|
const take = await loadTake(dir);
|
|
@@ -11180,15 +11356,16 @@ async function cmdCallout(argv) {
|
|
|
11180
11356
|
side,
|
|
11181
11357
|
mark,
|
|
11182
11358
|
leader: flags.leader === true,
|
|
11183
|
-
color: strFlag(flags, "color")
|
|
11359
|
+
color: strFlag(flags, "color"),
|
|
11360
|
+
bodyPxGiven: bodyPx !== void 0
|
|
11184
11361
|
});
|
|
11185
11362
|
} catch (e) {
|
|
11186
11363
|
throw new UsageError(e instanceof Error ? e.message : String(e));
|
|
11187
11364
|
}
|
|
11188
|
-
const { clip, window: window2 } = composed;
|
|
11365
|
+
const { clip, window: window2, sizes } = composed;
|
|
11189
11366
|
if (flags.print === true) {
|
|
11190
11367
|
r.done(
|
|
11191
|
-
{ clip, window: window2, register, brand: brand?.file ?? null },
|
|
11368
|
+
{ clip, window: window2, sizes, register, brand: brand?.file ?? null },
|
|
11192
11369
|
JSON.stringify(clip, null, 2)
|
|
11193
11370
|
);
|
|
11194
11371
|
return EXIT_OK;
|
|
@@ -11210,11 +11387,12 @@ async function cmdCallout(argv) {
|
|
|
11210
11387
|
{
|
|
11211
11388
|
clip,
|
|
11212
11389
|
window: window2,
|
|
11390
|
+
sizes,
|
|
11213
11391
|
register,
|
|
11214
11392
|
brand: brand?.file ?? null,
|
|
11215
11393
|
warnings: lint.warnings
|
|
11216
11394
|
},
|
|
11217
|
-
`${shape} "${clip.id}" at ${window2.start.toFixed(2)}s for ${window2.duration.toFixed(2)}s${clip.pin ? `, pinned to ${clip.pin.step !== void 0 ? `step ${String(clip.pin.step)}` : `the press at ${String(clip.pin.press)}s`}` : ""}, ground ${register.ground} \u2192 card in the product's hue, written to ${take.paths.doc}${lint.warnings.length ? `
|
|
11395
|
+
`${shape} "${clip.id}" at ${window2.start.toFixed(2)}s for ${window2.duration.toFixed(2)}s${clip.pin ? `, pinned to ${clip.pin.step !== void 0 ? `step ${String(clip.pin.step)}` : `the press at ${String(clip.pin.press)}s`}` : ""}, ground ${register.ground} \u2192 card in the product's hue, type ${sizes.kickerPx} / ${sizes.titlePx} / ${sizes.bodyPx} px (kicker / title / body; ${bodyPx !== void 0 ? `--body-px ${bodyPx} as delivered` : `the app's body as seen, footage scale ${sizes.scale}; --body-px <n> sets the delivered size`}), written to ${take.paths.doc}${lint.warnings.length ? `
|
|
11218
11396
|
warnings:
|
|
11219
11397
|
${lint.warnings.join("\n ")}` : ""}`
|
|
11220
11398
|
);
|
|
@@ -11377,8 +11555,16 @@ async function cmdPush3(argv) {
|
|
|
11377
11555
|
const dir = positionals[0];
|
|
11378
11556
|
if (!dir)
|
|
11379
11557
|
throw new UsageError(
|
|
11380
|
-
"vos push <take> [--title|--label|--note|--folder|--override|--yes|--key|--api]"
|
|
11558
|
+
"vos push <take> [--vos|--title|--label|--note|--folder|--override|--yes|--key|--api]"
|
|
11381
11559
|
);
|
|
11560
|
+
const refusal = takePushRefusal({
|
|
11561
|
+
vos: strFlag(flags, "vos"),
|
|
11562
|
+
trackedVosId: readSyncState(resolve9(dir))?.vosId ?? null,
|
|
11563
|
+
programOnly: ["slug", "desc", "tags", "base", "remix-of"].filter(
|
|
11564
|
+
(name) => hasFlag(flags, name)
|
|
11565
|
+
)
|
|
11566
|
+
});
|
|
11567
|
+
if (refusal) throw new UsageError(refusal);
|
|
11382
11568
|
const r = createReporter(flags.json === true);
|
|
11383
11569
|
const overrides = multi.override;
|
|
11384
11570
|
const result = await pushTake(
|
|
@@ -11388,6 +11574,7 @@ async function cmdPush3(argv) {
|
|
|
11388
11574
|
api: strFlag(flags, "api"),
|
|
11389
11575
|
origin: strFlag(flags, "origin"),
|
|
11390
11576
|
yes: flags.yes === true,
|
|
11577
|
+
vos: strFlag(flags, "vos"),
|
|
11391
11578
|
title: strFlag(flags, "title"),
|
|
11392
11579
|
label: strFlag(flags, "label"),
|
|
11393
11580
|
note: strFlag(flags, "note"),
|
|
@@ -11548,6 +11735,9 @@ export {
|
|
|
11548
11735
|
parseAgentBrowserLog,
|
|
11549
11736
|
splitCommand,
|
|
11550
11737
|
convertAgentBrowser,
|
|
11738
|
+
MULTI_FLAGS2 as MULTI_FLAGS,
|
|
11739
|
+
HELP,
|
|
11740
|
+
takeBrowserArgs,
|
|
11551
11741
|
run
|
|
11552
11742
|
};
|
|
11553
|
-
//# sourceMappingURL=chunk-
|
|
11743
|
+
//# sourceMappingURL=chunk-RECQEBXN.js.map
|