@vosjs/cli 0.28.0 → 0.29.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/README.md CHANGED
@@ -33,7 +33,7 @@ Until 0.9 the take pipeline and the vos.so verbs shipped separately as `@vosso/v
33
33
 
34
34
  ## Requirements
35
35
 
36
- Node 18 or newer and a Chromium-family browser. A system Chrome is used when present; otherwise `npx playwright install chromium` once, or set `VOS_BROWSER_PATH`. mp4 output needs Chrome (Chromium ships no AVC encoder). Render pages load `three`, `gsap` and mediabunny from a CDN, so rendering needs network access.
36
+ Node 18 or newer and a Chromium-family browser. A system Chrome is used when present; otherwise `npx playwright install chromium` once, or set `VOS_BROWSER_PATH`. mp4 output needs Chrome (Chromium ships no AVC encoder). Render pages load `three` and mediabunny from a CDN, so rendering needs network access.
37
37
 
38
38
  ## Engine verbs
39
39
 
@@ -161,6 +161,8 @@ Verified the flow in agent-browser already? `vos actions from-agent-browser step
161
161
 
162
162
  **Image and video overlays.** Media kinds on the same lane: `{ "id": "m0", "kind": "image" | "video", "start": 2, "duration": 4, "key": "/logo.png", "width": 0.35, "radius": 12, "opacity": 1, "loop": false, "transform": { … } }`. `key` is a file inside the take directory (`"/logo.png"`) or a URL; `width` is a fraction of the frame width, height follows the media's aspect; corners in design px. Video time is clip-local and muted by design; soundtracks belong to `doc.audio`.
163
163
 
164
+ **HTML layers.** A UI component authored as DOM on the same lane, sharp at any export size because it is rasterized from source, not magnified from the footage: `{ "id": "h0", "kind": "html", "start": 2, "duration": 4, "html": "<div class=\"card\">Open Code</div>", "css": ".card{font-family:'Inter';background:#0b0b0d;border-radius:16px;box-shadow:0 24px 60px rgba(0,0,0,.45)}", "box": { "width": 452, "height": 132 }, "transform": { … } }`. The markup is well-formed XML (`<br />`, `&amp;`, quoted attributes; `vos validate` refuses the rest in words, because a malformed layer decodes to nothing and says nothing). `box` is the design box in 1080p px; `width` defaults to the design size. The CSS is the chrome (shadow, radius, border; `bleed` is derived from the shadows, or stated), a `font-family` it names resolves against the hosted catalog at every `font-weight` it names (a face outside the catalog rides `fonts: [{ family, url }]`), an image inside must be a `data:` URI, and an `animation` runs on the wall clock rather than the timeline, so animate the layer with `anim` and `motion`. It is never re-hosted and never an upload.
165
+
164
166
  **3D props.** `doc.objects`: world-space props between the card and the overlays. `{ "id": "p0", "asset": { "kind": "primitive", "shape": "knot", "color": "#ffb03a" }, "span": { "start": 1, "duration": 3 }, "transform3d": { "x": 0.8, "y": 0.28, "z": 0.5, "rx": 0, "ry": 40, "rz": 0, "scale": 0.18 }, "animation": "spin" }`. Shapes `cube`, `sphere`, `torus`, `knot`; `kind: "gltf"` with `key` loads a GLB (bounding-box normalized so `scale` means the same for every model; a bad model fails open). `x`/`y` are frame fractions, `z` world units toward the camera from the card plane, `scale` a fraction of the frame height; `animation` is `spin` or `float`, deterministic.
165
167
 
166
168
  **Audio.** `doc.audio` clips (music, effects, `key` a take-directory file or a URL) are mixed with gain, fade and loop envelopes and muxed into every full render: Opus for webm, AAC (Opus fallback) for mp4. Audio forces single-flight (`--parallel` is ignored with a note); a `--range` render keeps its audio.
@@ -189,6 +189,7 @@ import {
189
189
  SEGMENT_EXIT_KINDS,
190
190
  enterOf,
191
191
  exitOf,
192
+ htmlLayerProblems,
192
193
  segmentOutputExtents,
193
194
  transitionSeconds,
194
195
  CARD_EXIT_KINDS,
@@ -926,9 +927,9 @@ function lintDoc(docIn) {
926
927
  problems.push(`${name} must be an object`);
927
928
  continue;
928
929
  }
929
- if (o.kind !== "text" && o.kind !== "image" && o.kind !== "video") {
930
+ if (o.kind !== "text" && o.kind !== "image" && o.kind !== "video" && o.kind !== "html") {
930
931
  problems.push(
931
- `${name}.kind must be "text" | "image" | "video" (got ${String(o.kind)})`
932
+ `${name}.kind must be "text" | "image" | "video" | "html" (got ${String(o.kind)})`
932
933
  );
933
934
  }
934
935
  if (!isNum(o.start) || o.start < 0)
@@ -1105,6 +1106,55 @@ function lintDoc(docIn) {
1105
1106
  problems.push(`${name}.opacity must be 0..1`);
1106
1107
  }
1107
1108
  }
1109
+ if (o.kind === "html") {
1110
+ if (typeof o.html !== "string") {
1111
+ problems.push(`${name}.html must be the layer's markup (a string)`);
1112
+ }
1113
+ if (o.css !== void 0 && typeof o.css !== "string") {
1114
+ problems.push(`${name}.css must be a string of CSS`);
1115
+ }
1116
+ const box = isObj(o.box) ? o.box : void 0;
1117
+ if (!box || !isNum(box.width) || !isNum(box.height) || box.width <= 0 || box.height <= 0) {
1118
+ problems.push(
1119
+ `${name}.box must be { width, height } in design px, both > 0 (the 1080p frame is 1920 wide)`
1120
+ );
1121
+ }
1122
+ if (o.bleed !== void 0 && (!isNum(o.bleed) || o.bleed < 0)) {
1123
+ problems.push(
1124
+ `${name}.bleed must be \u2265 0 design px (absent = derived from the CSS's shadows)`
1125
+ );
1126
+ }
1127
+ if (o.fonts !== void 0) {
1128
+ if (!Array.isArray(o.fonts)) {
1129
+ problems.push(
1130
+ `${name}.fonts must be an array of { family, url, weight?, style? }`
1131
+ );
1132
+ } else {
1133
+ o.fonts.forEach((f, fi) => {
1134
+ if (!isObj(f) || typeof f.family !== "string" || typeof f.url !== "string" || !/^https?:\/\//.test(f.url))
1135
+ problems.push(
1136
+ `${name}.fonts[${fi}] must be { family, url (https), weight?, style? }: a face the catalog does not host. A hosted family needs no entry; name it in the CSS`
1137
+ );
1138
+ });
1139
+ }
1140
+ }
1141
+ if (o.width !== void 0 && (!isNum(o.width) || o.width <= 0 || o.width > 1)) {
1142
+ problems.push(
1143
+ `${name}.width must be a fraction of the frame width in (0..1] (got ${String(o.width)}; absent = the design size)`
1144
+ );
1145
+ }
1146
+ if (o.opacity !== void 0 && (!isNum(o.opacity) || o.opacity < 0 || o.opacity > 1)) {
1147
+ problems.push(`${name}.opacity must be 0..1`);
1148
+ }
1149
+ if (typeof o.html === "string") {
1150
+ for (const p of htmlLayerProblems(o)) {
1151
+ ;
1152
+ (p.level === "problem" ? problems : warnings).push(
1153
+ `${name}: ${p.message}`
1154
+ );
1155
+ }
1156
+ }
1157
+ }
1108
1158
  const tf = isObj(o.transform) ? o.transform : void 0;
1109
1159
  if (!tf) {
1110
1160
  problems.push(
@@ -6515,6 +6565,7 @@ import { basename, join as join15 } from "path";
6515
6565
  import { lowerToComposition as lowerToComposition3, migrateHostedDoc as migrateHostedDoc3 } from "@vosjs/studio-core";
6516
6566
 
6517
6567
  // src/plugin/media.ts
6568
+ import { isKeyedOverlay } from "@vosjs/studio-core";
6518
6569
  import { createWriteStream, existsSync as existsSync11 } from "fs";
6519
6570
  import { writeFile as writeFile9 } from "fs/promises";
6520
6571
  import { join as join13 } from "path";
@@ -6562,7 +6613,7 @@ function docMediaRefs(doc, keep = isTakeRelativeKey) {
6562
6613
  });
6563
6614
  }
6564
6615
  for (const clip of doc.overlays ?? []) {
6565
- if (clip.kind === "text" || !keep(clip.key)) continue;
6616
+ if (!isKeyedOverlay(clip) || !keep(clip.key)) continue;
6566
6617
  out.push({
6567
6618
  where: `overlay ${clip.id}`,
6568
6619
  key: clip.key,
@@ -10536,4 +10587,4 @@ export {
10536
10587
  convertAgentBrowser,
10537
10588
  run
10538
10589
  };
10539
- //# sourceMappingURL=chunk-T35XY4YW.js.map
10590
+ //# sourceMappingURL=chunk-GSRCASV2.js.map