Skip to content

feat(app): experiment with Pierre Highlights for code - #57906

Closed
kitlangton wants to merge 3 commits into
betafrom
pierre-highlights
Closed

kitlangton wants to merge 3 commits into
betafrom
pierre-highlights

Conversation

@kitlangton

@kitlangton kitlangton commented Oct 21, 2026 •

Copy link
Copy Markdown
Contributor

Experiment, not for merge as-is. Adds Knobs → Experiments → Code highlighter: Shiki / Highlights (Shiki stays the default) to try Pierre's @pierre/highlights WebAssembly highlighter in two places:

  • Review panel diffs: @pierre/diffs gains preferredHighlighter: "highlights" only in 1.6.x, so the catalog moves 1.5.1 → 1.6.1. The worker pool is keyed by highlighter, so flipping the knob moves open diffs to a new pool. OpenCode is registered as a Zed theme too, mapping Highlights' token categories onto the same --syntax-* variables, so light and dark keep working.
  • Markdown code blocks (markdown worker only): with Highlights picked, streaming blocks go through LiveTokenizer. Each delta is one append edit, so only the growing last line and new lines are re-lexed, and lexer state carries across chunks. Completed blocks use codeToTokens, and full parses build Shiki-shaped HTML. Languages Highlights has no lexer for still use Shiki. markdown.tsx is untouched; the only protocol change is an optional highlighter field on parse and highlight requests.

Other changes: @pierre/highlights is added to minimumReleaseAgeExcludes next to @pierre/diffs (0.2.0 is a day old). The storybook-only marked.tsx is updated for 1.6's DiffsHighlighter API.

Numbers

Headless Chromium, mock-server app, production build, through HiveMind (e2e/performance/highlighter/). The workers were heavily loaded, so main-thread numbers vary a lot between runs. Worker numbers were consistent.

Streaming a code-heavy reply (24 blocks in TS, TSX, Python, Go, CSS, JSON, shell and diff; 17.5 KB in 366 deltas; 2–3 runs each)

Shiki Highlights
Markdown worker busy, 1× CPU 2.7 / 8.9 / 21.1 s 1.3 / 1.5 s
Markdown worker busy, 4× CPU 5.3 / 10.2 / 28.6 s 1.5 / 1.9 s
Worker tasks > 50 ms 10–35 1–4
Worker JS heap, incl. Wasm memory 14.4 MB 8.3 MB
Main-thread heap 23–24 MB 22 MB
Token spans in the DOM 4,455 4,122 (−7%)
Main-thread busy and long tasks no clear difference (noise across runs is larger than the gap)

The main-thread cost the jank report measured (code-token rebuilds in markdown.tsx) is DOM work, which this worker swap does not change.

Opening a 481 KB, 16,960-line TypeScript diff in the review panel: Highlights painted highlighted rows 11.2 s after opening the Review tab (one 4.4 s worker task, plus a 3.8 s main-thread task to render). Shiki had painted no highlighted rows after 90 s (a longer rerun was cut off by the 30-minute HiveMind limit while the fleet was congested).

Bundle (vite build dist, against origin/beta): +4.7 MB raw / +0.9 MB gzip, all in lazy chunks. The startup chunks are unchanged (markdown.worker +2 KB).

  • Highlights is about 266 KB per copy, with its Wasm inlined as base64. There is one copy per realm (main-thread diffs, diffs worker, markdown worker): 804 KB raw / 354 KB gzip in total.
  • The rest comes with the @pierre/diffs 1.6.1 bump (+174 lazy files).
  • Shiki's oniguruma (2 × 608 KB) and its grammars stay, since Shiki is still the default and the fallback.

Correctness

The token text is identical. Colours differ, measured as the share of non-space characters with a different computed colour in the light theme (dark uses the same variables):

TS TSX Python Go CSS JSON shell diff review diff
chars differing 36% 31% 36% 23% 44% 0% 0% 40% 29%
  • Highlights lexes more semantically: types such as Session, Schema, Row and ErrFull are purple, and method calls get the function colour. Shiki paints these as plain variables.
  • Palette choices that could be tuned toward Shiki:
    • property → --syntax-property makes CSS properties, Go fields, .id and #rows orange, where Shiki uses the variable colour.
    • . and : get --syntax-punctuation (grey), where Shiki uses pink.
    • CSS units are teal where Shiki has pink.
  • Diff blocks: Highlights colours whole +/- lines and the headers; Shiki colours only the markers.
  • Shiki's own quirks: in TS, Shiki paints the whole upsert body teal. Highlights doesn't.

Shiki (left) vs Highlights (right), light:

light 1 light 2

Dark:

dark 1 dark 2

Verification

  • bun typecheck passes in packages/ui, packages/session-ui and packages/app (e2e: no new errors).
  • Targeted tests: the new markdown-highlights.test.ts checks that streamed chunks end with the same tokens as one-shot highlighting, and fails if the stable-line cursor is off by one. The markdown worker queue, transport and protocol tests and the motion-tuning tests pass.
  • lint:changed: the changed files are clean.
  • CI: the e2e failures are the same tests that fail on beta's own runs (e.g. 39665329254), plus one flaky mobile motion test (mobile-timeline-scroll.spec.ts:855). Build, unit and session-ui component jobs pass.

Review fixes (55c7eae, 19d9744)

  • Shiki fallback: if Highlights fails to load or to highlight in the markdown worker, that block falls back to Shiki and the worker stays on Shiki for the rest of the session. The cached promise is replaced with a resolved undefined, so no rejection stays cached, and the session deliberately does not retry, since a failed load would only fail again.
  • Precache: onDemandAsset now excludes the highlights-* and markdown-highlights-* chunks, which carry the inlined Wasm, from the PWA precache, so they load only when Highlights is picked.
  • Electron: all three Highlights chunks import (compiling and instantiating their Wasm) in a sandboxed Electron 44.9.1 renderer (Chrome 152) and in module workers, served from a privileged oc:// scheme like the app's. The desktop app sets no CSP.
  • Dark theme: characters that change colour: TS 31%, TSX 62%, Python 47%, Go 35%, CSS 53%, JSON 13%, shell 0%, diff 59%. About half of that is punctuation taking --syntax-punctuation instead of the text colour.

@kitlangton kitlangton closed this Oct 21, 2026
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant