@vosjs/cli 0.58.0 → 0.58.1

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.
@@ -185,6 +185,37 @@ import {
185
185
  unionFitLevel
186
186
  } from "@vosjs/studio-core";
187
187
  import { TYPEFACE_CATALOG, findFontFamily, findTypeface } from "@vosjs/shared";
188
+ var PIN_NEAR_FRACTION = 0.08;
189
+ var PIN_SURFACE_AREA = 0.25;
190
+ var LABEL_STOPWORDS = /* @__PURE__ */ new Set([
191
+ "the",
192
+ "and",
193
+ "for",
194
+ "with",
195
+ "your",
196
+ "you",
197
+ "this",
198
+ "that",
199
+ "from",
200
+ "into",
201
+ "button",
202
+ "link"
203
+ ]);
204
+ function wordsOf(text) {
205
+ return text.toLowerCase().split(/[^a-z0-9]+/).filter((x) => x.length >= 3 && !LABEL_STOPWORDS.has(x));
206
+ }
207
+ function layerWords(o) {
208
+ const src = typeof o.html === "string" ? o.html.replace(/<[^>]*>/g, " ") : typeof o.text === "string" ? o.text : "";
209
+ return new Set(wordsOf(src));
210
+ }
211
+ function selectorLabelWords(selector) {
212
+ const out = [];
213
+ const re = /(?:has-text\(|text=|(?:aria-label|title|alt|placeholder|name)\s*[*^$~|]?=\s*)['"]([^'"]+)['"]/g;
214
+ for (let m = re.exec(selector); m; m = re.exec(selector)) {
215
+ out.push(...wordsOf(m[1]));
216
+ }
217
+ return [...new Set(out)];
218
+ }
188
219
  var STEP_KINDS_ALL = [
189
220
  "none",
190
221
  "fade",
@@ -248,6 +279,20 @@ function lintAnim(name, anim, kinds, problems) {
248
279
  if (key === "stagger" && (typeof obj.stagger !== "number" || obj.stagger < 0 || obj.stagger > 2))
249
280
  problems.push(`${name}.anim.${side}.stagger must be seconds in 0..2`);
250
281
  }
282
+ if (obj.at !== void 0) {
283
+ if (!kinds.card || side !== "enter")
284
+ problems.push(
285
+ `${name}.anim.${side}.at is the card's own: frame.anim.enter.at delays the card's entrance; a clip already starts at its own start`
286
+ );
287
+ else if (typeof obj.at !== "number" || !Number.isFinite(obj.at) || obj.at < 0 || obj.at > 30)
288
+ problems.push(
289
+ `${name}.anim.enter.at must be OUTPUT seconds in 0..30 (when the card arrives; the ground plays alone until then)`
290
+ );
291
+ else if (kind === "none")
292
+ problems.push(
293
+ `${name}.anim.enter.at has nothing to delay: the enter is "none", so the card is on screen from the first frame`
294
+ );
295
+ }
251
296
  if (obj.side !== void 0) {
252
297
  if (kind !== "slide")
253
298
  problems.push(`${name}.anim.${side}.side is for a slide`);
@@ -599,6 +644,7 @@ function lintDoc(docIn) {
599
644
  }
600
645
  const tilt = entries(doc.tilt);
601
646
  checkByMedia(tilt, "tilt");
647
+ const dramatic = [];
602
648
  for (const t of tilt) {
603
649
  const name = spanName("tilt", t);
604
650
  for (const ax of ["rx", "ry"]) {
@@ -611,8 +657,8 @@ function lintDoc(docIn) {
611
657
  }
612
658
  checkTransition(t, name, problems);
613
659
  if (isNum(t.rx) && isNum(t.ry) && Math.abs(t.rx) + Math.abs(t.ry) > 25) {
614
- warnings.push(
615
- `${name}: combined lean ${(Math.abs(t.rx) + Math.abs(t.ry)).toFixed(0)}\xB0 reads dramatic \u2014 the premium band is \xB15..18\xB0 per axis`
660
+ dramatic.push(
661
+ `${name} ${(Math.abs(t.rx) + Math.abs(t.ry)).toFixed(0)}\xB0`
616
662
  );
617
663
  }
618
664
  if (t.source !== void 0 && t.source !== "manual" && t.source !== "auto") {
@@ -624,6 +670,11 @@ function lintDoc(docIn) {
624
670
  );
625
671
  }
626
672
  }
673
+ if (dramatic.length) {
674
+ warnings.push(
675
+ `tilt: ${dramatic.length === 1 ? "a span leans" : `${dramatic.length} spans lean`} past 25\xB0 combined (${dramatic.join(", ")}) and read${dramatic.length === 1 ? "s" : ""} dramatic \u2014 the premium band is \xB15..18\xB0 per axis; keep it if the piece means it`
676
+ );
677
+ }
627
678
  if (typeof doc.zoomStyle === "string" && doc.zoomStyle in RETIRED_ZOOM_STYLES) {
628
679
  const r = RETIRED_ZOOM_STYLES[doc.zoomStyle];
629
680
  warnings.push(
@@ -836,7 +887,8 @@ function lintDoc(docIn) {
836
887
  enter: CARD_ENTER_KINDS,
837
888
  exit: CARD_EXIT_KINDS,
838
889
  idle: null,
839
- words: false
890
+ words: false,
891
+ card: true
840
892
  },
841
893
  problems
842
894
  );
@@ -930,9 +982,12 @@ function lintDoc(docIn) {
930
982
  }
931
983
  if (footageEnd !== null && footageEnd > 0 && isNum(o.start) && isNum(o.duration)) {
932
984
  const over = o.start + o.duration - footageEnd;
933
- if (over > 0.05) {
985
+ const frameAnim = isObj(docIn) && isObj(docIn.frame) && isObj(docIn.frame.anim) ? docIn.frame.anim : null;
986
+ const exit = frameAnim ? isObj(frameAnim.exit) ? frameAnim.exit.kind : frameAnim.exit : void 0;
987
+ const cardLeaves = exit !== void 0 && exit !== "none";
988
+ if (over > 0.05 && !cardLeaves) {
934
989
  warnings.push(
935
- 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`
990
+ o.start >= footageEnd ? `${name} starts at ${o.start.toFixed(2)}s, after the footage ends (${footageEnd.toFixed(2)}s): it plays over the card's held last frame 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), put a freeze under it, or, for a closing card on the ground, let the card leave (frame.anim.exit: 'fade')` : `${name} ends ${over.toFixed(2)}s after the footage (${footageEnd.toFixed(2)}s): the card holds its last frame under it. Shorten it, put a freeze under it, or, for a closing card on the ground, let the card leave (frame.anim.exit: 'fade')`
936
991
  );
937
992
  }
938
993
  }
@@ -1604,13 +1659,67 @@ function framingWarnings(docIn, doc, zoom, overlays, warnings) {
1604
1659
  if (!tf || !isNum(tf.x) || !isNum(tf.y)) return;
1605
1660
  const pinned = pins.get(String(o.id));
1606
1661
  if (!pinned && o.pin === void 0 && (o.kind === "html" || o.kind === "image" || o.kind === "video")) {
1607
- const cands = pinCandidates(docIn, {
1662
+ const words = layerWords(o);
1663
+ let layerBox = null;
1664
+ try {
1665
+ const b = pinBox(o, layout);
1666
+ layerBox = {
1667
+ x: tf.x * layout.W - b.w / 2,
1668
+ y: tf.y * layout.H - b.h / 2,
1669
+ w: b.w,
1670
+ h: b.h
1671
+ };
1672
+ } catch {
1673
+ layerBox = null;
1674
+ }
1675
+ const about = [];
1676
+ for (const c of pinCandidates(docIn, {
1608
1677
  start: o.start,
1609
1678
  duration: o.duration
1610
- });
1611
- if (cands.length) {
1679
+ })) {
1680
+ const named = c.selector ? selectorLabelWords(c.selector).filter((x) => words.has(x)) : [];
1681
+ if (named.length) {
1682
+ about.push({
1683
+ step: String(c.step),
1684
+ why: `its words name ${c.step}'s element ("${named.join(" ")}")`
1685
+ });
1686
+ continue;
1687
+ }
1688
+ if (!layerBox) continue;
1689
+ let ref;
1690
+ try {
1691
+ ref = pinReferent(docIn, { step: c.step });
1692
+ } catch {
1693
+ ref = null;
1694
+ }
1695
+ if (!ref || ref.rect.w * ref.rect.h > PIN_SURFACE_AREA) continue;
1696
+ const target = pinRectOnScreen(
1697
+ ref.rect,
1698
+ c.output.start,
1699
+ layout,
1700
+ camera,
1701
+ zoomTrack
1702
+ );
1703
+ const dx = Math.max(
1704
+ 0,
1705
+ target.x - (layerBox.x + layerBox.w),
1706
+ layerBox.x - (target.x + target.w)
1707
+ );
1708
+ const dy = Math.max(
1709
+ 0,
1710
+ target.y - (layerBox.y + layerBox.h),
1711
+ layerBox.y - (target.y + target.h)
1712
+ );
1713
+ if (Math.hypot(dx, dy) <= PIN_NEAR_FRACTION * layout.H) {
1714
+ about.push({
1715
+ step: String(c.step),
1716
+ why: `it sits beside ${c.step}'s element`
1717
+ });
1718
+ }
1719
+ }
1720
+ if (about.length) {
1612
1721
  warnings.push(
1613
- `overlays[${i}] is not pinned, and ${cands.length === 1 ? "a step with an element sits" : "steps with an element sit"} inside its window: ${cands.map((c) => `${String(c.step)} (${c.do}${c.selector ? ` ${c.selector}` : ""})`).join(", ")} \u2014 a callout about one of them says so: pin: { step: "${String(cands[0].step)}" }`
1722
+ `overlays[${i}] is not pinned, but ${about[0].why} \u2014 a callout about it says so, and follows it through the camera: pin: { step: "${about[0].step}" }`
1614
1723
  );
1615
1724
  }
1616
1725
  }
@@ -14700,4 +14809,4 @@ export {
14700
14809
  verbHelp,
14701
14810
  run
14702
14811
  };
14703
- //# sourceMappingURL=chunk-K6PE735V.js.map
14812
+ //# sourceMappingURL=chunk-RZZUR2IF.js.map