fix(web): stop pty sizing loop on fractional cell widths - #84
Merged
Conversation
contentSize read offsetWidth, which rounds the screen's fractional Retina width to whole pixels. The error, divided into a cell width and multiplied back across the pane, flips cellsThatFit between N and N+1 depending on the current dimensions — the pty answers each flip with the other, redrawing the prompt several times a second at unlucky pane widths (split panes, browser sidebar open). Read the exact size both renderers write into the screen's style attribute instead.
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.
Problem
An active split pane flickers at certain pane widths — most visibly with a browser sidebar open. The pane redraws its prompt several times a second and never settles.
Root cause
A closed feedback loop between the view's fit reports and the pty, driven by a rounding error:
contentSize()measured.xterm-screenwithoffsetWidth/offsetHeight, which round to whole pixels. On a Retina display the true width is fractional (a cell is a whole count of device pixels divided by the pixel ratio), so the measurement is off by up to half a pixel.cellBoxdivides that by the column count, andcellsThatFitmultiplies the error back out across the pane — enough to flip the fitted answer between N and N+1 columns depending on the dimensions the screen currently wears.syncSize), so the reporting view's desire always wins: the pty takes N+1, SIGWINCH redraws the prompt,size_changedre-lays the emulator out, the re-measure now rounds the other way and answers N. Forever.Simulated with real cell metrics: roughly 2–4% of pane widths oscillate permanently, which is why it appears at specific layouts (split panes at fractional flex widths, sidebar open) and follows the active pane — only its reports move the pty.
Fix
Both the DOM renderer and the WebGL addon write the screen's exact laid-out size into the element's style attribute.
contentSize()now reads that value (falling back to the offset measurement when absent). The exact cell width makes the fitted answer a function of the pane alone, so the loop has a fixed point and converges after one report.Testing
contentSizereports the exact fractional size, not whole pixels (fails on the old implementation).pnpm vitest run: 1464 tests pass;pnpm run lintclean.🤖 Generated with Claude Code