diff --git a/.gitattributes b/.gitattributes index daa28b284..4c9ff87e5 100644 --- a/.gitattributes +++ b/.gitattributes @@ -17,6 +17,12 @@ packages/producer/tests/**/*.webm filter=lfs diff=lfs merge=lfs -text packages/producer/tests/**/*.png filter=lfs diff=lfs merge=lfs -text packages/producer/tests/**/output/compiled.html filter=lfs diff=lfs merge=lfs -text +# HF#2550 fixture's source index.html carries the real-world project's +# embedded base64 assets (744 KB) — over the largefiles hook's 500 KB +# non-LFS limit. Scoped to this one file, not a broad tests/**/*.html +# pattern, since every other fixture's source HTML is small plain git. +packages/producer/tests/hf2550-video-subcomposition-ghost/src/index.html filter=lfs diff=lfs merge=lfs -text + # ONNX models must ALWAYS use LFS regardless of location: a 31 MB ppmattingv2 # model was once committed raw into skills/ then deleted — but a raw commit # lives in history forever. No path in the repo legitimately ships a raw .onnx. diff --git a/packages/core/src/runtime/init.test.ts b/packages/core/src/runtime/init.test.ts index c83fa4d30..042b26208 100644 --- a/packages/core/src/runtime/init.test.ts +++ b/packages/core/src/runtime/init.test.ts @@ -37,13 +37,18 @@ function createMockTimeline(duration: number): RuntimeTimelineLike { function createPaddableMockTimeline(duration: number): RuntimeTimelineLike { const timeline = createMockTimeline(duration) as RuntimeTimelineLike & { - to: (_target: object, vars: { duration: number }, position: number) => void; + to: (_target: object, vars: { duration: number }, position?: number) => void; }; const baseDuration = timeline.duration; let paddedDuration = baseDuration(); timeline.duration = () => paddedDuration; + // Mirrors GSAP: an omitted position appends sequentially at the current end. timeline.to = (_target, vars, position) => { - paddedDuration = Math.max(paddedDuration, position + Math.max(0, Number(vars.duration) || 0)); + const resolvedPosition = position ?? paddedDuration; + paddedDuration = Math.max( + paddedDuration, + resolvedPosition + Math.max(0, Number(vars.duration) || 0), + ); }; return timeline; } @@ -1391,6 +1396,118 @@ describe("initSandboxRuntimeModular", () => { }, ); + it("ignores the async media-metadata duration rebind once render capture has started seeking frames (regression HF#2550)", async () => { + const root = document.createElement("div"); + root.setAttribute("data-composition-id", "main"); + root.setAttribute("data-root", "true"); + root.setAttribute("data-start", "0"); + root.setAttribute("data-width", "1920"); + root.setAttribute("data-height", "1080"); + document.body.appendChild(root); + + const video = document.createElement("video"); + video.setAttribute("data-start", "0"); + document.body.appendChild(video); + + // A root timeline with no usable duration yet — mirrors a composition + // whose length is derived from a full-length