@@ -383,10 +383,24 @@ export function createXtermEmulator(opts: XtermOptions = {}): Emulator {
383383 if ( disposed ) return null
384384 const screen = term . element ?. querySelector ( SCREEN_SELECTOR )
385385 if ( ! ( screen instanceof HTMLElement ) ) return null
386- // offsetWidth/offsetHeight rather than getBoundingClientRect, because a
387- // non-primary view is scaled by CSS: the rect would report the scaled
388- // box, and dividing the pane by that converges on nothing.
389- const size = { width : screen . offsetWidth , height : screen . offsetHeight }
386+ // The style attribute, which both renderers write the screen's exact
387+ // size into, rather than either measurement the browser offers. The
388+ // rect is out because a non-primary view is scaled by CSS: it reports
389+ // the scaled box, and dividing the pane by that converges on nothing.
390+ // And offsetWidth — unscaled, the obvious next choice — rounds to
391+ // whole pixels, when on a Retina display the true width is fractional:
392+ // a cell is a whole count of device pixels divided by the pixel ratio.
393+ // The half-pixel it drops, divided into a cell width and multiplied
394+ // back out across the pane, can flip cellsThatFit between N and N+1
395+ // columns depending on the dimensions the screen currently wears; the
396+ // pty then answers each flip with the other one — a sizing loop that
397+ // redraws the prompt several times a second for as long as the pane
398+ // keeps that width. (Style, not "the obvious CSS word for it": see the
399+ // Tailwind scanner note in src/styles.css.)
400+ const size = {
401+ width : parseFloat ( screen . style . width ) || screen . offsetWidth ,
402+ height : parseFloat ( screen . style . height ) || screen . offsetHeight ,
403+ }
390404 return size . width > 0 && size . height > 0 ? size : null
391405 } ,
392406
0 commit comments