@koda-sl/baker-cli 0.265.0-dev.1f1c09c80 → 0.265.2-dev.1f1c09c80

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/cli.js CHANGED
@@ -32651,12 +32651,13 @@ function buildAdBrandOverlay(opts) {
32651
32651
  const parts = [
32652
32652
  "<!-- Brand layer, emitted by `baker canvas scaffold-ad`. The mark and the closing",
32653
32653
  " card are drawn HERE, by the composition \u2014 never generated as a picture, because",
32654
- " a video model garbles a wordmark every time. Restyle freely; keep the `clip`",
32655
- " class and the data-start/data-dur attributes, which is how the runtime times them. -->"
32654
+ " a video model garbles a wordmark every time. Restyle freely, but keep the",
32655
+ " data-start/data-dur attributes and the script below: that script is what shows",
32656
+ " and hides these, and the host composition has nothing that would. -->"
32656
32657
  ];
32657
32658
  if (logo) {
32658
32659
  parts.push(
32659
- `<img class="ov clip pos-top-left brand-mark" src="${escapeHtml2(logo.file)}" alt="${escapeHtml2(logo.alt)}" data-start="0" data-dur="${totalS}" />`
32660
+ `<img class="clip brand-mark" src="${escapeHtml2(logo.file)}" alt="${escapeHtml2(logo.alt)}" data-start="0" data-dur="${totalS}" />`
32660
32661
  );
32661
32662
  }
32662
32663
  const cardBits = [];
@@ -32668,24 +32669,61 @@ function buildAdBrandOverlay(opts) {
32668
32669
  cardBits.push(`<div class="endcard-cta"${style}>${escapeHtml2(cta.trim())}</div>`);
32669
32670
  }
32670
32671
  parts.push(
32671
- `<div class="ov clip pos-center endcard" data-start="${cardAtS}" data-dur="${cardDurationS}" data-role="cta">`,
32672
+ `<div class="clip endcard" data-start="${cardAtS}" data-dur="${cardDurationS}" data-role="cta">`,
32672
32673
  ...cardBits.map((b) => ` ${b}`),
32673
- "</div>"
32674
+ "</div>",
32675
+ brandLayerDriver()
32674
32676
  );
32675
32677
  return parts.join("\n");
32676
32678
  }
32679
+ function brandLayerDriver() {
32680
+ return [
32681
+ "<script>",
32682
+ " (function () {",
32683
+ " var els = Array.prototype.slice.call(document.querySelectorAll('.brand-mark, .endcard'));",
32684
+ " if (!els.length) return;",
32685
+ " els.forEach(function (el) { gsap.set(el, { visibility: 'hidden' }); });",
32686
+ " function attach(tl) {",
32687
+ " if (!tl) return;",
32688
+ " els.forEach(function (el) {",
32689
+ " var at = parseFloat(el.getAttribute('data-start') || '0') || 0;",
32690
+ " var dur = parseFloat(el.getAttribute('data-dur') || '0') || 0;",
32691
+ " tl.set(el, { visibility: 'visible' }, at);",
32692
+ " if (dur > 0) tl.set(el, { visibility: 'hidden' }, at + dur);",
32693
+ " });",
32694
+ " }",
32695
+ " var store = window.__timelines || {};",
32696
+ " var main = store.main;",
32697
+ " if (main) { attach(main); return; }",
32698
+ " Object.defineProperty(store, 'main', {",
32699
+ " configurable: true,",
32700
+ " get: function () { return main; },",
32701
+ " set: function (tl) { main = tl; attach(tl); },",
32702
+ " });",
32703
+ " window.__timelines = store;",
32704
+ " })();",
32705
+ "</script>"
32706
+ ].join("\n");
32707
+ }
32677
32708
  function adBrandOverlayCss() {
32678
32709
  return [
32679
32710
  " /* --- brand layer (baker canvas scaffold-ad) --- */",
32680
- " .brand-mark { width: 220px; height: auto; opacity: 0.92;",
32681
- " filter: drop-shadow(0 2px 8px rgba(0,0,0,0.45)); }",
32682
- " .endcard { display: flex; flex-direction: column; align-items: center; gap: 56px;",
32711
+ " /* Positioned by this block alone. The composition that actually renders an ad",
32712
+ " is the caption track, which has no .pos-* helpers \u2014 relying on them put the",
32713
+ " brand layer in a file nothing opens and shipped an empty green end plate. */",
32714
+ " .brand-mark { position: absolute; top: 90px; left: 56px; width: 220px; height: auto;",
32715
+ " opacity: 0.92; filter: drop-shadow(0 2px 8px rgba(0,0,0,0.45)); }",
32716
+ " .endcard { position: absolute; top: 50%; left: 50%; transform: translate(-50%, -50%);",
32717
+ " display: flex; flex-direction: column; align-items: center; gap: 56px;",
32683
32718
  " width: 100%; max-width: 1080px; padding: 0 60px; text-align: center; }",
32684
32719
  " .endcard-logo { width: 620px; max-width: 78%; height: auto; }",
32685
32720
  " /* Capped and wrapping, not sized to its content: measured in a real browser,",
32686
32721
  " `GET YOUR FREE QUOTE` renders 948px wide against a 1080px canvas, so the next",
32687
32722
  " slightly longer call to action runs off the frame. Two lines beat an edge. */",
32688
- " .endcard-cta { font-size: 64px; font-weight: 900; text-transform: uppercase;",
32723
+ " /* Declares its own family: rendered without one it inherited the document",
32724
+ " default and the call to action came out in Times, beside sans-serif captions. */",
32725
+ " .endcard-cta { font-family: 'BrandFont', 'Arial Black', 'Helvetica Neue', Arial, sans-serif;",
32726
+ " font-size: 64px; font-weight: 900; text-transform: uppercase;",
32689
32727
  " letter-spacing: 0.01em; color: #fff; padding: 28px 58px; border-radius: 56px;",
32690
32728
  " max-width: 900px; line-height: 1.15; background: rgba(0,0,0,0.82); }"
32691
32729
  ].join("\n");
@@ -32694,7 +32732,11 @@ function injectAdBrandOverlay(indexHtml, overlayHtml) {
32694
32732
  if (!overlayHtml.trim()) return indexHtml;
32695
32733
  const withCss = indexHtml.replace(" </style>", `${adBrandOverlayCss()}
32696
32734
  </style>`);
32697
- return withCss.replace("<!--OVERLAYS-->", () => overlayHtml);
32735
+ if (withCss.includes("<!--OVERLAYS-->")) return withCss.replace("<!--OVERLAYS-->", () => overlayHtml);
32736
+ const rootClose = withCss.lastIndexOf("</div>");
32737
+ if (rootClose === -1) return withCss;
32738
+ return `${withCss.slice(0, rootClose)}${overlayHtml}
32739
+ ${withCss.slice(rootClose)}`;
32698
32740
  }
32699
32741
 
32700
32742
  // src/commands/canvas/composition-path.ts
@@ -32796,18 +32838,35 @@ var scaffoldAdCommand = defineCommand104({
32796
32838
  await writeSceneFiles(outDir, blueprint);
32797
32839
  await writeFile9(blueprintStylePath, renderStyleProjectionFromValue(blueprint), "utf8");
32798
32840
  const logoPath = spec.data.brand?.logo?.trim();
32841
+ const opts = {
32842
+ imageModel: AD_IMAGE_MODEL,
32843
+ videoModel: DEFAULT_VIDEO_GENERATE_MODEL,
32844
+ overlayCompositionPath: path23.relative(outDir, compositionDest),
32845
+ captionsCompositionPath: path23.relative(outDir, captionsDest),
32846
+ blueprintPath: path23.relative(outDir, blueprintPath),
32847
+ blueprintStylePath: path23.relative(outDir, blueprintStylePath),
32848
+ aspect: spec.data.format.aspect_ratio,
32849
+ resolution: spec.data.format.resolution,
32850
+ // A beat is one clause and one card, so the cap has to clear a whole clause.
32851
+ // The composition's default of 3 is TikTok karaoke styling; applied to an ad
32852
+ // it overrode the clause rule on every line and rendered "IS STANDARD. WE".
32853
+ // 8 is the composition's own maximum and clears every beat measured here.
32854
+ captionWordsPerGroup: 8
32855
+ };
32856
+ const canvas = scaffoldVideoCanvas(blueprint, adSpecCastElements(spec.data), opts);
32857
+ const renderNode3 = canvas.nodes.find((n) => n.type === "hyperframe_render");
32858
+ const renderedDir = renderNode3 ? path23.join(outDir, String(renderNode3.params?.composition ?? "")) : null;
32799
32859
  let staged = null;
32800
- if (logoPath) {
32860
+ if (logoPath && renderedDir) {
32801
32861
  const ext = path23.extname(logoPath) || ".png";
32802
- const dest = path23.join(compositionDest, `brand-mark${ext}`);
32803
32862
  try {
32804
- await copyFile(logoPath, dest);
32863
+ await copyFile(logoPath, path23.join(renderedDir, `brand-mark${ext}`));
32805
32864
  staged = { file: `brand-mark${ext}`, alt: spec.data.brand?.name ?? "brand" };
32806
32865
  } catch {
32807
32866
  }
32808
32867
  }
32809
32868
  const closing = blueprint.scenes[blueprint.scenes.length - 1];
32810
- const overlayHtml = endCardWanted(spec.data) && closing ? buildAdBrandOverlay({
32869
+ const overlayHtml = endCardWanted(spec.data) && closing && renderedDir ? buildAdBrandOverlay({
32811
32870
  logo: staged,
32812
32871
  cta: endCardCta(spec.data),
32813
32872
  cardAtS: closing.start_s,
@@ -32815,27 +32874,11 @@ var scaffoldAdCommand = defineCommand104({
32815
32874
  totalS: blueprint.estimated_duration_s,
32816
32875
  accent: firstHex(spec.data.brand?.palette)
32817
32876
  }) : "";
32818
- if (overlayHtml) {
32819
- const indexPath = path23.join(compositionDest, "index.html");
32877
+ if (overlayHtml && renderedDir) {
32878
+ const indexPath = path23.join(renderedDir, "index.html");
32820
32879
  const html = await readFile15(indexPath, "utf-8");
32821
32880
  await writeFile9(indexPath, injectAdBrandOverlay(html, overlayHtml), "utf-8");
32822
32881
  }
32823
- const opts = {
32824
- imageModel: AD_IMAGE_MODEL,
32825
- videoModel: DEFAULT_VIDEO_GENERATE_MODEL,
32826
- overlayCompositionPath: path23.relative(outDir, compositionDest),
32827
- captionsCompositionPath: path23.relative(outDir, captionsDest),
32828
- blueprintPath: path23.relative(outDir, blueprintPath),
32829
- blueprintStylePath: path23.relative(outDir, blueprintStylePath),
32830
- aspect: spec.data.format.aspect_ratio,
32831
- resolution: spec.data.format.resolution,
32832
- // A beat is one clause and one card, so the cap has to clear a whole clause.
32833
- // The composition's default of 3 is TikTok karaoke styling; applied to an ad
32834
- // it overrode the clause rule on every line and rendered "IS STANDARD. WE".
32835
- // 8 is the composition's own maximum and clears every beat measured here.
32836
- captionWordsPerGroup: 8
32837
- };
32838
- const canvas = scaffoldVideoCanvas(blueprint, adSpecCastElements(spec.data), opts);
32839
32882
  await writeFile9(outPath, `${JSON.stringify(canvas, null, 2)}
32840
32883
  `, "utf-8");
32841
32884
  writeJson({