fix(cli): sweep_static geometry fingerprint now covers SVG stroke/dash motion - #3914
Open
miga-heygen wants to merge 1 commit into
Open
miga-heygen wants to merge 1 commit into
miga-heygen wants to merge 1 commit into
Conversation
Contributor
Author
|
Addressed in b8abfc7. |
miga-heygen
force-pushed
the
fix/sweep-static-svg-dash
branch
from
September 14, 2026 18:26
f35dfe3 to
b8abfc7
Compare
|
Preview deployment for your docs. Learn more about Mintlify Previews.
💡 Tip: Enable Automations to automatically generate PRs for you. |
miga-heygen
changed the base branch from
main
to
magi/fix-sweep-static-text-fingerprint
September 14, 2026 18:26
Contributor
Author
miga-heygen
force-pushed
the
fix/sweep-static-svg-dash
branch
from
September 15, 2026 01:19
b8abfc7 to
582018d
Compare
Contributor
Author
miga-heygen
force-pushed
the
fix/sweep-static-svg-dash
branch
from
September 15, 2026 02:24
582018d to
5f4196b
Compare
A "draw the line in" SVG entrance animates stroke-dasharray / stroke-dashoffset on a shape whose geometry never changes, so the box+opacity channels read it as a frozen timeline and `hyperframes check` false-positives sweep_static on a visibly animating composition. - Append strokeDashChannel to ELEMENT_CHANNELS: hashes the computed dash pattern and offset for SVG geometry elements with a painted stroke; `none` / all-zero patterns, unpainted (none, transparent, zero-width, stroke-opacity 0) strokes and non-SVG elements contribute "". - Widen isVisibleElement for stroked geometry with one degenerate bbox axis: Chromium's getBoundingClientRect for SVG shapes excludes the stroke, so a straight horizontal or vertical connector reports 0 height or 0 width and was dropped by the box gate — the most common shape for this animation. motion-sample's `visible` bit for such connectors is corrected as a consequence. - Treat <defs>, <clipPath> and <symbol> as never-painted subtrees in the hidden-subtree walk (display is not inherited, so descendants are hidden only through the ancestor walk) rather than relying on one engine's empty-box behaviour for their descendants. Tests: happy-dom pins for the degenerate-bbox connector (moves / frozen), a display:none ancestor, <defs>/<clipPath> content and a stroke-opacity:0 decoy; real-Chromium pins for the connector draw-in and the ignored cases. Co-Authored-By: Miguel Ángel <miguel.sierra@heygen.com>
miga-heygen
force-pushed
the
fix/sweep-static-svg-dash
branch
from
September 15, 2026 02:34
5f4196b to
95624bc
Compare
Contributor
Author
|
Updated as 95624bc: SVG container set and guard. |
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
hyperframes check's seek-audit firessweep_static("Timeline did not advance under seek") when the per-sample motion signature is byte-identical across the whole run. An SVGstroke-dasharray/stroke-dashoffset"draw the line in" entrance — the standard way to animate a connector or diagram line — moves no bounding box and no opacity, so a genuinely animating composition using it was flagged as a frozen timeline.With #3707 the signature has a single owner (
packages/cli/src/commands/motion-signature.browser.js,ELEMENT_CHANNELS), shared by the frozen-sweep guard andkeepsMovingliveness. This PR:strokeDashChanneltoELEMENT_CHANNELS. Hashes the computed dash pattern and offset for SVG geometry elements (SVGGeometryElement: path/circle/ellipse/rect/line/polyline/polygon) with a painted stroke.noneand all-zero patterns (which render solid), unpainted strokes (none, transparent, zero width,stroke-opacity: 0) and non-SVG elements contribute"", so ordinary compositions gain no payload.isVisibleElementfor stroked geometry with one degenerate bbox axis. Chromium'sgetBoundingClientRectfor SVG shapes is the object bounding box without the stroke: a straight horizontal or vertical connector reports 0 height or 0 width regardless ofstroke-width, so the box gate dropped exactly the most common shape for this animation. A stroked shape with one non-degenerate axis now counts as visible. As a consequencemotion-sample'svisiblebit for such connectors is corrected too (it previously reported them invisible).<defs>,<clipPath>,<mask>,<pattern>,<marker>and<symbol>as never-painted subtrees in the existing hidden-subtree walk (the same container listlayout-audit.browser.jsskips for connectors).displayis not inherited — a child of adisplay:noneparent computesdisplay: block— so descendants are excluded only through the ancestor walk, which now also starts at these containers rather than relying on one engine's empty-box behaviour for their descendants. The match is namespaced (instanceof SVGElement), so an HTML element that merely shares a name is not pruned.Test plan
motion-signature.browser.test.ts(happy-dom, 36 passing): dash-offset moves on a 290×0 connector change the signature; identical scene hashes identically; dash motion under adisplay:noneancestor, inside<defs>/<clipPath>/<mask>/<pattern>/<marker>/<symbol>, and on astroke-opacity: 0connector is ignored; a fade inside an HTML element named<defs>still changes the signature. Fixtures use the real Chromium bbox of a horizontal stroked path (height 0) so they pin the widened gate, not the pre-existing box path.motion-signature.browser.chromium.test.ts(real Chromium, 37 passing): astroke-dashoffsetdraw-in on a straight connector changes both the sweep and the liveness signature; dash motion underdisplay:noneand inside all six unpainted containers leaves both identical; a fade inside an HTML<defs>element changes both signatures.motion-sample.browser.test.ts(happy-dom, 8 passing) unchanged.<path>reports a 290×0 box;display:nonedescendants computedisplay: inline; descendants of all six containers (<defs>,<clipPath>,<mask>,<pattern>,<marker>,<symbol>) report 0×0 whilecheckVisibilityreturns true for them, so the container rule is what excludes them by intent rather than the box gate; computedstroke-dashoffsetis rounded (144.99999999px→145px), so the signature cannot vary on its own at a fixed seek.stroke: transparent/stroke-opacity: 0counting as painted;stroke-dasharray: 0(solid) counting as a pattern; tag-name matching hitting HTML unknown elements (replaced byinstanceof SVGGeometryElement).<mask>/<pattern>/<marker>missing from the never-painted set although they meet its definition (and the widened bbox gate would otherwise admit a straight connector inside them); the tag-name match lacking a namespace guard.oxlint,oxfmt --check, andtsc --noEmitclean forpackages/cli. The happy-dom suites cannot be collected directly in the local sandbox under bun (No such built-in module: node:from the@vitest-environment happy-domloader); they were run with--environment nodeplus a setup file that populates globals from a happy-domWindow, and pass. CI runs them as-is.Deliberately out of scope: dashing on
<text>/<use>(no channel today), and other "moves nothing else" SVG reveals (fill-opacity,stroke-opacity, gradientstop-opacity) — this closes the stroke/dash gap only.