fix(core,producer): render SDK position edits in producer pipeline

This commit is contained in:
Vance Ingalls
2026-07-09 17:38:31 -07:00
parent 16eb11367a
commit 85bab88afb
12 changed files with 298 additions and 6 deletions
@@ -7,6 +7,7 @@ import { parseHTML } from "linkedom";
import {
collectExternalAssets,
compileForRender,
injectSdkPositionEditsRenderScript,
detectRenderModeHints,
detectShaderTransitionUsage,
detectThreeDTransformUsage,
@@ -19,6 +20,28 @@ import {
} from "./htmlCompiler.js";
import { validateNoSystemFonts } from "./render/planValidation.js";
describe("injectSdkPositionEditsRenderScript", () => {
it("injects before </body> when SDK position-edit markers are present", () => {
const html =
'<html><body><h1 data-x="-231" data-y="-139" data-hf-edit-base-x="0" data-hf-edit-base-y="0">Hi</h1></body></html>';
const out = injectSdkPositionEditsRenderScript(html);
expect(out).toContain("<script>");
expect(out.indexOf("<script>")).toBeLessThan(out.indexOf("</body>"));
expect(out).toContain("data-hf-edit-base-x");
});
it("appends the script when there is no </body> tag", () => {
const out = injectSdkPositionEditsRenderScript('<div data-hf-edit-base-y="0"></div>');
expect(out.startsWith('<div data-hf-edit-base-y="0"></div>')).toBe(true);
expect(out).toContain("<script>");
});
it("is a no-op for style/text-only HTML", () => {
const html = '<html><body><h1 style="color:#f00">Hi</h1></body></html>';
expect(injectSdkPositionEditsRenderScript(html)).toBe(html);
});
});
// ── collectExternalAssets ──────────────────────────────────────────────────
describe("collectExternalAssets", () => {
+13 -1
View File
@@ -57,6 +57,7 @@ import {
} from "./deterministicFonts.js";
import { prepareAnimatedGifInputs } from "./animatedGifPrep.js";
import { createStudioPositionSeekReapplyScript } from "@hyperframes/studio-server/manual-edits-render-script";
import { getPositionEditsRenderScript } from "@hyperframes/core/runtime/position-edits-render";
import { defaultLogger, type ProducerLogger } from "../logger.js";
export interface CompiledComposition {
@@ -84,6 +85,16 @@ function parseSubCompHtmlForValidity(html: string): ParsableDocumentLike {
return parseHTML(html).document as unknown as ParsableDocumentLike;
}
export function injectSdkPositionEditsRenderScript(html: string): string {
if (!html.includes("data-hf-edit-base-x") && !html.includes("data-hf-edit-base-y")) {
return html;
}
const script = `<script>${getPositionEditsRenderScript()}</script>`;
const bodyClose = html.search(/<\/body\s*>/i);
if (bodyClose < 0) return `${html}${script}`;
return `${html.slice(0, bodyClose)}${script}${html.slice(bodyClose)}`;
}
/**
* Thrown by {@link assertSubCompositionsUsable} when one or more
* `data-composition-src` references resolve to a missing, empty, or
@@ -1730,6 +1741,7 @@ export async function compileForRender(
`<script>${createStudioPositionSeekReapplyScript()}</script></body>`,
)
: assembledHtml;
const htmlWithSdkPositionScript = injectSdkPositionEditsRenderScript(htmlWithPositionScript);
// Download remote <video> and <audio> sources to compiledDir and rewrite the
// src attributes so the renderer reads from localhost. Remote S3 URLs cause
@@ -1737,7 +1749,7 @@ export async function compileForRender(
// over the network; any that don't reach readyState >= 2 in time render as
// blank black frames. Localising them eliminates the race.
const { html: htmlWithLocalMedia, remoteMediaAssets } = await localizeRemoteMediaSources(
htmlWithPositionScript,
htmlWithSdkPositionScript,
downloadDir,
);