@vosjs/cli 0.65.4 → 0.66.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-IDPTF5PP.js → chunk-SFU4U3GM.js} +75 -22
- package/dist/chunk-SFU4U3GM.js.map +1 -0
- package/dist/cli.js +3 -3
- package/dist/index.js +1 -1
- package/dist/{run-J7CQANKJ.js → run-OY6B5AFZ.js} +2 -2
- package/package.json +3 -3
- package/schema/doc.schema.json +39 -3
- package/dist/chunk-IDPTF5PP.js.map +0 -1
- /package/dist/{run-J7CQANKJ.js.map → run-OY6B5AFZ.js.map} +0 -0
package/README.md
CHANGED
|
@@ -263,7 +263,7 @@ Verified the flow in agent-browser already? `vos actions from-agent-browser step
|
|
|
263
263
|
|
|
264
264
|
**Tilt.** `doc.tilt`: source-anchored, non-overlapping spans where the card leans to a pose and returns to rest: `[{ "id": "u0", "in": 4, "out": 8, "rx": 6, "ry": -9, "source": "manual" }]`. `rx`/`ry` are degrees (±45 hard limit; ±5..18 reads well); `+rx` brings the top edge toward the camera, `+ry` the left edge, so a lean toward a right-side focus is a negative `ry`. Rest is flat. `doc.tiltStyle` (`off`, `subtle`, `medium`, `strong`) is the intensity the planner derives automatic spans from.
|
|
265
265
|
|
|
266
|
-
**Text overlays.** `doc.overlays`: screen-space clips above the card, outside the zoom, output-anchored. `{ "id": "t0", "kind": "text", "start": 1, "duration": 3, "text": "Ship it", "preset": "title", "transform": { "x": 0.5, "y": 0.82, "scale": 1, "rotation": 0 }, "enter": "rise", "exit": "fade" }`. Presets `title`, `caption`, `label`, overridable with `size` (12..200 design px) and `color`; `\n` breaks lines; a caption (a layer with no referent) is a lower third at `y` ≈ 0.82. Enter and exit: `rise`, `fade`, `none`. Fonts load at render start, fail-open to system stacks.
|
|
266
|
+
**Text overlays.** `doc.overlays`: screen-space clips above the card, outside the zoom, output-anchored. `{ "id": "t0", "kind": "text", "start": 1, "duration": 3, "text": "Ship it", "preset": "title", "transform": { "x": 0.5, "y": 0.82, "scale": 1, "rotation": 0 }, "enter": "rise", "exit": "fade" }`. Presets `title`, `caption`, `label`, overridable with `size` (12..200 design px) and `color`; `\n` breaks lines; a caption (a layer with no referent) is a lower third at `y` ≈ 0.82. Enter and exit: `rise`, `fade`, `none`. Fonts load at render start, fail-open to system stacks. **Styled words are runs.** `text` is a string, or a list of runs when parts of it are set differently: `"text": [{ "text": "Ship " }, { "text": "faster", "weight": 700, "underline": true }]`. A run is a piece of text plus the fields in which it departs from the layer: `weight` (700 is bold; snapped to what the family hosts), `italic`, `color`, `underline`, `strike`, `highlight` (a colour behind it). There is no markup in the words (an asterisk is an asterisk), a style may start and end anywhere, a run may hold `\n`, and a per-word or per-character entrance keeps every unit in its own style. `vos validate` names a run field it does not know and prints the runs to write for a layer still using the retired `emphasis`.
|
|
267
267
|
|
|
268
268
|
**Callouts in the grammar.** `vos callout <take> note --step copy --kicker "index.css" --title "Every token you tuned, as CSS variables." --mark ring` writes a callout in the house grammar (three shapes a viewer learns once: `note`, a kicker, a title and a line; `tag`, one label on the accent; `code`, the payload block) from the product's REGISTER: `BRAND.md` beside the take (`bgA` the ground, `accent`, `fontBody`; a kit whose accent is RESERVED, a recorder's red that marks time and nothing else, names a `callout` role and the notes speak in that hue instead) or `--ground #hex --accent #hex --font "…"`. The card inverts the app's value in the product's hue (a light app gets a dark card, a dark app a light one), keeps the accent for the kicker, sets its title at 1.35× the app's body AS SEEN ON SCREEN at the layer's start (the camera's level is read from the document; `--body-px <n>` instead sets the body size on the DELIVERED frame and is never rescaled, which is the flag a rich capture needs, since a 2560-wide take on a padded frame sits near scale 0.6 and lands every default on the grammar's floor; the verb prints the three sizes and the scale it measured), lifts on a real shadow and a hairline, rises in and fades out, opens a beat after the step's press lands and closes before the next scroll or navigation, and is pinned to the step (`--side`, `--mark`, `--leader`, `--color` refine the pin; `--color` paints the mark and the leader, never the kicker, which is the accent). `--at <s> --seconds <n>` places one without a step; `--print` prints the clip instead of writing it. `vos validate <take> --picture` renders the footage under every html layer at its start and reports the ΔE between the card's ground and what it covers: under 8 is a problem (the card reads as one more panel), under 16 a warning. An unpinned html or media layer whose window holds a step with an element gets the pin named in a warning.
|
|
269
269
|
|
|
@@ -214,7 +214,12 @@ import {
|
|
|
214
214
|
pinCandidates,
|
|
215
215
|
pinRectOnScreen,
|
|
216
216
|
pinReferent,
|
|
217
|
-
|
|
217
|
+
marksEmphasis,
|
|
218
|
+
migrateTextClip,
|
|
219
|
+
plainText,
|
|
220
|
+
resolveOverlayStyle,
|
|
221
|
+
snapRunWeight,
|
|
222
|
+
RUN_STYLE_KEYS,
|
|
218
223
|
ratedSegments,
|
|
219
224
|
recommendedExportResolution,
|
|
220
225
|
resolvePins,
|
|
@@ -244,7 +249,7 @@ function wordsOf(text) {
|
|
|
244
249
|
return text.toLowerCase().split(/[^a-z0-9]+/).filter((x) => x.length >= 3 && !LABEL_STOPWORDS.has(x));
|
|
245
250
|
}
|
|
246
251
|
function layerWords(o) {
|
|
247
|
-
const src = typeof o.html === "string" ? o.html.replace(/<[^>]*>/g, " ") : typeof o.text === "string" ? o.text : "";
|
|
252
|
+
const src = typeof o.html === "string" ? o.html.replace(/<[^>]*>/g, " ") : typeof o.text === "string" || Array.isArray(o.text) ? plainText(o.text) : "";
|
|
248
253
|
return new Set(wordsOf(src));
|
|
249
254
|
}
|
|
250
255
|
function selectorLabelWords(selector) {
|
|
@@ -1063,28 +1068,75 @@ function lintDoc(docIn) {
|
|
|
1063
1068
|
}
|
|
1064
1069
|
checkPin(o, name, docIn, recording, problems, warnings);
|
|
1065
1070
|
if (o.kind === "text") {
|
|
1066
|
-
if (
|
|
1067
|
-
|
|
1071
|
+
if (Array.isArray(o.text)) {
|
|
1072
|
+
const HINTS = {
|
|
1073
|
+
bold: "weight: 700",
|
|
1074
|
+
fontWeight: "weight",
|
|
1075
|
+
strikethrough: "strike: true",
|
|
1076
|
+
background: "highlight",
|
|
1077
|
+
colour: "color"
|
|
1078
|
+
};
|
|
1079
|
+
o.text.forEach((run2, ri) => {
|
|
1080
|
+
const at2 = `${name}.text[${ri}]`;
|
|
1081
|
+
if (!run2 || typeof run2 !== "object" || Array.isArray(run2)) {
|
|
1082
|
+
problems.push(
|
|
1083
|
+
`${at2} must be a run: { text, weight?, italic?, color?, underline?, strike?, highlight? }`
|
|
1084
|
+
);
|
|
1085
|
+
return;
|
|
1086
|
+
}
|
|
1087
|
+
const r = run2;
|
|
1088
|
+
if (typeof r.text !== "string")
|
|
1089
|
+
problems.push(`${at2}.text must be a string`);
|
|
1090
|
+
else if (!r.text)
|
|
1091
|
+
warnings.push(`${at2} has no text and paints nothing; drop it`);
|
|
1092
|
+
for (const k of Object.keys(r)) {
|
|
1093
|
+
if (k === "text" || RUN_STYLE_KEYS.includes(k))
|
|
1094
|
+
continue;
|
|
1095
|
+
problems.push(
|
|
1096
|
+
`${at2}.${k} is not a run field (${RUN_STYLE_KEYS.join(", ")})${HINTS[k] ? `: write ${HINTS[k]}` : ""}`
|
|
1097
|
+
);
|
|
1098
|
+
}
|
|
1099
|
+
if (r.weight !== void 0 && (!isNum(r.weight) || r.weight < 100 || r.weight > 900))
|
|
1100
|
+
problems.push(`${at2}.weight must be 100..900`);
|
|
1101
|
+
for (const k of ["italic", "underline", "strike"]) {
|
|
1102
|
+
if (r[k] !== void 0 && typeof r[k] !== "boolean")
|
|
1103
|
+
problems.push(`${at2}.${k} must be true or false`);
|
|
1104
|
+
}
|
|
1105
|
+
for (const k of ["color", "highlight"]) {
|
|
1106
|
+
if (r[k] !== void 0 && typeof r[k] !== "string")
|
|
1107
|
+
problems.push(`${at2}.${k} must be a CSS colour`);
|
|
1108
|
+
}
|
|
1109
|
+
if (isNum(r.weight) && r.weight >= 100 && r.weight <= 900) {
|
|
1110
|
+
const layer = o;
|
|
1111
|
+
const own = resolveOverlayStyle({ ...layer, text: "" }).weight;
|
|
1112
|
+
if (r.weight !== own && snapRunWeight(layer, r.weight) === own)
|
|
1113
|
+
warnings.push(
|
|
1114
|
+
`${at2}.weight ${r.weight} paints no differently: the layer's family hosts no weight nearer to it than the layer's own ${own}`
|
|
1115
|
+
);
|
|
1116
|
+
}
|
|
1117
|
+
});
|
|
1118
|
+
} else if (typeof o.text !== "string") {
|
|
1119
|
+
problems.push(
|
|
1120
|
+
`${name}.text must be a string, or a list of runs ([{ text, weight?, italic?, color?, underline?, strike?, highlight? }])`
|
|
1121
|
+
);
|
|
1122
|
+
}
|
|
1068
1123
|
if (typeof o.preset === "string" && !PRESETS.includes(o.preset)) {
|
|
1069
1124
|
warnings.push(
|
|
1070
1125
|
`${name}.preset "${String(o.preset)}" is not a known preset (${PRESETS.join("|")}) \u2014 falls back to "title"`
|
|
1071
1126
|
);
|
|
1072
1127
|
}
|
|
1073
|
-
if (o.emphasis !== void 0) {
|
|
1074
|
-
const
|
|
1075
|
-
|
|
1076
|
-
|
|
1077
|
-
|
|
1078
|
-
|
|
1079
|
-
|
|
1080
|
-
|
|
1081
|
-
|
|
1082
|
-
|
|
1083
|
-
problems.push(`${name}.emphasis.color must be a CSS colour`);
|
|
1084
|
-
}
|
|
1085
|
-
} else if (typeof o.text === "string" && parseEmphasis(o.text) !== null) {
|
|
1128
|
+
if (o.emphasis !== void 0 && typeof o.text === "string") {
|
|
1129
|
+
const runs = migrateTextClip(o).text;
|
|
1130
|
+
warnings.push(
|
|
1131
|
+
`${name}.emphasis is retired (styled words are runs now, with no markers in the text). It is still read, but write it as: "text": ${JSON.stringify(runs)} and drop "emphasis"`
|
|
1132
|
+
);
|
|
1133
|
+
} else if (typeof o.text === "string" && marksEmphasis(o.text)) {
|
|
1134
|
+
const runs = migrateTextClip({
|
|
1135
|
+
...o,
|
|
1136
|
+
emphasis: {}
|
|
1137
|
+
}).text;
|
|
1086
1138
|
warnings.push(
|
|
1087
|
-
`${name}.text has
|
|
1139
|
+
`${name}.text has words between asterisks, which show as typed. To set them bold write runs: "text": ${JSON.stringify(runs)}`
|
|
1088
1140
|
);
|
|
1089
1141
|
}
|
|
1090
1142
|
if (o.size !== void 0 && (!isNum(o.size) || o.size < 12 || o.size > 200)) {
|
|
@@ -4333,8 +4385,9 @@ import {
|
|
|
4333
4385
|
computeCardLayout,
|
|
4334
4386
|
docStillTime as docStillTime2,
|
|
4335
4387
|
migrateHostedDoc,
|
|
4388
|
+
overlayPlainText,
|
|
4336
4389
|
overlayRect,
|
|
4337
|
-
resolveOverlayStyle
|
|
4390
|
+
resolveOverlayStyle as resolveOverlayStyle2
|
|
4338
4391
|
} from "@vosjs/studio-core";
|
|
4339
4392
|
var POSTER_CLASSES = [
|
|
4340
4393
|
"landscape",
|
|
@@ -4453,7 +4506,7 @@ function posterTextBoxes(doc, px, time) {
|
|
|
4453
4506
|
if (clip.kind !== "text") continue;
|
|
4454
4507
|
if (time < clip.start || time > clip.start + clip.duration) continue;
|
|
4455
4508
|
const rect = overlayRect(clip, estimateWidth, W, H);
|
|
4456
|
-
const style =
|
|
4509
|
+
const style = resolveOverlayStyle2(clip);
|
|
4457
4510
|
const r3 = (v) => Math.round(v * 1e3) / 1e3;
|
|
4458
4511
|
out.push({
|
|
4459
4512
|
x: r3((rect.cx - rect.w / 2) / W),
|
|
@@ -4461,7 +4514,7 @@ function posterTextBoxes(doc, px, time) {
|
|
|
4461
4514
|
w: r3(rect.w / W),
|
|
4462
4515
|
h: r3(rect.h / H),
|
|
4463
4516
|
role: clip.preset === "title" ? "headline" : "body",
|
|
4464
|
-
label: clip.
|
|
4517
|
+
label: overlayPlainText(clip).split("\n")[0].slice(0, 40),
|
|
4465
4518
|
.../^#[0-9a-f]{6}$/i.test(style.color) ? { color: style.color } : {}
|
|
4466
4519
|
});
|
|
4467
4520
|
}
|
|
@@ -15093,4 +15146,4 @@ export {
|
|
|
15093
15146
|
verbHelp,
|
|
15094
15147
|
run
|
|
15095
15148
|
};
|
|
15096
|
-
//# sourceMappingURL=chunk-
|
|
15149
|
+
//# sourceMappingURL=chunk-SFU4U3GM.js.map
|