@vosjs/studio-core 0.6.0 → 0.7.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.
package/dist/index.d.ts CHANGED
@@ -922,6 +922,15 @@ interface EndCard {
922
922
  headline?: string;
923
923
  sub?: string;
924
924
  wordmark?: string;
925
+ /**
926
+ * The brand's MARK as an image (a take-dir path or a URL the render page
927
+ * can load), with its aspect (width over height), drawn above the
928
+ * wordmark; a wide mark (a stylised wordmark asset) stands alone.
929
+ */
930
+ mark?: {
931
+ key: string;
932
+ aspect: number;
933
+ };
925
934
  /**
926
935
  * The words' colour (any CSS colour). The house presets are white for
927
936
  * footage; over a light plate the brand's ink is what reads. Absent =
package/dist/index.js CHANGED
@@ -1813,7 +1813,11 @@ var STUDIO_SETUP = `async (ctx) => {
1813
1813
  if (olv && olv.length && typeof FontFace !== 'undefined') {
1814
1814
  try {
1815
1815
  const faces = ctx.data.overlayFonts || ${JSON.stringify(OVERLAY_FONT_FACES)}
1816
+ // Mark what SETUP registers, so ON_FRAME's live path (a text overlay
1817
+ // arriving through SET_DATA) never adds the same face twice.
1818
+ const ofSet = window.__voilaFontSet || (window.__voilaFontSet = {})
1816
1819
  const loads = faces.map((f) => {
1820
+ ofSet[f.family + '|' + f.weight] = 1
1817
1821
  const ff = new FontFace(f.family, 'url(' + f.url + ')', { weight: String(f.weight) })
1818
1822
  document.fonts.add(ff)
1819
1823
  return ff.load()
@@ -2273,20 +2277,37 @@ var STUDIO_FRAME = `(ctx, content, dt) => {
2273
2277
  continue
2274
2278
  }
2275
2279
  var olPx = ol.fs * olMS * s
2276
- // Live style edits: SET_DATA never re-runs SETUP, so an override face
2277
- // arriving mid-session lazy-loads here (fail-open; frames repaint as it
2278
- // lands). Cold loads (export) awaited the full list in SETUP already.
2279
- if (ol.face && typeof FontFace !== 'undefined') {
2280
+ // Faces arriving LIVE: SET_DATA never re-runs SETUP, so the first text
2281
+ // overlay a session gains registers the house faces here (a title added
2282
+ // to a fresh document used to paint in the stack's system fallback until
2283
+ // the next cold load), and an override face lazy-loads the same way.
2284
+ // Keyed per window, so SETUP's cold-load faces and repeat frames never
2285
+ // re-register; fail-open, frames repaint as each face lands.
2286
+ if (typeof FontFace !== 'undefined') {
2280
2287
  var ofSet = window.__voilaFontSet || (window.__voilaFontSet = {})
2281
- var ofKey = ol.face.f + '|' + ol.face.w
2282
- if (!ofSet[ofKey]) {
2283
- ofSet[ofKey] = 1
2288
+ var ofBase = d.overlayFonts || ${JSON.stringify(OVERLAY_FONT_FACES)}
2289
+ for (var ofi = 0; ofi < ofBase.length; ofi++) {
2290
+ var ofB = ofBase[ofi]
2291
+ var ofBK = ofB.family + '|' + ofB.weight
2292
+ if (ofSet[ofBK]) continue
2293
+ ofSet[ofBK] = 1
2284
2294
  try {
2285
- var ofFace = new FontFace(ol.face.f, 'url(' + ol.face.u + ')', { weight: String(ol.face.w) })
2286
- document.fonts.add(ofFace)
2287
- ofFace.load().catch(function () {})
2295
+ var ofBF = new FontFace(ofB.family, 'url(' + ofB.url + ')', { weight: String(ofB.weight) })
2296
+ document.fonts.add(ofBF)
2297
+ ofBF.load().catch(function () {})
2288
2298
  } catch (e) {}
2289
2299
  }
2300
+ if (ol.face) {
2301
+ var ofKey = ol.face.f + '|' + ol.face.w
2302
+ if (!ofSet[ofKey]) {
2303
+ ofSet[ofKey] = 1
2304
+ try {
2305
+ var ofFace = new FontFace(ol.face.f, 'url(' + ol.face.u + ')', { weight: String(ol.face.w) })
2306
+ document.fonts.add(ofFace)
2307
+ ofFace.load().catch(function () {})
2308
+ } catch (e) {}
2309
+ }
2310
+ }
2290
2311
  }
2291
2312
  ovC.save()
2292
2313
  ovC.globalAlpha = olA
@@ -2859,8 +2880,37 @@ function expandEndCard(doc, outputDuration) {
2859
2880
  clips.push(text("endcard-title", card.headline.trim(), "title", 0.44, 0.35));
2860
2881
  if (card.sub?.trim())
2861
2882
  clips.push(text("endcard-sub", card.sub.trim(), "caption", 0.57, 0.5));
2862
- if (card.wordmark?.trim())
2863
- clips.push(text("endcard-mark", card.wordmark.trim(), "label", 0.88, 0.65));
2883
+ const mark = card.mark;
2884
+ const wide = !!mark && mark.aspect > 2.2;
2885
+ if (mark && mark.key) {
2886
+ const size = resolveExportSize(doc);
2887
+ const designW = 1080 * size.width / Math.max(1, size.height);
2888
+ const hPx = wide ? 44 : 30;
2889
+ const clip = {
2890
+ id: "endcard-markimg",
2891
+ kind: "image",
2892
+ key: mark.key,
2893
+ width: Math.min(0.9, hPx * Math.max(0.2, mark.aspect) / designW),
2894
+ radius: 0,
2895
+ shadow: "none",
2896
+ start: endStart + 0.65,
2897
+ duration: Math.max(0.3, seconds - 0.65),
2898
+ transform: { x: 0.5, y: wide ? 0.88 : 0.835, scale: 1, rotation: 0 },
2899
+ enter: "rise",
2900
+ exit: "none"
2901
+ };
2902
+ clips.push(clip);
2903
+ }
2904
+ if (card.wordmark?.trim() && !wide)
2905
+ clips.push(
2906
+ text(
2907
+ "endcard-mark",
2908
+ card.wordmark.trim(),
2909
+ "label",
2910
+ mark && mark.key ? 0.895 : 0.88,
2911
+ 0.65
2912
+ )
2913
+ );
2864
2914
  const overlays = [...doc.overlays ?? [], ...clips];
2865
2915
  return { doc: { ...doc, segments, overlays }, endStart, seconds };
2866
2916
  }