@vosjs/cli 0.26.0 → 0.28.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 +11 -11
- package/dist/{chunk-I3UUIBSH.js → chunk-T35XY4YW.js} +154 -6
- package/dist/chunk-T35XY4YW.js.map +1 -0
- package/dist/cli.js +1 -1
- package/dist/index.js +1 -1
- package/dist/{run-VSENMRFF.js → run-QAI3HGTK.js} +2 -2
- package/package.json +5 -5
- package/schema/doc.schema.json +54 -4
- package/dist/chunk-I3UUIBSH.js.map +0 -1
- /package/dist/{run-VSENMRFF.js.map → run-QAI3HGTK.js.map} +0 -0
package/README.md
CHANGED
|
@@ -77,23 +77,23 @@ vos record --actions actions.json --out take --strict # footage replaced; the
|
|
|
77
77
|
vos plan take --reuse # re-time that cut onto the new recording; what could not follow is named
|
|
78
78
|
```
|
|
79
79
|
|
|
80
|
-
| Verb | Flags
|
|
81
|
-
| -------- |
|
|
82
|
-
| `record` | `--actions <file>` (or positional) `--url` `--out take` `--strict` `--max-duration <s>` `--background <slug\|url\|none>`
|
|
83
|
-
| `create` | The `record` flags plus the render flags (`--width` `--height` `--fps` `--format` `--parallel` `--draft` `--frame` `--set`), no `--range`. With `--strict` an incomplete recording exits 2 before anything is rendered
|
|
84
|
-
| `plan` | `--fresh` (discard the current plan) `--reuse` `--from <doc.json>` (defaults to `<take>/doc.prev.json`) `--style <doc.json\|take\|vosId>` `--with <doc.json\|take\|vosId>[@end\|@start\|@step:<id>\|@<seconds>]` (a template, repeatable) `--background` `--motion` (re-propose the motion) `--headline` `--kicker` `--launch` `--brand` `--music` `--entrance` `--end-card on\|none\|<ref>` `--captions` `--clicks` `--release` |
|
|
85
|
-
| `digest` | `--out <take>/digest` `--full 960` `--crop 640` (image long edges, the token budget) `--no-frames` `--transcript <file>` (Whisper-shaped segments merged as `said`) `--style <ref>` (report a reference document's style fields)
|
|
86
|
-
| `frames` | `--times 0,25%,50%,75%,100%` (the default selector, output seconds or percent) `--frame <t>` `--at-zooms` `--at-moments` `--size WxH` `--out <take>/stills` `--background` `--set …`; writes `stills.json`
|
|
87
|
-
| `render` | `--width` `--height` `--fps` `--format webm\|mp4` `--parallel N` (1..16 sessions) `--range a..b` (output seconds; keeps its audio) `--draft` `--frame <kind>` `--background` `--set …`; `out` defaults to `<take>/out.<format>`
|
|
88
|
-
| `open` | `--studio http://localhost:6060` `--print` (print the URL, do not launch a browser)
|
|
80
|
+
| Verb | Flags |
|
|
81
|
+
| -------- | ------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
|
|
82
|
+
| `record` | `--actions <file>` (or positional) `--url` `--out take` `--strict` `--max-duration <s>` `--background <slug\|url\|none>` |
|
|
83
|
+
| `create` | The `record` flags plus the render flags (`--width` `--height` `--fps` `--format` `--parallel` `--draft` `--frame` `--set`), no `--range`. With `--strict` an incomplete recording exits 2 before anything is rendered |
|
|
84
|
+
| `plan` | `--fresh` (discard the current plan) `--reuse` `--from <doc.json>` (defaults to `<take>/doc.prev.json`) `--style <doc.json\|take\|vosId>` `--with <doc.json\|take\|vosId>[@end\|@start\|@step:<id>\|@<seconds>]` (a template, repeatable) `--background` `--motion` (re-propose the motion) `--headline` `--kicker` `--launch` `--brand` `--music` `--entrance` `--transitions slide\|fade\|scale\|none` `--end-card on\|none\|<ref>` `--captions` `--clicks` `--release` |
|
|
85
|
+
| `digest` | `--out <take>/digest` `--full 960` `--crop 640` (image long edges, the token budget) `--no-frames` `--transcript <file>` (Whisper-shaped segments merged as `said`) `--style <ref>` (report a reference document's style fields) |
|
|
86
|
+
| `frames` | `--times 0,25%,50%,75%,100%` (the default selector, output seconds or percent) `--frame <t>` `--at-zooms` `--at-moments` `--size WxH` `--out <take>/stills` `--background` `--set …`; writes `stills.json` |
|
|
87
|
+
| `render` | `--width` `--height` `--fps` `--format webm\|mp4` `--parallel N` (1..16 sessions) `--range a..b` (output seconds; keeps its audio) `--draft` `--frame <kind>` `--background` `--set …`; `out` defaults to `<take>/out.<format>` |
|
|
88
|
+
| `open` | `--studio http://localhost:6060` `--print` (print the URL, do not launch a browser) |
|
|
89
89
|
|
|
90
90
|
**Digest first.** `vos digest <take>` is how an agent sees a recording without reading the video. It writes `digest/digest.json`: one moment per thing the cursor track says mattered (click clusters, typing sessions, scroll runs, dwells, idle gaps, head, tail, and frame-diff scene changes), each with source and output extents, a normalized `focus` and `rect` you can copy into a zoom span, per-second `activity`, and the planners' `proposed` span ids; plus one footage frame and a crop around the target per moment, and `sheet.png`, the contact sheet. Read the JSON, then the sheet, then a crop only where you must decide. `vos validate` then warns when a zoom does not contain what was clicked under it, and `vos frames --at-moments` renders the composed output at every moment so a still and its footage crop share an id.
|
|
91
91
|
|
|
92
|
-
**Many media in one take (concat).** `doc.json` may carry `media: [{ id, videoKey, cursor, meta, … }]`, the take's OTHER recordings or uploads, each the `source` shape with an `id`; a segment on one (`{ in, out, media: "m1" }`) plays it, and a zoom, tilt, speed, freeze or cam-move span names the media its source seconds belong to (absent = the primary, `source`). The Video row shows the clips in order, a span is drawn where its media plays, `vos plan` proposes zoom and speed spans on every media from its own cursor track, `vos validate` measures each span against its own media's length, and `vos push` and `vos pull --media` carry every media through the recording door. A media wears its OWN card: `media[].frame` carries the card-owned fields (its placement and size as `inset`, the browser bar, the corner, the shadow, the border, the cover fit and its focus) over the take's frame while it plays; the bar names that media's recorded page, an upload with no page wears none, and the frame-wide fields (the aspect, the padding, the ground, the backdrop, the card's animation) stay the take's. Sound: the primary's tracks play at the primary's moments; another media's own audio is not spliced into the cut yet.
|
|
92
|
+
**Many media in one take (concat).** `doc.json` may carry `media: [{ id, videoKey, cursor, meta, … }]`, the take's OTHER recordings or uploads, each the `source` shape with an `id`; a segment on one (`{ in, out, media: "m1" }`) plays it, and a zoom, tilt, speed, freeze or cam-move span names the media its source seconds belong to (absent = the primary, `source`). The Video row shows the clips in order, a span is drawn where its media plays, `vos plan` proposes zoom and speed spans on every media from its own cursor track, `vos validate` measures each span against its own media's length, and `vos push` and `vos pull --media` carry every media through the recording door. A media wears its OWN card: `media[].frame` carries the card-owned fields (its placement and size as `inset`, the browser bar, the corner, the shadow, the border, the cover fit and its focus) over the take's frame while it plays; the bar names that media's recorded page, an upload with no page wears none, and the frame-wide fields (the aspect, the padding, the ground, the backdrop, the card's animation) stay the take's. Sound: the primary's tracks play at the primary's moments; another media's own audio is not spliced into the cut yet. **Many cards**: an image or video overlay may show a document media by reference (`key: "media:<id>"`, `media:` alone the primary; its cursor track and recorded page come with it) and wear a card (`frame`: `browserBar`, a `lean` `{rx, ry}` in degrees, `shadow`, `shadowContact`, `shadowColor`, `cursor`), drawn by the card painter on its own plane above the primary card with the media's cursor dot and click rings inside it; a layer without `frame` stays the flat picture. The primary card stays primary: the sequence, the camera and the cut are its.
|
|
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 `
|
|
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`, `fade` or `slide` and leaving by `recede` or `fade`), a text, image or video clip, a prop, and a FOOTAGE clip at its boundaries (`segments[].anim`: `exit` is how it leaves at the boundary after it, `enter` how it arrives at the one before, by `slide`, `fade` or `scale`; a slide names its `side`, a step its `seconds`, never longer than half the shorter clip). An exit and the next clip's enter at one boundary run together as a push: the incoming clip plays live while the outgoing, frozen on its last frame, moves away, the camera rests through the window, and one cursor dot crosses from the outgoing card's last point to the incoming's. A page change inside one recording is the same primitive: two clips of one media, the load between them cut. 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, a transition at every page change the recorder marked (`meta.steps[].navigated`: the outgoing clip ends on the frame before the change, the incoming begins once the page has settled; `transitions: slide|fade|scale|none`, slide by default), from LAUNCH.md's `entrance`, `transitions`, `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
|
|
|
@@ -185,6 +185,12 @@ import {
|
|
|
185
185
|
CAM_SIZE_MIN,
|
|
186
186
|
CAM_SPAN_MIN,
|
|
187
187
|
CARD_ENTER_KINDS,
|
|
188
|
+
SEGMENT_ENTER_KINDS,
|
|
189
|
+
SEGMENT_EXIT_KINDS,
|
|
190
|
+
enterOf,
|
|
191
|
+
exitOf,
|
|
192
|
+
segmentOutputExtents,
|
|
193
|
+
transitionSeconds,
|
|
188
194
|
CARD_EXIT_KINDS,
|
|
189
195
|
EXPORT_RESOLUTION_OPTIONS,
|
|
190
196
|
FREEZE_SECONDS_MAX,
|
|
@@ -219,8 +225,11 @@ var STEP_KINDS_ALL = [
|
|
|
219
225
|
"typewriter",
|
|
220
226
|
"tilt-in",
|
|
221
227
|
"pull-out",
|
|
222
|
-
"recede"
|
|
228
|
+
"recede",
|
|
229
|
+
"slide",
|
|
230
|
+
"scale"
|
|
223
231
|
];
|
|
232
|
+
var STEP_SIDES = ["left", "right", "up", "down"];
|
|
224
233
|
var STEP_UNITS = ["block", "line", "word", "char"];
|
|
225
234
|
var STEP_DIRS = ["forward", "reverse", "center"];
|
|
226
235
|
function lintAnim(name, anim, kinds, problems) {
|
|
@@ -270,6 +279,14 @@ function lintAnim(name, anim, kinds, problems) {
|
|
|
270
279
|
if (key === "stagger" && (typeof obj.stagger !== "number" || obj.stagger < 0 || obj.stagger > 2))
|
|
271
280
|
problems.push(`${name}.anim.${side}.stagger must be seconds in 0..2`);
|
|
272
281
|
}
|
|
282
|
+
if (obj.side !== void 0) {
|
|
283
|
+
if (kind !== "slide")
|
|
284
|
+
problems.push(`${name}.anim.${side}.side is for a slide`);
|
|
285
|
+
else if (typeof obj.side !== "string" || !STEP_SIDES.includes(obj.side))
|
|
286
|
+
problems.push(
|
|
287
|
+
`${name}.anim.${side}.side must be ${STEP_SIDES.join(" | ")}`
|
|
288
|
+
);
|
|
289
|
+
}
|
|
273
290
|
}
|
|
274
291
|
if (a.idle !== void 0 && a.idle !== null) {
|
|
275
292
|
if (!kinds.idle) problems.push(`${name}.anim.idle is for props only`);
|
|
@@ -547,6 +564,56 @@ function lintDoc(docIn) {
|
|
|
547
564
|
problems.push("segments must be an array of {in, out} spans");
|
|
548
565
|
}
|
|
549
566
|
checkByMedia(entries(doc.segments), "segment");
|
|
567
|
+
const segs = Array.isArray(doc.segments) ? doc.segments.filter(isObj) : [];
|
|
568
|
+
segs.forEach((seg, i) => {
|
|
569
|
+
lintAnim(
|
|
570
|
+
`segments[${i}]`,
|
|
571
|
+
seg.anim,
|
|
572
|
+
{
|
|
573
|
+
enter: SEGMENT_ENTER_KINDS,
|
|
574
|
+
exit: SEGMENT_EXIT_KINDS,
|
|
575
|
+
idle: null,
|
|
576
|
+
words: false
|
|
577
|
+
},
|
|
578
|
+
problems
|
|
579
|
+
);
|
|
580
|
+
});
|
|
581
|
+
if (segs.length && segs.every((s) => isNum(s.in) && isNum(s.out))) {
|
|
582
|
+
const anims = segs.map(
|
|
583
|
+
(s) => s.anim
|
|
584
|
+
);
|
|
585
|
+
if (enterOf(anims[0]) && enterOf(anims[0]).kind !== "none")
|
|
586
|
+
warnings.push(
|
|
587
|
+
"segments[0].anim.enter is ignored: the first clip arrives with the card (frame.anim.enter)"
|
|
588
|
+
);
|
|
589
|
+
const last = segs.length - 1;
|
|
590
|
+
if (last > 0 && exitOf(anims[last]) && exitOf(anims[last]).kind !== "none")
|
|
591
|
+
warnings.push(
|
|
592
|
+
`segments[${last}].anim.exit is ignored: the last clip leaves with the card (frame.anim.exit)`
|
|
593
|
+
);
|
|
594
|
+
let extents = [];
|
|
595
|
+
try {
|
|
596
|
+
extents = segmentOutputExtents(doc);
|
|
597
|
+
} catch {
|
|
598
|
+
extents = [];
|
|
599
|
+
}
|
|
600
|
+
for (let i = 0; i + 1 < segs.length && extents.length === segs.length; i++) {
|
|
601
|
+
const cap = Math.min(
|
|
602
|
+
extents[i].end - extents[i].start,
|
|
603
|
+
extents[i + 1].end - extents[i + 1].start
|
|
604
|
+
) / 2;
|
|
605
|
+
const exitS = transitionSeconds(exitOf(anims[i]));
|
|
606
|
+
const enterS = transitionSeconds(enterOf(anims[i + 1]));
|
|
607
|
+
if (exitS > cap + EPS)
|
|
608
|
+
problems.push(
|
|
609
|
+
`segments[${i}].anim.exit: ${exitS}s is longer than half the shorter clip at that boundary (${cap.toFixed(2)}s); the clip must be seen still before it moves`
|
|
610
|
+
);
|
|
611
|
+
if (enterS > cap + EPS)
|
|
612
|
+
problems.push(
|
|
613
|
+
`segments[${i + 1}].anim.enter: ${enterS}s is longer than half the shorter clip at that boundary (${cap.toFixed(2)}s); the clip must be seen still before it moves`
|
|
614
|
+
);
|
|
615
|
+
}
|
|
616
|
+
}
|
|
550
617
|
}
|
|
551
618
|
const speed = entries(doc.speed);
|
|
552
619
|
checkByMedia(speed, "speed");
|
|
@@ -988,6 +1055,39 @@ function lintDoc(docIn) {
|
|
|
988
1055
|
if (o.fx !== void 0) {
|
|
989
1056
|
problems.push(`${name}.fx is text-only (media clips have enter/exit)`);
|
|
990
1057
|
}
|
|
1058
|
+
if (typeof o.key === "string" && o.key.startsWith("media:")) {
|
|
1059
|
+
const ref = o.key.slice("media:".length);
|
|
1060
|
+
const ids = new Set(
|
|
1061
|
+
(Array.isArray(doc.media) ? doc.media : []).map((m) => typeof m.id === "string" ? m.id : "")
|
|
1062
|
+
);
|
|
1063
|
+
if (ref !== "" && !ids.has(ref))
|
|
1064
|
+
problems.push(
|
|
1065
|
+
`${name}.key names media "${ref}", which is not one of media[].id (media: alone is the primary)`
|
|
1066
|
+
);
|
|
1067
|
+
}
|
|
1068
|
+
const lcf = o.frame;
|
|
1069
|
+
if (lcf !== void 0) {
|
|
1070
|
+
if (!isObj(lcf)) {
|
|
1071
|
+
problems.push(`${name}.frame must be an object (the layer's card)`);
|
|
1072
|
+
} else {
|
|
1073
|
+
const LCF = [
|
|
1074
|
+
"browserBar",
|
|
1075
|
+
"lean",
|
|
1076
|
+
"shadow",
|
|
1077
|
+
"shadowContact",
|
|
1078
|
+
"shadowColor",
|
|
1079
|
+
"cursor"
|
|
1080
|
+
];
|
|
1081
|
+
for (const k of Object.keys(lcf))
|
|
1082
|
+
if (!LCF.includes(k))
|
|
1083
|
+
problems.push(
|
|
1084
|
+
`${name}.frame.${k}: not a layer card field (${LCF.join(", ")})`
|
|
1085
|
+
);
|
|
1086
|
+
const lean = lcf.lean;
|
|
1087
|
+
if (lean !== void 0 && (!isObj(lean) || !isNum(lean.rx) || !isNum(lean.ry)))
|
|
1088
|
+
problems.push(`${name}.frame.lean must be { rx, ry } in degrees`);
|
|
1089
|
+
}
|
|
1090
|
+
}
|
|
991
1091
|
if (typeof o.key !== "string" || o.key.length === 0) {
|
|
992
1092
|
problems.push(
|
|
993
1093
|
`${name}.key must be a non-empty media URL or take-dir file (e.g. "/logo.png")`
|
|
@@ -3531,6 +3631,43 @@ var BED_ID = "bed";
|
|
|
3531
3631
|
var CLICK_ID_PREFIX = "click-";
|
|
3532
3632
|
var CAPTION_ID_PREFIX = "caption-";
|
|
3533
3633
|
var off = (v) => v !== void 0 && /^(none|off|false|no)$/i.test(v.trim());
|
|
3634
|
+
var round3 = (v) => Math.round(v * 1e3) / 1e3;
|
|
3635
|
+
var TRANSITION_CLIP_MIN = 1;
|
|
3636
|
+
function proposeTransitions(input, kind) {
|
|
3637
|
+
const doc = structuredClone(input);
|
|
3638
|
+
const skipped = [];
|
|
3639
|
+
let count = 0;
|
|
3640
|
+
const steps = doc.source.meta.steps ?? [];
|
|
3641
|
+
for (const step of steps) {
|
|
3642
|
+
if (!step.navigated || step.skipped) continue;
|
|
3643
|
+
const name = step.id ?? String(step.step);
|
|
3644
|
+
const wait = step.do === "wait";
|
|
3645
|
+
const outEnd = round3(wait ? step.tStart : step.tEnd);
|
|
3646
|
+
const inStart = round3(wait ? step.tEnd : step.tEnd + STEP_SETTLE_SECONDS);
|
|
3647
|
+
const segs = doc.segments;
|
|
3648
|
+
if (segs.some(
|
|
3649
|
+
(s) => !s.media && Math.abs(s.out - outEnd) < 0.05 && s !== segs.at(-1)
|
|
3650
|
+
))
|
|
3651
|
+
continue;
|
|
3652
|
+
const i = segs.findIndex(
|
|
3653
|
+
(s) => !s.media && outEnd >= s.in + TRANSITION_CLIP_MIN && inStart <= s.out - TRANSITION_CLIP_MIN
|
|
3654
|
+
);
|
|
3655
|
+
if (i < 0) {
|
|
3656
|
+
skipped.push(
|
|
3657
|
+
`no transition at step ${name}: the page change sits too close to a clip's edge`
|
|
3658
|
+
);
|
|
3659
|
+
continue;
|
|
3660
|
+
}
|
|
3661
|
+
const seg = segs[i];
|
|
3662
|
+
const before = { ...seg, out: outEnd, anim: { exit: kind } };
|
|
3663
|
+
const after = { ...seg, in: inStart, anim: { enter: kind } };
|
|
3664
|
+
if (seg.anim?.enter !== void 0) before.anim.enter = seg.anim.enter;
|
|
3665
|
+
if (seg.anim?.exit !== void 0) after.anim.exit = seg.anim.exit;
|
|
3666
|
+
doc.segments = [...segs.slice(0, i), before, after, ...segs.slice(i + 1)];
|
|
3667
|
+
count++;
|
|
3668
|
+
}
|
|
3669
|
+
return { doc, count, skipped };
|
|
3670
|
+
}
|
|
3534
3671
|
function pickTrack(catalog, ask) {
|
|
3535
3672
|
if (!catalog || !ask || off(ask)) return null;
|
|
3536
3673
|
const want = ask.trim().toLowerCase();
|
|
@@ -3601,6 +3738,14 @@ function proposeMotion(input, opts) {
|
|
|
3601
3738
|
if (Object.keys(rest).length) doc.frame.anim = rest;
|
|
3602
3739
|
else delete doc.frame.anim;
|
|
3603
3740
|
}
|
|
3741
|
+
const trRole = launch.transitions;
|
|
3742
|
+
if (!off(trRole)) {
|
|
3743
|
+
const trKind = trRole && /^(slide|fade|scale)$/.test(trRole.trim()) ? trRole.trim() : "slide";
|
|
3744
|
+
const made = proposeTransitions(doc, trKind);
|
|
3745
|
+
doc = made.doc;
|
|
3746
|
+
if (made.count) notes.push(`${made.count} transition(s) ${trKind}`);
|
|
3747
|
+
for (const n of made.skipped) skipped.push(n);
|
|
3748
|
+
}
|
|
3604
3749
|
const endCardRole = launch.endCard;
|
|
3605
3750
|
doc = dropTemplate(doc, END_CARD_FROM);
|
|
3606
3751
|
const templates = (opts.templates ?? []).filter(
|
|
@@ -5671,6 +5816,7 @@ async function recordTake(browser, url, actions, paths, log, opts = {}) {
|
|
|
5671
5816
|
}
|
|
5672
5817
|
const stepStart = now();
|
|
5673
5818
|
const skippedBefore = skipped.length;
|
|
5819
|
+
const urlBefore = page.url();
|
|
5674
5820
|
switch (step.do) {
|
|
5675
5821
|
case "wait":
|
|
5676
5822
|
await sleep(clampWait(step.ms, now(), maxSeconds));
|
|
@@ -5802,7 +5948,8 @@ async function recordTake(browser, url, actions, paths, log, opts = {}) {
|
|
|
5802
5948
|
...stepInfo.selector ? { selector: stepInfo.selector } : {},
|
|
5803
5949
|
tStart: +(stepStart / 1e3).toFixed(3),
|
|
5804
5950
|
tEnd: +(now() / 1e3).toFixed(3),
|
|
5805
|
-
...skipped.length > skippedBefore ? { skipped: true } : {}
|
|
5951
|
+
...skipped.length > skippedBefore ? { skipped: true } : {},
|
|
5952
|
+
...page.url() !== urlBefore ? { navigated: true } : {}
|
|
5806
5953
|
});
|
|
5807
5954
|
}
|
|
5808
5955
|
if (!capped) await sleep(600);
|
|
@@ -6381,7 +6528,7 @@ var KEYS = [
|
|
|
6381
6528
|
{ key: "camKey", file: CAM_NAME }
|
|
6382
6529
|
];
|
|
6383
6530
|
function isTakeRelativeKey(key) {
|
|
6384
|
-
return !!key && !/^(https?:|blob:|data:|\/\/)/.test(key) && !key.startsWith("/api/");
|
|
6531
|
+
return !!key && !/^(https?:|blob:|data:|media:|\/\/)/.test(key) && !key.startsWith("/api/");
|
|
6385
6532
|
}
|
|
6386
6533
|
var takeRelativeFile = (key) => key.replace(/^\/+/, "");
|
|
6387
6534
|
function docMediaRefs(doc, keep = isTakeRelativeKey) {
|
|
@@ -9001,7 +9148,7 @@ var HELP = `vos \u2014 record a browser flow, plan effects, render a product vid
|
|
|
9001
9148
|
Take pipeline
|
|
9002
9149
|
vos create --actions actions.json [--url <url>] [--out take] [out.webm] [--strict] [--max-duration <s>] [--background <slug|url|none>] [render flags] [--json]
|
|
9003
9150
|
vos record --actions actions.json [--url <url>] [--out take] [--strict] [--max-duration <s>] [--background <slug|url|none>] [--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]
|
|
9151
|
+
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]
|
|
9005
9152
|
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]
|
|
9006
9153
|
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]
|
|
9007
9154
|
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]
|
|
@@ -9147,7 +9294,7 @@ from BRAND.md logoUrl and a freeze of the last frame under it, from:
|
|
|
9147
9294
|
endcard; offline, the house clips stand in), a CAPTION per
|
|
9148
9295
|
actions.json step at the step's moment, a music BED from LAUNCH.md's music
|
|
9149
9296
|
role (a catalog slug or a mood) and a click sound on every press when the
|
|
9150
|
-
take has no mic. LAUNCH.md's entrance, endCard, captions, music and clicks
|
|
9297
|
+
take has no mic. LAUNCH.md's entrance, transitions, endCard, captions, music and clicks
|
|
9151
9298
|
roles, or the flags, change or switch each off; a deleted proposal stays
|
|
9152
9299
|
deleted on a refresh. deliver applies each destination's MECHANICS and
|
|
9153
9300
|
nothing more: the README loop drops the card's motion, every clip a
|
|
@@ -9576,6 +9723,7 @@ async function releaseInputs(dir, flags, r, withRefs = []) {
|
|
|
9576
9723
|
for (const [flag, role] of [
|
|
9577
9724
|
["music", "music"],
|
|
9578
9725
|
["entrance", "entrance"],
|
|
9726
|
+
["transitions", "transitions"],
|
|
9579
9727
|
["end-card", "endCard"],
|
|
9580
9728
|
["captions", "captions"],
|
|
9581
9729
|
["clicks", "clicks"]
|
|
@@ -10388,4 +10536,4 @@ export {
|
|
|
10388
10536
|
convertAgentBrowser,
|
|
10389
10537
|
run
|
|
10390
10538
|
};
|
|
10391
|
-
//# sourceMappingURL=chunk-
|
|
10539
|
+
//# sourceMappingURL=chunk-T35XY4YW.js.map
|