Chromatic: snap pane splits to final geometry instead of tweening#235
Merged
Conversation
…not just content The terminal-story readiness gate waited only for text to reach the xterm buffer, which happens almost instantly (flattenScenario writes the whole prompt in one go). It never waited for the terminal's column count to settle. But a split kicks off a Lath tween that animates each pane's real width across many frames (LathHost sets el.style.width per rAF), and TerminalPane's refit is throttled with a 150ms trailing edge, so cols keeps reflowing well after the content lands. settleTerminals resolved ~2 paint frames after content appeared, so Chromatic sometimes snapshotted a pane still laid out at a transitional width -- `user@dormouse:~$` clipped to `user@do`. Intermittent because the two-frame cushion sometimes outlasted the throttle and sometimes didn't. Gate on content AND settled geometry: each poll, force every live terminal to fit its current resting container (bypassing the 150ms throttle so we read the true resting cols), and require the grid to hold steady for STABLE_POLLS consecutive polls before releasing the snapshot. Also await document.fonts.ready first (bounded) since cell metrics are measured from the font. Public API (settleTerminals / waitForCondition) is unchanged, so all consumer stories are unaffected. Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
Deploying mouseterm with
|
| Latest commit: |
84a0902
|
| Status: | ✅ Deploy successful! |
| Preview URL: | https://3f4f3b80.mouseterm.pages.dev |
| Branch Preview URL: | https://chromatic-stability.mouseterm.pages.dev |
dormouse-bot
approved these changes
Jul 9, 2026
Terminal stories that split panes (Wall: Multi Pane, WithDoors) intermittently snapshotted a pane with its last prompt line clipped — `user@dormouse:~$` rendered as `user@do`. Root cause: the split runs Lath's 440ms geometry tween, resizing panes through many transient widths. xterm's DOM renderer can latch a frame where a pane is still narrow and freeze its last line clipped to that width even after the layout settles to full width. It's a rendering race on the animation, not a content or column-count problem — the buffer is correct throughout. Fix: disable Lath layout motion under Chromatic (cfg.layout.animate), the same way preview.ts already freezes marching ants, the cursor blink, and the alert ring for deterministic snapshots. Splits/restores/kills snap straight to their final geometry, so no transient-width frame exists for the renderer to latch. The animator already collapses to instant at durationMs 0 (the reduced-motion path), so this reuses that code path. Snapshots capture the settled state, which is identical with or without the tween — no visual change to any story. This supersedes the earlier settle-terminals "settled grid geometry" gate, which targeted column width and did not address the rendering race; settle-terminals is reverted here to its original content-only form. Validated with a headless-visible Playwright harness (mirrors Chromatic) over the Wall: Multi Pane story: 30% truncation baseline → 0/150 with this change. Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
dormouse-bot
approved these changes
Jul 9, 2026
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.
Symptom
Terminal stories that split panes (Wall: Multi Pane, WithDoors) intermittently snapshot a pane whose last prompt line is clipped —
user@dormouse:~$rendered asuser@do.Root cause
The split runs Lath's 440ms geometry tween, resizing panes through many transient widths. xterm's DOM renderer can latch onto a frame where a pane is still narrow and freeze its last line clipped to that width — even after the layout settles to full width. It's a rendering race on the animation, not a content or column-count problem: the terminal buffer is correct the whole time, only the painted DOM is stale.
This is why the first attempt in this PR (gating the snapshot on "settled grid geometry") didn't help — it targeted column width, but the clipped frame is a renderer artifact of the tween, independent of the final cols.
Fix
Disable Lath layout motion under Chromatic (
cfg.layout.animate), exactly the waypreview.tsalready freezes marching ants, the cursor blink, and the alert ring for deterministic snapshots. Splits / restores / kills snap straight to their final geometry, so there's no transient-width frame for the renderer to latch onto.durationMs: 0(the existing reduced-motion path), so this just routes Chromatic through it — no new animation code.play, which is identical with or without the tween. No story intentionally captures a mid-animation frame, so there's no visual change to any snapshot.settle-terminals.tsis reverted to its original content-only gate (the superseded geometry approach is removed).Files
lib/src/cfg.ts— addlayout.animateflaglib/src/components/wall/lath-wall-engine.ts— collapse tween to instant when!cfg.layout.animatelib/.storybook/preview.ts— setcfg.layout.animate = falseunder the Chromatic UAValidation
Reproduced and measured with a headless-but-visible Playwright harness that loads the story in fresh pages (mirroring how Chromatic renders), over the Wall: Multi Pane story:
user@doreproduced exactlyAlso confirmed via component isolation that the animation is the sole cause: instant-motion alone → 0%, while keeping the tween on with other mitigations → still ~25%.
Lib typecheck + full unit suite (1045 tests) green.
🤖 Generated with Claude Code