storyforge 0.16.0 → 0.17.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.
@@ -5652,7 +5652,18 @@ var renderGeminiRemotion = async (shot, ctx) => {
5652
5652
  ...shot.textAnimation ? { textAnimation: shot.textAnimation } : {},
5653
5653
  ...shot.textAccent ? { textAccent: shot.textAccent } : {},
5654
5654
  ...shot.textAccentColor ? { textAccentColor: shot.textAccentColor } : {},
5655
- ...shot.textColor ? { textColor: shot.textColor } : {}
5655
+ ...shot.textColor ? { textColor: shot.textColor } : {},
5656
+ // ── Tesla-grade primitive composition fields (v10+) ──
5657
+ // These were being silently dropped before this fix, so
5658
+ // ForgeShot's PrimitiveDispatch never fired.
5659
+ ...shot.shotKind ? { shotKind: shot.shotKind } : {},
5660
+ ...shot.heroText ? { heroText: shot.heroText } : {},
5661
+ ...shot.chipLabel ? { chipLabel: shot.chipLabel } : {},
5662
+ ...shot.statBadges ? { statBadges: shot.statBadges } : {},
5663
+ ...shot.bars ? { bars: shot.bars } : {},
5664
+ ...shot.comparisonImage ? { comparisonImage: shot.comparisonImage } : {},
5665
+ ...shot.photoFilter ? { photoFilter: shot.photoFilter } : {},
5666
+ ...typeof shot.vignette === "boolean" ? { vignette: shot.vignette } : {}
5656
5667
  },
5657
5668
  null,
5658
5669
  2
@@ -5759,6 +5770,71 @@ function overlayFontSize(aspect) {
5759
5770
  function escapeHtml(s) {
5760
5771
  return s.replace(/&/g, "&amp;").replace(/</g, "&lt;").replace(/>/g, "&gt;").replace(/"/g, "&quot;").replace(/'/g, "&#39;");
5761
5772
  }
5773
+ function wrapLlmHyperframesHtml(rawHtml, opts) {
5774
+ const { width, height } = dimensionsForAspect(opts.aspect);
5775
+ const compositionId = `forge-shot-${opts.shotId}`;
5776
+ const lower = rawHtml.toLowerCase();
5777
+ const hasDoctype = lower.includes("<!doctype");
5778
+ const hasHtmlTag = /<html[\s>]/i.test(rawHtml);
5779
+ const hasBody = /<body[\s>]/i.test(rawHtml);
5780
+ const hasCompositionId = rawHtml.includes("data-composition-id");
5781
+ const hasTimelineRegistration = /window\.__timelines\s*\[/.test(rawHtml);
5782
+ const hasGsapTimeline = /gsap\.timeline\s*\(/.test(rawHtml);
5783
+ let patched = rawHtml;
5784
+ if (hasGsapTimeline && !hasTimelineRegistration) {
5785
+ patched = patched.replace(
5786
+ /(<script\b[^>]*>)([\s\S]*?gsap\.timeline\s*\([\s\S]*?)(<\/script>)/i,
5787
+ (_full, open, body, close) => {
5788
+ const inject = `
5789
+ window.__timelines = window.__timelines || {};
5790
+ window.__timelines[${JSON.stringify(compositionId)}] = tl;
5791
+ try { tl.pause(); } catch(e) {}
5792
+ `;
5793
+ return `${open}${body}${inject}${close}`;
5794
+ }
5795
+ );
5796
+ }
5797
+ if (!hasCompositionId) {
5798
+ if (hasBody) {
5799
+ patched = patched.replace(
5800
+ /<body([^>]*)>([\s\S]*?)<\/body>/i,
5801
+ (_full, attrs, inner) => `<body${attrs}><div id="root" data-composition-id="${compositionId}" data-start="0" data-width="${width}" data-height="${height}">${inner}</div></body>`
5802
+ );
5803
+ } else {
5804
+ const scripts = [];
5805
+ const styles = [];
5806
+ const visible = patched.replace(/<script[\s\S]*?<\/script>/gi, (m) => {
5807
+ scripts.push(m);
5808
+ return "";
5809
+ }).replace(/<style[\s\S]*?<\/style>/gi, (m) => {
5810
+ styles.push(m);
5811
+ return "";
5812
+ });
5813
+ patched = styles.join("") + `<div id="root" data-composition-id="${compositionId}" data-start="0" data-width="${width}" data-height="${height}">` + visible + `</div>` + scripts.join("");
5814
+ }
5815
+ }
5816
+ if (!hasDoctype || !hasHtmlTag || !hasBody) {
5817
+ patched = `<!doctype html>
5818
+ <html lang="en">
5819
+ <head>
5820
+ <meta charset="utf-8" />
5821
+ <title>${compositionId}</title>
5822
+ <style>
5823
+ html,body{margin:0;padding:0;width:${width}px;height:${height}px;overflow:hidden;background:#000;font-family:'Inter',system-ui,sans-serif;}
5824
+ </style>
5825
+ </head>
5826
+ <body>
5827
+ ${patched}
5828
+ </body>
5829
+ </html>`;
5830
+ } else {
5831
+ patched = patched.replace(
5832
+ /<head([^>]*)>/i,
5833
+ (_m, attrs) => `<head${attrs}><style>html,body{margin:0;padding:0;width:${width}px;height:${height}px;overflow:hidden;background:#000;}</style>`
5834
+ );
5835
+ }
5836
+ return patched;
5837
+ }
5762
5838
 
5763
5839
  // ../pipeline/src/clip-render/engines/hyperframes.ts
5764
5840
  var HyperFramesNotInstalledError = class extends Error {
@@ -5818,7 +5894,11 @@ ${stderrTail}`));
5818
5894
  }
5819
5895
  function resolveShotHtml(shot, aspect) {
5820
5896
  if (shot.hyperframesHtml && shot.hyperframesHtml.trim().length > 0) {
5821
- return shot.hyperframesHtml;
5897
+ return wrapLlmHyperframesHtml(shot.hyperframesHtml, {
5898
+ shotId: shot.id,
5899
+ durationSec: shot.durationSec,
5900
+ aspect
5901
+ });
5822
5902
  }
5823
5903
  if (shot.imagePath || shot.overlayText) {
5824
5904
  return buildHyperframesHtml(shot, {
@@ -7201,6 +7281,11 @@ var renderHyperframesRunPod = async (shot, ctx) => {
7201
7281
  }
7202
7282
  ctx.progress.emit({ phase: "preparing", shotId: shot.id });
7203
7283
  const dims = ctx.aspect === "9:16" ? { width: 1080, height: 1920 } : { width: 1920, height: 1080 };
7284
+ const wrappedHtml = wrapLlmHyperframesHtml(shot.hyperframesHtml, {
7285
+ shotId: shot.id,
7286
+ durationSec: shot.durationSec,
7287
+ aspect: ctx.aspect
7288
+ });
7204
7289
  const status = await runJob(
7205
7290
  endpointId,
7206
7291
  {
@@ -7209,7 +7294,7 @@ var renderHyperframesRunPod = async (shot, ctx) => {
7209
7294
  // so the payload must be nested under `input_data`.
7210
7295
  input: {
7211
7296
  input_data: {
7212
- html: shot.hyperframesHtml,
7297
+ html: wrappedHtml,
7213
7298
  durationSec: shot.durationSec,
7214
7299
  fps: ctx.fps,
7215
7300
  width: dims.width,
package/dist/index.js CHANGED
@@ -1689,7 +1689,7 @@ Return ONLY the complete updated TSX. No markdown fences, no explanation.`;
1689
1689
  return "0.0.0";
1690
1690
  })();
1691
1691
  void (async () => {
1692
- const { BridgePoller } = await import("./bridge-poller-NDX5EL7P.js");
1692
+ const { BridgePoller } = await import("./bridge-poller-TOYJEO7S.js");
1693
1693
  const poller = new BridgePoller({ baseUrl: bridgeUrl, token: bridgeToken2, clientVersion: `storyforge ${pkgVersion}` });
1694
1694
  poller.start();
1695
1695
  })();
@@ -2512,7 +2512,7 @@ function resolveBridgeToken2(explicit) {
2512
2512
  // package.json
2513
2513
  var package_default = {
2514
2514
  name: "storyforge",
2515
- version: "0.16.0",
2515
+ version: "0.17.0",
2516
2516
  description: "StoryForge \u2014 local bridge for the Forge video production web app. Parallel clip-render orchestrator (Remotion 4 + Manim + HyperFrames + ffmpeg) + final video stitcher + dependency doctor.",
2517
2517
  type: "module",
2518
2518
  bin: {
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "storyforge",
3
- "version": "0.16.0",
3
+ "version": "0.17.0",
4
4
  "description": "StoryForge — local bridge for the Forge video production web app. Parallel clip-render orchestrator (Remotion 4 + Manim + HyperFrames + ffmpeg) + final video stitcher + dependency doctor.",
5
5
  "type": "module",
6
6
  "bin": {