fix: keep html/body sized to the composition root, warn when a scaffold's copies drift - #4005
Merged
Merged
Conversation
|
Thanks for the quick fix! Happy to pick up the follow-up (passing |
…en a scaffold's copies drift A composition whose root declares portrait data-width/data-height renders at the correct output size, but a project scaffolded landscape (init without --resolution) still ships html/body CSS and a viewport meta at the old landscape size. The stale body's overflow:hidden then clips the correctly-sized root at the old height, so everything past it renders as page background instead of real content. applyCompositionSizing now mirrors the SAME forced width/height it already computes for the root onto documentElement/body, reusing rather than recomputing them. overflow:hidden is left untouched (a deliberate anti-white-bar guard set earlier in the same init pass): once body's own size agrees with the root it contains, it clips nothing that matters. Runs in both preview and render, since both load the one bundled runtime artifact this file builds into. Adds a warning-level root_dimensions_mismatch lint rule alongside root_missing_dimensions, comparing the root's dims against the html/body CSS block and the viewport meta - the two other places a scaffold's resolution lives besides the root itself. The read-only regexes live in @hyperframes/parsers (packages/lint cannot depend on packages/cli, which owns the closest existing pattern in applyResolutionPreset) so a future caller has one place to update if the scaffold's shape changes, rather than a rule silently comparing against a guess. Closes #4001. Co-Authored-By: Miguel Ángel <miguel.sierra@heygen.com>
miga-heygen
force-pushed
the
fix-portrait-body-clip-4001
branch
from
September 16, 2026 16:14
ae8828e to
b4fbc3e
Compare
miguel-heygen
approved these changes
Sep 16, 2026
Merged
miga-heygen
added a commit
that referenced
this pull request
Sep 16, 2026
- Portrait compositions render the full frame when the project was scaffolded at a different size: html/body are sized to the composition root at runtime; new lint warning root_dimensions_mismatch (#4005, fixes #4001) - render_complete telemetry adds composition_element_tags, aroll_video_count, heygen_video_count; render_error adds error_name and failed_stage_code (#4004) - Catalog docs "Source" accordion titles are plain strings, so docs search no longer shows [object Object] (#4006) Co-Authored-By: Miguel Ángel <miguel.sierra@heygen.com>
miguel-heygen
added a commit
that referenced
this pull request
Sep 16, 2026
- Portrait compositions render the full frame when the project was scaffolded at a different size: html/body are sized to the composition root at runtime; new lint warning root_dimensions_mismatch (#4005, fixes #4001) - render_complete telemetry adds composition_element_tags, aroll_video_count, heygen_video_count; render_error adds error_name and failed_stage_code (#4004) - Catalog docs "Source" accordion titles are plain strings, so docs search no longer shows [object Object] (#4006) Co-authored-by: Miguel Ángel <miguel.sierra@heygen.com>
Merged
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
A composition whose root declares portrait
data-width/data-height(e.g.1080/1920) renders at the correct output size — that always comes from the root's own attributes — but a project scaffolded landscape (hyperframes initwithout--resolution) still shipshtml, bodyCSS and a<meta viewport>at the old landscape size. The stale body'soverflow: hiddenthen visually clips the correctly-sized root at the old height, so everything past it renders as the page background instead of real content.hyperframes lint/checkdon't catch it: their layout audits measure against the root's own (already-correct) rect, not the body's.Runtime fix (
packages/core/src/runtime/init.ts):applyCompositionSizingalready computesforcedWidth/forcedHeightfrom the root's data attributes to size the root element itself. This change mirrors the SAME already-computed values ontodocument.documentElement/document.bodytoo, reusing rather than recomputing them.overflow: hiddenon html/body is left untouched — it's set unconditionally earlier in the same init pass specifically to keep browser-default margins from bleeding into renders as white bars, and once body's own size agrees with the root it contains, that clip doesn't hide anything real.findRootCompositionElresolves the outermost[data-root="true"]composition, so a sub-composition's own CSS (compiler-scoped elsewhere) is never touched. Both the preview/Studio path and the render/capture path load the one bundled runtime artifact this file builds into, so a single fix covers both by construction.The
<meta name="viewport">tag has no effect on the capture path:frameCapture.tssets the headless Chromium capture viewport directly via CDP (page.setViewport()), independent of any HTML viewport meta, which is a mobile-browser/touch-emulation hint that a non-mobile-emulated headless context ignores.Lint rule (
packages/lint/src/rules/core.ts): new warning-levelroot_dimensions_mismatch, next toroot_missing_dimensions. Compares the root'sdata-width/data-heightagainst thehtml, bodyCSS block and the viewport meta — the two other places a scaffold's resolution lives. Fires on a document whose scaffold copies have drifted; a sub-composition fragment has neither anhtml/bodynor a viewport meta to compare against, so this is naturally top-level-only without a separate scoping guard.The two regex patterns this rule reads are extracted into
packages/parsers/src/canvasScaffoldPatterns.ts(re-exported from that package's root) rather than duplicated inline, since@hyperframes/lintcannot depend on@hyperframes/cli(the reverse is true), and@hyperframes/parsersis a dependency both already share.@hyperframes/cli'sapplyResolutionPresetkeeps its own prefix/suffix-capturing regexes for its REPLACE operation on these same two locations — a different shape than these read-only, value-capturing patterns — so the two aren't literally the same regex objects, but they track the same two locations and the new file's doc comment says so explicitly rather than overclaiming a shared implementation.Cost: both checks run once — the runtime mirroring during the composition's own init pass (no new browser session, no new DOM pass beyond what
applyCompositionSizingalready does), the lint rule once perhyperframes lintinvocation on the top-level file. Neither touches a hot/per-frame path.Out of scope, follow-up:
skills/general-video/SKILL.mdandskills/motion-graphics/SKILL.mdcallinghyperframes initwithout passing--resolutionfrom the brief — that's the other half of preventing this class of mistake at the source, not touched here.Test plan
packages/core/src/runtime/init.test.ts: new test constructs a portrait root (1080x1920) inside a document whosehtml/bodyare still sized 1920x1080 (mimicking the scaffolded landscape template), calls the runtime init, and assertsdocumentElement/bodyend up at 1080x1920 to match the root.packages/lint/src/rules/core.test.ts: 3 new tests — fires when html/body CSS and the viewport meta are still the scaffolded landscape size (including the height-before-width CSS ordering); does not fire on a sub-composition fragment with no html/body/viewport to compare against; does not fire when they already agree.html/bodyleft unedited): before the fix, sampling the output frame's center column showed the root's red (#c0392b) through y≈1080 and then the page's own dark background color (#0a0a0a) for every y beyond that — the green bar never appears. After the fix, the same column shows red through the full portrait height and the green bar (#27ae60) correctly visible at the bottom, withoverflow: hiddenleft in place throughout.oxlint/oxfmtclean on all touched files.packages/lint's fullcore.test.tssuite passes (81/81) locally.packages/core's runtime typecheck (tsc --noEmit -p tsconfig.runtime.json) is clean.Closes #4001.