From 01144e94969d45136696362e022a029bdf95a94d Mon Sep 17 00:00:00 2001 From: Harry Cordewener Date: Tue, 30 Jun 2026 18:58:47 -0500 Subject: [PATCH] Fix spurious horizontal scroll on the output column track MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit The font is fitted from a measured 200-char glyph run so exactly MinColumns (78) columns span the content box. But the min-width track that holds those columns used the CSS `ch` unit (`calc(var(--sc-cols) * 1ch)`), whose single- glyph advance rounds ~2px wider than the averaged run. So `78ch` exceeded the box by a couple px and .sc-output-area's overflow-x:auto showed a permanent tiny horizontal scrollbar — you had to center the scroll to see all 78, and long lines scrolled instead of wrapping at the column edge. measureGrid now publishes the track width in pixels (--sc-cols-width), measured from a real run of `cols` glyphs at the fitted size — the same basis the font was fitted to. .sc-output min-width uses that px value instead of 1ch. In the fitting case the track stays inside the box (no scroll; lines wrap at column 78); it only exceeds the box and scrolls when the screen genuinely can't fit the columns at the minimum font, which is the intended fallback. Co-Authored-By: Claude Opus 4.8 Claude-Session: https://claude.ai/code/session_01HHCRc5CJ6595iMzEgYYdQp --- src/SharpClient.UI/wwwroot/app.css | 12 +++++++----- src/SharpClient.UI/wwwroot/sc-interop.js | 9 +++++++++ 2 files changed, 16 insertions(+), 5 deletions(-) diff --git a/src/SharpClient.UI/wwwroot/app.css b/src/SharpClient.UI/wwwroot/app.css index fe8ac03..ed7781f 100644 --- a/src/SharpClient.UI/wwwroot/app.css +++ b/src/SharpClient.UI/wwwroot/app.css @@ -74,12 +74,14 @@ body { /* ── OutputView component classes (.sc-output / .sc-line) ──────── */ .sc-output { display: block; - /* Hold a floor of --sc-cols character columns (set by measureGrid alongside --out-fs). On a - screen too narrow to fit them at the minimum font, the output scrolls horizontally instead of - wrapping the server's NAWS-width lines — so what the player sees matches what the server wrapped - to. font-size must equal the line font so 1ch is the real column width. */ + /* Hold a floor of --sc-cols character columns. The track width is published in pixels by + measureGrid as --sc-cols-width, measured from a real glyph run on the SAME basis the font was + fitted to — NOT the CSS `ch` unit, whose single-glyph rounding ran ~2px wider than the fit and + produced a spurious horizontal scrollbar even when the columns fit. In the fitting case the + track stays inside the box, so lines wrap at the column edge with no scroll; only on a screen + too narrow to fit the columns at the minimum font does it exceed the box and scroll. */ font-size: var(--out-fs); - min-width: calc(var(--sc-cols, 0) * 1ch); + min-width: var(--sc-cols-width, 0px); } .sc-line { diff --git a/src/SharpClient.UI/wwwroot/sc-interop.js b/src/SharpClient.UI/wwwroot/sc-interop.js index 7d2ca49..d5519da 100644 --- a/src/SharpClient.UI/wwwroot/sc-interop.js +++ b/src/SharpClient.UI/wwwroot/sc-interop.js @@ -143,8 +143,17 @@ export function measureGrid(element, targetCols, minFont, maxFont) { cols = clampGrid(Math.floor(contentW / (advanceRatio * fontPx))); } + // Publish the column-track width in PIXELS, measured on the same basis the font was fitted to + // (a real run of `cols` glyphs). The CSS `ch` unit can't be used for the track: its single-glyph + // advance rounds a couple px wider than this averaged run, which pushed `cols * 1ch` past the + // content box and produced a spurious horizontal scrollbar even though the columns fit. Measured + // px keeps the track <= the box in the fitting case (no scroll, lines wrap at the column edge) and + // only exceeds it when the screen genuinely can't fit `cols` at the min font (then it scrolls). + const trackW = runWidth(fontPx, '0'.repeat(cols)); + element.style.setProperty('--out-fs', fontPx + 'px'); element.style.setProperty('--sc-cols', String(cols)); + element.style.setProperty('--sc-cols-width', trackW + 'px'); return { cols, rows: clampGrid(Math.floor(contentH / (lineRatio * fontPx))) }; }