@vosjs/cli 0.25.0 → 0.26.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 +1 -1
- package/dist/{chunk-TYTUA5L5.js → chunk-I3UUIBSH.js} +46 -17
- package/dist/chunk-I3UUIBSH.js.map +1 -0
- package/dist/cli.js +1 -1
- package/dist/index.d.ts +2 -0
- package/dist/index.js +1 -1
- package/dist/{run-OZR6NVK3.js → run-VSENMRFF.js} +2 -2
- package/package.json +6 -6
- package/schema/doc.schema.json +5 -0
- package/dist/chunk-TYTUA5L5.js.map +0 -1
- /package/dist/{run-OZR6NVK3.js.map → run-VSENMRFF.js.map} +0 -0
package/README.md
CHANGED
|
@@ -93,7 +93,7 @@ vos plan take --reuse # re-time that cut onto
|
|
|
93
93
|
|
|
94
94
|
**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.
|
|
95
95
|
|
|
96
|
-
**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.
|
|
96
|
+
**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. **The frame that STANDS for a take** is `still` (output seconds; `vos plan --still <t>`, LAUNCH.md `still:`), else the take's own last freeze (a poster's rest; a template's freezes, stamped `from`, never count), else the hero moment after the card and the opening clips have entered: the shelf's cover, the kit's card stills and `vos frames --at-still` all read that one derivation, and a plan never moves a still the author set. 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.
|
|
97
97
|
|
|
98
98
|
**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.
|
|
99
99
|
|
|
@@ -203,6 +203,7 @@ import {
|
|
|
203
203
|
ZOOM_LEVEL_MIN,
|
|
204
204
|
ZOOM_SPAN_MIN,
|
|
205
205
|
docCardLayout,
|
|
206
|
+
outputEnd,
|
|
206
207
|
ratedSegments,
|
|
207
208
|
recommendedExportResolution,
|
|
208
209
|
spanOutputExtent,
|
|
@@ -456,6 +457,22 @@ function lintDoc(docIn) {
|
|
|
456
457
|
);
|
|
457
458
|
}
|
|
458
459
|
}
|
|
460
|
+
if (recording && doc.still !== void 0) {
|
|
461
|
+
const st = doc.still;
|
|
462
|
+
if (!isNum(st) || st < 0) {
|
|
463
|
+
problems.push("still must be a number \u2265 0 (OUTPUT seconds)");
|
|
464
|
+
} else {
|
|
465
|
+
const rated = ratedSegments(doc);
|
|
466
|
+
const end = outputEnd(
|
|
467
|
+
doc,
|
|
468
|
+
rated.reduce((a, s) => a + (s.out - s.in) / (s.rate ?? 1), 0)
|
|
469
|
+
);
|
|
470
|
+
if (st > end + 1e-6)
|
|
471
|
+
problems.push(
|
|
472
|
+
`still=${String(st)} is past the output's end (${end.toFixed(2)}s)`
|
|
473
|
+
);
|
|
474
|
+
}
|
|
475
|
+
}
|
|
459
476
|
for (const [i, seg] of (Array.isArray(doc.segments) ? doc.segments : []).entries()) {
|
|
460
477
|
const hold = seg.hold;
|
|
461
478
|
if (hold === void 0) continue;
|
|
@@ -1419,6 +1436,7 @@ import { existsSync as existsSync4 } from "fs";
|
|
|
1419
1436
|
import { join as join4, resolve as resolve2 } from "path";
|
|
1420
1437
|
import { compileVosConfig } from "@vosjs/core";
|
|
1421
1438
|
import {
|
|
1439
|
+
docStillTime,
|
|
1422
1440
|
lowerToComposition,
|
|
1423
1441
|
momentsFromDoc as momentsFromDoc2,
|
|
1424
1442
|
planForDigest as planForDigest2,
|
|
@@ -2751,10 +2769,12 @@ async function framesTake(browser, dir, opts) {
|
|
|
2751
2769
|
shots.push({ time: clamp(m.outputAt), kind: "moment", momentId: m.id });
|
|
2752
2770
|
}
|
|
2753
2771
|
}
|
|
2772
|
+
if (opts.atStill)
|
|
2773
|
+
shots.push({ time: clamp(docStillTime(doc)), kind: "still" });
|
|
2754
2774
|
shots.sort((a, b) => a.time - b.time);
|
|
2755
2775
|
if (!shots.length)
|
|
2756
2776
|
throw new Error(
|
|
2757
|
-
"no frame times \u2014 pass --times/--frame, --at-zooms or --at-
|
|
2777
|
+
"no frame times \u2014 pass --times/--frame, --at-zooms, --at-moments or --at-still"
|
|
2758
2778
|
);
|
|
2759
2779
|
const outDir = resolve2(opts.outDir ?? join4(dir, "stills"));
|
|
2760
2780
|
await mkdir3(outDir, { recursive: true });
|
|
@@ -3498,6 +3518,7 @@ import {
|
|
|
3498
3518
|
endCardClips,
|
|
3499
3519
|
migrateMotion,
|
|
3500
3520
|
ratedSegments as ratedSegments4,
|
|
3521
|
+
sameMedia,
|
|
3501
3522
|
spanOutputExtent as spanOutputExtent4
|
|
3502
3523
|
} from "@vosjs/studio-core";
|
|
3503
3524
|
var SOUND_DESTINATIONS = /* @__PURE__ */ new Set([
|
|
@@ -3624,19 +3645,20 @@ function proposeMotion(input, opts) {
|
|
|
3624
3645
|
const endStart = outputLength(doc);
|
|
3625
3646
|
const lastSeg = doc.segments.at(-1);
|
|
3626
3647
|
if (lastSeg) {
|
|
3627
|
-
const
|
|
3628
|
-
|
|
3629
|
-
);
|
|
3648
|
+
const atEnd = (f) => Math.abs(f.at - lastSeg.out) <= 1e-9 && sameMedia(f.media, lastSeg.media);
|
|
3649
|
+
const kept2 = (doc.freeze ?? []).find((f) => atEnd(f) && !f.from);
|
|
3650
|
+
const own = (doc.freeze ?? []).filter((f) => !atEnd(f));
|
|
3630
3651
|
const taken = new Set(own.map((f) => f.id));
|
|
3631
3652
|
let n = 0;
|
|
3632
3653
|
while (taken.has(`f${n}`)) n++;
|
|
3633
3654
|
doc.freeze = [
|
|
3634
3655
|
...own,
|
|
3635
|
-
{
|
|
3656
|
+
kept2 ? { ...kept2, seconds: Math.max(kept2.seconds, END_CARD_SECONDS) } : {
|
|
3636
3657
|
id: `f${n}`,
|
|
3637
3658
|
at: lastSeg.out,
|
|
3638
3659
|
seconds: END_CARD_SECONDS,
|
|
3639
|
-
from: END_CARD_FROM
|
|
3660
|
+
from: END_CARD_FROM,
|
|
3661
|
+
...lastSeg.media ? { media: lastSeg.media } : {}
|
|
3640
3662
|
}
|
|
3641
3663
|
].sort((a, b) => a.at - b.at);
|
|
3642
3664
|
}
|
|
@@ -3798,13 +3820,11 @@ function isLightHexGround(hex2) {
|
|
|
3798
3820
|
import { existsSync as existsSync5 } from "fs";
|
|
3799
3821
|
import { readFile as readFile5 } from "fs/promises";
|
|
3800
3822
|
import { isAbsolute, join as join6, resolve as resolve3 } from "path";
|
|
3801
|
-
import { totalDuration } from "@vosjs/timeline";
|
|
3802
3823
|
import {
|
|
3803
3824
|
computeCardLayout,
|
|
3804
|
-
|
|
3825
|
+
docStillTime as docStillTime2,
|
|
3805
3826
|
migrateHostedDoc,
|
|
3806
3827
|
overlayRect,
|
|
3807
|
-
ratedSegments as ratedSegments5,
|
|
3808
3828
|
resolveOverlayStyle
|
|
3809
3829
|
} from "@vosjs/studio-core";
|
|
3810
3830
|
|
|
@@ -4044,11 +4064,7 @@ async function findPosterDocs(takeDir, launchRoles) {
|
|
|
4044
4064
|
return out;
|
|
4045
4065
|
}
|
|
4046
4066
|
function posterStillTime(doc, duration) {
|
|
4047
|
-
|
|
4048
|
-
if (rest != null) return rest;
|
|
4049
|
-
const footage = totalDuration(ratedSegments5(doc));
|
|
4050
|
-
const end = footage > 0 ? Math.min(duration, footage) : duration;
|
|
4051
|
-
return Math.max(0, end - 1 / 30);
|
|
4067
|
+
return Math.max(0, Math.min(duration, docStillTime2(doc)));
|
|
4052
4068
|
}
|
|
4053
4069
|
var DESIGN_H = 1080;
|
|
4054
4070
|
function designFrame(px) {
|
|
@@ -6243,6 +6259,8 @@ async function planTake(dir, opts = {}) {
|
|
|
6243
6259
|
if (prev.objects?.length) doc.objects = prev.objects;
|
|
6244
6260
|
if (prev.audio.length) doc.audio = prev.audio;
|
|
6245
6261
|
if (prev.camMotion?.length) doc.camMotion = prev.camMotion;
|
|
6262
|
+
if (prev.still !== void 0) doc.still = prev.still;
|
|
6263
|
+
if (opts.still != null) doc.still = opts.still;
|
|
6246
6264
|
await writeJson(take.paths.doc, doc, true);
|
|
6247
6265
|
return {
|
|
6248
6266
|
doc,
|
|
@@ -6321,6 +6339,7 @@ async function planTake(dir, opts = {}) {
|
|
|
6321
6339
|
doc = proposed.doc;
|
|
6322
6340
|
motion = { notes: proposed.notes, skipped: proposed.skipped };
|
|
6323
6341
|
}
|
|
6342
|
+
if (opts.still != null) doc.still = opts.still;
|
|
6324
6343
|
await writeJson(take.paths.doc, doc, true);
|
|
6325
6344
|
return {
|
|
6326
6345
|
doc,
|
|
@@ -8967,6 +8986,7 @@ var BOOLEAN_FLAGS5 = /* @__PURE__ */ new Set([
|
|
|
8967
8986
|
"draft",
|
|
8968
8987
|
"at-zooms",
|
|
8969
8988
|
"at-moments",
|
|
8989
|
+
"at-still",
|
|
8970
8990
|
"no-frames",
|
|
8971
8991
|
"media",
|
|
8972
8992
|
"print",
|
|
@@ -8981,9 +9001,9 @@ var HELP = `vos \u2014 record a browser flow, plan effects, render a product vid
|
|
|
8981
9001
|
Take pipeline
|
|
8982
9002
|
vos create --actions actions.json [--url <url>] [--out take] [out.webm] [--strict] [--max-duration <s>] [--background <slug|url|none>] [render flags] [--json]
|
|
8983
9003
|
vos record --actions actions.json [--url <url>] [--out take] [--strict] [--max-duration <s>] [--background <slug|url|none>] [--json]
|
|
8984
|
-
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|none] [--end-card on|none|<doc.json|vosId>] [--captions none] [--clicks none] [--release v2.1] [--json]
|
|
9004
|
+
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|none] [--end-card on|none|<doc.json|vosId>] [--captions none] [--clicks none] [--still <t>] [--release v2.1] [--json]
|
|
8985
9005
|
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]
|
|
8986
|
-
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]
|
|
9006
|
+
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]
|
|
8987
9007
|
vos deliver <take> --to cws,producthunt,x,linkedin,og,github,youtube (or all) [--launch LAUNCH.md] [--look plate|gradient|dark|none] [--brand BRAND.md] [--composed] [--set path=value] [--release v2.1] [--out dir] [--times a,b] [--range a..b] [--parallel N] [--json]
|
|
8988
9008
|
vos digest <take> [--out dir] [--full 960] [--crop 640] [--no-frames] [--transcript <file.json>] [--style <doc.json|vosId>] [--json]
|
|
8989
9009
|
vos brand <url> [--out BRAND.md] [--json]
|
|
@@ -9475,12 +9495,19 @@ async function cmdPlan(argv) {
|
|
|
9475
9495
|
const backdrop = hasDoc ? null : await takeBackdrop(flags, r);
|
|
9476
9496
|
const release = await releaseInputs(dir, flags, r, multi.with ?? []);
|
|
9477
9497
|
const motionWanted = !hasDoc || flags.motion === true || flags.fresh === true;
|
|
9498
|
+
const stillRaw = strFlag(flags, "still") ?? release.launchRoles.still;
|
|
9499
|
+
const still = stillRaw === void 0 ? null : Number(stillRaw);
|
|
9500
|
+
if (still !== null && !(Number.isFinite(still) && still >= 0))
|
|
9501
|
+
throw new UsageError(
|
|
9502
|
+
`--still expects output seconds \u2265 0, got "${stillRaw}"`
|
|
9503
|
+
);
|
|
9478
9504
|
const s = await planTake(dir, {
|
|
9479
9505
|
...style ? { style } : {},
|
|
9480
9506
|
...reuse ? { reuse } : {},
|
|
9481
9507
|
backdrop,
|
|
9482
9508
|
words: release.words,
|
|
9483
9509
|
mark: release.mark,
|
|
9510
|
+
...still !== null ? { still } : {},
|
|
9484
9511
|
...motionWanted ? {
|
|
9485
9512
|
motion: {
|
|
9486
9513
|
words: release.words,
|
|
@@ -9728,6 +9755,7 @@ async function cmdFrames(argv) {
|
|
|
9728
9755
|
const timesRaw = strFlag(flags, "times");
|
|
9729
9756
|
const atZooms = flags["at-zooms"] === true;
|
|
9730
9757
|
const atMoments = flags["at-moments"] === true;
|
|
9758
|
+
const atStill = flags["at-still"] === true;
|
|
9731
9759
|
let times;
|
|
9732
9760
|
try {
|
|
9733
9761
|
times = frameRaw !== void 0 ? parseTimes(frameRaw, duration) : timesRaw !== void 0 ? parseTimes(timesRaw, duration) : atZooms || atMoments ? [] : parseTimes("0,25%,50%,75%,100%", duration);
|
|
@@ -9741,6 +9769,7 @@ async function cmdFrames(argv) {
|
|
|
9741
9769
|
times,
|
|
9742
9770
|
atZooms,
|
|
9743
9771
|
atMoments,
|
|
9772
|
+
atStill,
|
|
9744
9773
|
width: size?.width,
|
|
9745
9774
|
height: size?.height,
|
|
9746
9775
|
outDir: strFlag(flags, "out"),
|
|
@@ -10359,4 +10388,4 @@ export {
|
|
|
10359
10388
|
convertAgentBrowser,
|
|
10360
10389
|
run
|
|
10361
10390
|
};
|
|
10362
|
-
//# sourceMappingURL=chunk-
|
|
10391
|
+
//# sourceMappingURL=chunk-I3UUIBSH.js.map
|