diff --git a/demos/mandelbrot/shell.html b/demos/mandelbrot/shell.html index 5e65c2b..0ddac56 100644 --- a/demos/mandelbrot/shell.html +++ b/demos/mandelbrot/shell.html @@ -25,6 +25,11 @@ @media (orientation: landscape) { body { justify-content: center; } } /* transition eases the pop-to-bright when a freshly computed frame lands */ #app { flex: 0 0 auto; transition: filter 0.15s ease-out; } + /* xterm.css gives the viewport overflow-y:scroll, which reserves a scrollbar + even with scrollback:0. Where scrollbars are classic (Linux, Windows, macOS + with a mouse) it paints a 15px bar over the image's right edge. Nothing + scrolls here, so hide it; !important because xterm.css loads after us. */ + .xterm .xterm-viewport { overflow-y: hidden !important; } /* Touch-only ⌨ toggle (created in JS): re-summons the soft keyboard we suppress by default, so the keyboard controls (hjkl ,. r q) stay reachable on a phone. Floats over the top-right corner; .on marks it active. */ diff --git a/demos/pathtrace/shell.html b/demos/pathtrace/shell.html index 75b7abc..4caeea8 100644 --- a/demos/pathtrace/shell.html +++ b/demos/pathtrace/shell.html @@ -19,6 +19,11 @@ body { display: flex; flex-direction: column; align-items: center; justify-content: flex-start; } @media (orientation: landscape) { body { justify-content: center; } } #app { flex: 0 0 auto; } + /* xterm.css gives the viewport overflow-y:scroll, which reserves a scrollbar + even with scrollback:0. Where scrollbars are classic (Linux, Windows, macOS + with a mouse) it paints a 15px bar over the image's right edge. Nothing + scrolls here, so hide it; !important because xterm.css loads after us. */ + .xterm .xterm-viewport { overflow-y: hidden !important; } /* Touch-only ⌨ toggle: re-summons the soft keyboard so the keyboard controls (hjkl n r q) stay reachable on a phone. */ #kbd-toggle { diff --git a/test/viewport_fit_test.py b/test/viewport_fit_test.py index 0625b10..649d97e 100644 --- a/test/viewport_fit_test.py +++ b/test/viewport_fit_test.py @@ -4,6 +4,7 @@ Drives the served browser bundle with Playwright. For each viewport it asserts: - no horizontal overflow (the terminal box fits the viewport width), + - no scrollbar on the xterm viewport (Chromium runs with scrollbars drawn), - the program adapted SCALE (smaller on the phone, 3 on desktop), - no console/page errors, - a center click keeps the center x within one cell and zooms in, @@ -71,7 +72,9 @@ def new_page(p, w, h): """Launch a chromium page at the given viewport with error capture wired and the document loaded. Returns (page, errs); errs accumulates pageerror + console-error text for the caller to assert on.""" - pg = p.chromium.launch().new_context(viewport={"width": w, "height": h}).new_page() + # Headless Chromium hides scrollbars by default; draw them so a classic + # scrollbar over the terminal shows up as a measurable gutter. + pg = p.chromium.launch(ignore_default_args=["--hide-scrollbars"]).new_context(viewport={"width": w, "height": h}).new_page() errs = [] pg.on("pageerror", lambda e: errs.append(str(e))) pg.on("console", lambda m: errs.append(m.text) if m.type == "error" else None) @@ -108,8 +111,11 @@ def check_viewport(p, tag, w, h, want_scale): "() => {const el=document.getElementById('app')||document.getElementById('terminal');" "const r=el.getBoundingClientRect();" "return {scale:window.__lgScale, grid:window.__lgGrid, termW:Math.round(r.width), " - "vpW:window.innerWidth, overflowX:Math.round(r.width)>window.innerWidth+1};}" + "vpW:window.innerWidth, overflowX:Math.round(r.width)>window.innerWidth+1, " + "gutter:(v=>v?v.offsetWidth-v.clientWidth:0)(document.querySelector('.xterm-viewport'))};}" ) + if info["gutter"]: + fails.append(f"{tag}: {info['gutter']}px scrollbar on the xterm viewport") if info["overflowX"]: fails.append(f"{tag}: terminal {info['termW']}px overflows viewport {info['vpW']}px (clipped)") if info["scale"] != want_scale: