Repository navigation
fix(file-viewer): keep the line-number gutter pinned and ease the code font - #1200
Merged
Merged
Conversation
…e font Scrolling a wide source line sideways took its line number with it: the gutter was an ordinary column of the scrolling row, so past a few hundred pixels the reader loses which line they are on. - `FILE_LINE_NUMBER_STYLE`: `position: sticky; left: 0; z-index: 1` pins the gutter to the viewer's own `overflow: auto` content box. All three gutters take this one constant — the highlighted Source view (`lineNumberStyle`, whose <pre> sets `overflow: visible` so the content box is the scroller), the past-the-limit plain fallback, and the Diff view — and each wraps its rows in a `width: max-content` box with `min-width: 100%` rows, which is the room sticky travels in. - `FILE_CODE_STYLE`: 13px -> 12px, a taste call. It rides along because the gutter repeats the code line's height in px: the constant is now `fontSize * 1.6`, so the font and `lineHeight` (20.8px -> 19.2px) must move in one commit or the numbers sit off their line boxes. Splitting it would only ship the broken intermediate state. - A test renders the source rows with the module's real style constants: the sticky pin, and the gutter px height = code fontSize * lineHeight, a coupling nothing else visible would catch. Both runners: `bun test components/FileViewer.test.mjs` and `node --experimental-strip-types --test` on the file; the components suite passes 553. tsc and eslint unchanged.
brynne8
force-pushed
the
fix/file-viewer-pinned-gutter
branch
from
October 12, 2026 03:11
cff75b4 to
7ec03b6
Compare
The pinned gutter does not need a smaller code font, and a font size is a separate call from that fix: restore FILE_CODE_STYLE's 13px and the gutter's matching 20.8px line height (13 * 1.6), so this change only pins the line numbers. The test checks that the two stay equal, not a size, so it is unchanged. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
A sticky cell costs the browser work on every scroll, vertical too, and the plain fallback is not virtualized: with every number pinned, a 24,000-line log scrolled with 50-67 ms frames in headless Chrome (main: 17 ms), and a 7,000-line lockfile did the same at 4x CPU throttling. Keep the pin where the row count is bounded by SOURCE_HIGHLIGHT_MAX_LINES: the highlighted Source view (never longer) and a diff that short, through FILE_PINNED_LINE_NUMBER_STYLE. The plain fallback and longer diffs keep the unpinned gutter, as before this pull request. The test now checks the pinned style and that the fallback's rows stay unpinned. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Since the pin moved to its own style, the test checks that style and the unpinned plain fallback, but no view that keeps the pin: switching the highlighted Source view back to FILE_LINE_NUMBER_STYLE, or the Diff view's `<=` to `<`, still passed it, and lint too, as the other view keeps the pinned style in use. It now builds the highlighted element and the Diff view with the module's constants: the highlighted Source view and a diff of SOURCE_HIGHLIGHT_MAX_LINES rows are pinned, a diff one row longer is not. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Owner
|
Thanks @brynne8! I reproduced the bug on main. After a 300px sideways scroll the line numbers sit at -300px in the highlighted Source view and in the plain fallback past 1,000 lines; in Diff they sit at -297px, because its rows have a 3px change border. With this PR they stay at the left edge of the content. I pushed three commits on top:
Checked on a merge with main: tsc, lint and the full unit suite pass. In headless Chrome, against both a dev and a production build, I checked where the gutter sits after a sideways scroll (light, dark and phone layouts) and the scroll frame times. |
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.
What the reader sees today
Scroll a wide line sideways in the right panel's Source view and the line
number scrolls out of view with the text — a few hundred pixels in, there is
no way to tell which line you are on. Same in the Diff view.
Cause
The gutter is an ordinary flex child of the scrolling row. Nothing pins it to
the scroller's left edge.
Change
The fix —
FILE_LINE_NUMBER_STYLEgetsposition: sticky; left: 0; z-index: 1. All three gutters take this one constant, so they move together:lineNumberStyle; react-syntax-highlighter putsthe numbered span as the first child of each
.file-source-linerow),Sticky needs a scroll container and room to travel; both exist upstream
today, verified by reading:
.file-viewer-content(overflow: auto), which wrapssource, diff and preview alike.
<pre>,and its theme
overflow: autois overridden by the viewer's owncustomStyleoverflow: "visible", so the content box stays the scroller.<pre>/wrapper iswidth: max-contentand every row ismin-width: 100%, so each row spans the widest line and the pinned numberslides inside it; the gutter already has an opaque
var(--bg-panel)background, so text slides behind it, not through it.
The preview modes render no gutter, so they are unaffected. With wrap-lines
on there is no sideways scroll; sticky is then inert, not wrong.
The taste call, stated openly —
FILE_CODE_STYLE13px → 12px. It is notneeded by the fix and rides along for one structural reason: the gutter
repeats the code line's height in px, so
lineHeight(20.8px) must equalfontSize × 1.6, and any font change moves it in the same commit. Landingthe font alone would ship numbers off their line boxes; splitting would be
two PRs for one coupled constant. Reverting the two font-touched lines
(
fontSize: 12,lineHeight: "19.2px", back to 13 / 20.8px) leaves the fixstanding on its own — the test pins the equality and the pin, never a
particular size.
Test
One test in
components/FileViewer.test.mjs, no source-text matching: itevaluates the module's own
FILE_CODE_STYLE/FILE_LINE_NUMBER_STYLEandrenders the lightweight source rows with the real gutter style, then asserts
on the rendered row — gutter
position: sticky,left: 0,zIndex ≥ 1,row
min-width: 100%, and gutter pxlineHeight === fontSize × lineHeight(the coupling nothing else would catch).
Fails before / passes after, both runners: