feat(desktop): File tabs and a page's source follow View settings' syntax theme
Merged
DA
13d ago+1086+1086+0123456789012345678901234567890123456789-62-62-01234567890123456789
Summary
PR 2 of "one look for all code", after #3848 (type). The File tab and a page's source now follow View settings' syntax theme, using option B: the panel recolours with the diff bundle's highlighter.
- Default pair costs nothing new. With
github-dark-default/github-light-default(View settings' defaults, and the host's own themes), the host's colours are final. The diff bundle and its pool are never loaded:ThemedCodeLinesViewrenders the plainCodeLinesViewand doesn't read the bundle atom. Checked in the harness: nodiff-view.jsrequest and no worker started. - Custom pair: host first paint, then a per-window swap.
- The host's GitHub colours paint first.
- Then each window of lines held (
windowAt, 500 lines) is coloured by the bundle's newcolourLines, in the kept pool's workers (#3752), or on the main thread where the pool settled there. - The result replaces that window's runs only if every line's words are identical, so the same text sits in the same line boxes and only colours change. No layout shift.
- The run then stands on the theme's ground and ink (Pierre's
themeStyles), as a diff in that theme does.
- Grammar mismatch keeps the host colours. It never falls back to uncoloured:
- A window the host drew plain (no coloured run) isn't sent.
- A name Pierre has no grammar for (
text) returns nothing. - A failed or mismatched result keeps the host's runs.
- Live: changing the theme recolours what's held, with no re-read. Switching back to the default pair returns to the host's colours.
- View settings on the File tab and page source gains the Syntax theme rows (Light/Dark, then "For dark mode" / "For light mode"), via
ThemeRows, which Files changed shares. CodeLineis memoised, so a window coloured anew redraws only its own lines rather than every line held.- Getting to the theme sooner:
- Visible window first. The window in view is asked for first, then the rest by nearness, two at a time (one per worker), and the order is re-picked on scroll.
- Kept across tabs. Each window's colours are kept in nested maps by read (
RunRead { source, revision }: the path and commit, or the page and version), dark theme, light theme, and window, bounded to 64 windows (oldest out first). Reopening a File tab, or switching between two, draws the theme at once with no request. Kept colours are drawn only if they still match the window's words, and a mounted run never re-asks for a window just because it was evicted globally. - Warmed on open. With a custom pair,
ThemeWarmerin the File tab and page source loads the bundle and has the pool colour one empty line of the file's name, before the host's read lands. That puts the themes and the grammar into the workers.
Performance
Chromium harness, 3 runs each. Times are from navigation. "Read delay" simulates the host's read landing later.
A normal File tab (one 500-line window held, PR #2908's build.mjs):
| read delay | first paint (host colours) | first window recoloured | swap after paint |
|---|---|---|---|
| 0 ms | 322–334 ms | 876–935 ms | ~0.55–0.6 s |
| 400 ms | 671–690 ms | 1361–1422 ms | ~0.7 s |
| 800 ms | 1057 ms | 1596–1631 ms | ~0.55 s |
- The bundle request starts at about 250 ms, before the read lands (warming).
- Most of what's left is the worker tokenising the 500 lines on Shiki's JS engine.
- Reopening the tab is instant from the kept colours.
A big file with everything held at once (4,960 lines, 10 windows, worse than the tab's scrolled reads), read delay 400 ms:
- First paint: 771–828 ms, the same as the default pair (780–819 ms).
- First window recoloured: 2146–2255 ms (about 1.3–1.5 s after paint). The 5,000-row initial commit (a ~500 ms main-thread task) delays it.
- All windows recoloured: 3.8–3.9 s.
- Scroll is unchanged in both pairs (frame p95 16.8–17.0 ms).
The default pair loads no bundle and starts no worker (verified), so it costs nothing new.
Screenshots
Electron (xvfb). The harness draws a File tab's bar end over host-coloured build.mjs.
| light | dark | |
|---|---|---|
| before (default GitHub pair, host colours) | ![]() | ![]() |
| after (Solarized Light / Dracula) | ![]() | ![]() |
| View settings | ![]() | ![]() |
Evidence
themed-lines.test.ts:- The default pair never imports the bundle and draws the host colours.
- A custom pair asks once per window (500 + 200), with each window's words and the look's themes, then draws the returned colours on the theme's ground.
- A window given no colours or other words, or drawn plain by the host, keeps the host's runs (and the plain one is never asked for).
- Changing the theme recolours.
- The window in view is asked for first, at most two at a time.
- A run drawn again takes its kept colours without asking.
ThemeWarmerloads nothing in the default pair, and warms the file's grammar in a custom pair.
colour-lines.test.ts: Pierre's HAST is read back into runs: same-colour tokens joined, the line break dropped, the theme's ground and ink read. Wrong line counts or missing theme colours give no result.file-view-menu.test.tsandpage-pane.test.ts: the popover now lists Syntax theme and For dark mode.- Local checks: 2449 panel, desktop and settings tests; tsc for panel and desktop; oxlint; biome; knip; repository-checks; the desktop build (bundle budget).
Made with Luke





