fix: hold external sub-compositions in render mode + regenerate baseline#918
Merged
Conversation
The sub-composition visibility fix (2b46565) correctly holds external compositions through their authored data-duration. This changes style-12-prod output from t=8.26s onward: the mondrian-colors sub-composition now stays visible instead of going black when its GSAP timeline ends. Baseline regenerated inside Docker per CLAUDE.md. Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
Both the core bundler (htmlBundler.ts) and the producer (htmlCompiler.ts) had parallel ~200-line implementations of sub-composition inlining. This divergence caused bug #911 (producer didn't set data-composition-file). Extract the shared logic into core/compiler/inlineSubCompositions.ts: - Single function handles: template/body extraction, CSS/script scoping, asset path rewriting, data-composition-file attribution, content injection - Callers provide environment-specific callbacks (HTML resolution, parsing, variable handling, inner root flattening) - Core bundler passes its advanced features (runtime IDs, variables, inline style rewriting, inner root flattening) - Producer passes a simpler resolver (map + filesystem fallback) and adds pixel sizing post-hoc Net: -215 lines, one source of truth for sub-comp inlining. Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
When linkedom parses a fragment like `<div data-composition-id="X">... </div>`, the div becomes the documentElement and body is empty. contentDoc.body?.innerHTML returns "" losing the composition wrapper. Fall back to contentDoc.documentElement?.outerHTML when body content is empty, preserving composition IDs for sub-compositions where the host data-composition-id differs from the inner root's. Fixes style-1-prod regression (captions sub-comp has host id "captions-comp" but inner root id "captions"). Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
The sub-composition inlining now correctly preserves composition IDs when the host data-composition-id differs from the inner root's (e.g., host "captions-comp" with inner root "captions"). The captions layer renders with proper scoping, changing visual output. Baseline regenerated inside Docker per CLAUDE.md. Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
Sub-composition visibility fix changes output for compositions with external sub-compositions. Baseline regenerated in Docker. Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
Regenerated baselines for all regression tests with sub-compositions in the cancelled shards: style-3-prod, style-5-prod, style-9-prod, style-15-prod, style-16-prod, style-17-prod, style-18-prod, sub-composition-video, many-cuts. Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
jrusso1020
added a commit
that referenced
this pull request
May 17, 2026
`Dockerfile.test:56` installed `chrome-headless-shell@stable` via `@puppeteer/browsers`. `@stable` is a moving tag, so every Chrome stable bump shifted pixel output enough to fail PSNR on the golden baselines. The regression suite silently broke whenever Docker.test rebuilt against a freshly-promoted stable. Pin to `chrome-headless-shell@148.0.7778.167` — the Chrome 148 stable build that `@stable` currently resolves to, matching what most goldens on `main` were captured against. Comment notes that future bumps must be paired with `docker:test:update` so the pin and the baselines stay in lockstep. Also regenerates the `style-12-prod` golden baseline. PR #918 regenerated it once at b9bdc80, but that commit landed *before* the `refactor: extract shared inlineSubCompositions from bundler and producer` (581e7a7) and the linkedom-fragment fix (754b0ed) in the same stack. The compiler refactor changes `__hfRootSelector` from `null` to a scoped `[data-composition-id="..."]` selector in the inlined sub-compositions, which affects the rendered output. style-12-prod was the one fixture in that stack that didn't get a second regen pass after the refactor, so it has been failing on plain `origin/main` (PSNR ~13 from frame 8.26s onward — the mondrian-colors blocks no longer match expected). The new baseline regenerated under this pin passes at PSNR 62-102 dB.
4 tasks
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Summary
htmlCompiler.tsstripsdata-composition-srcduring sub-composition inlining but wasn't settingdata-composition-file(unlike the core bundler). The runtime visibility check now handles both attributes. Closes Sub-composition slot goes black after GSAP timeline ends, regardless of host data-duration #911.inlineSubCompositionsfunction in core. Both callers now use this function, eliminating the divergence that caused Sub-composition slot goes black after GSAP timeline ends, regardless of host data-duration #911.style-12-prodbaseline inside Docker — the Mondrian color grid now correctly holds through its authoreddata-duration.Changes
packages/core/src/compiler/inlineSubCompositions.tspackages/core/src/compiler/htmlBundler.tspackages/core/src/compiler/index.tspackages/core/src/runtime/init.tsdata-composition-filein visibility computationpackages/core/src/runtime/init.test.tspackages/producer/src/services/htmlCompiler.tsinlineSubCompositionswith 90-line wrapper using shared functionpackages/producer/tests/style-12-prod/output/Root cause of #911
The producer and core bundler had parallel implementations of sub-composition inlining (~200 lines each). The core bundler correctly set
data-composition-fileafter inlining; the producer didn't. PR #917 added a runtime check fordata-composition-src, but that attribute is stripped by both inliners — onlydata-composition-filesurvives to runtime.The fix: one shared function, one source of truth for
data-composition-file.Architecture
The shared function accepts environment-specific callbacks:
resolveHtml: Core bundler reads from filesystem; producer checks a pre-compiled map firstparseHtml: Both pass their DOM parser (core usesparseHTMLContent, producer uses linkedom'sparseHTML)flattenInnerRoot: Core bundler'sprepareFlattenedInnerRoot(strips attrs, marks inner root); producer skips thisreadVariableDefaults/parseHostVariables: Core bundler handles variable merging; producer skipsProducer-specific logic (explicit pixel dimensions on hosts) stays in the producer wrapper.
Test plan
bun run --filter @hyperframes/core test— 906 tests pass (including 19 htmlBundler tests)style-12-prodbaseline regenerated inside Dockerbunx oxlintandbunx oxfmt --checkpass on all changed files🤖 Generated with Claude Code