perf(desktop): shiki HTML cache + preload-hydrated stash — kill the highlight flash, instant first clicks - #368
Conversation
…ighlight flash, instant first clicks Post-#366 diagnosis on the MBP (layout-shift + Shiki-swap observers over CDP): the remaining 'occasional flash' was code cards re-highlighting from scratch on EVERY remount — the SWR wholesale replace and the idle budget raise both remount cards, and each rendered plain-first then swapped to highlighted ~120ms later, visibly, in the viewport (128 swaps per switch, 5 in-view; CLS entries 0.04-0.12 from the same remounts). 1. shiki-html-cache.ts: module-level LRU (400 entries) of highlighted HTML keyed on (lang, code). A fence highlighted once this app run renders its colored HTML SYNCHRONOUSLY in the mount commit — remounts never show the plain->color pop. Replaces the react-shiki component in the chat path (right-rail preview + diff-lines keep react-shiki). Fail-open: tokenize errors leave plain code. 2. transcript-preload.ts also hydrates the in-memory stash (not just the disk cache), so the FIRST click on a pinned/visible session after app launch paints synchronously — previously the stash only covered sessions visited this run, which is why a fresh reopen still felt slow. Stash cap 20 (holds MAX_PRELOAD=12 + a click working set); cap now an exported constant with a behavior-contract test instead of hardcoded 8s.
|
| Filename | Overview |
|---|---|
| apps/desktop/src/components/chat/shiki-html-cache.ts | New module-level LRU (400 entries) for Shiki highlighted HTML. Cache-first hook initialises state synchronously so remounts skip the plain-to-color swap. Fail-open on tokenise errors, alive guard prevents stale async updates. |
| apps/desktop/src/components/chat/shiki-html-cache.test.tsx | New test file covering all documented contracts: miss-to-async fill, synchronous remount hit, unknown-language fallback, independent caching per code string, fail-open, and LRU eviction boundary. |
| apps/desktop/src/components/chat/shiki-highlighter.tsx | Replaces react-shiki with the new CachedHighlight component; SHIKI_THEME/SHIKI_COLOR_REPLACEMENTS moved to shiki-html-cache.ts and re-exported here for back-compat. |
| apps/desktop/src/app/transcript-preload.ts | Adds stashTranscript(session.id, rows) call after every successful disk-cache push, so the in-memory stash is hydrated at boot. |
| apps/desktop/src/app/transcript-stash.ts | MAX_ENTRIES raised from 8 to 20 (exported as MAX_STASH_ENTRIES). LRU logic unchanged and correct. |
| apps/desktop/src/app/transcript-stash.test.ts | Tests rewritten against exported MAX_STASH_ENTRIES with regression guard asserting cap >= MAX_PRELOAD + 4. |
| apps/desktop/src/app/transcript-preload.test.ts | Adds afterEach cleanup and new integration test verifying preload rows are readable synchronously from the stash. |
Reviews (2): Last reviewed commit: "Merge branch 'main' into perf/shiki-cach..." | Re-trigger Greptile
…ELOAD-relative stash guard, preload stash cleanup
E2E on the MBP (build
|
| Switch | CLS entries | in-view Shiki swaps |
|---|---|---|
| pre-fix baseline | 0.04–0.12 | 5 |
| warm-up pass (this build) | 0–1 | 0 |
| measured pass ×4 | 0 | 0 |
First-click latency (blank/loader frames after click):
| Switch | Blank frames |
|---|---|
| Cold A (first visit) | 0 (preload-hydrated stash) |
| Cold B | 0 |
| Warm back→A/B | 0 |
The plain→color pop is gone (highlighted HTML now renders synchronously from the module cache on every remount), and the preload→stash hydration means even first clicks after launch paint with zero blank frames. Debug port closed, app relaunched clean.
Symptoms (user re-report after #363/#366)
Diagnosis (CDP: layout-shift observer + Shiki-swap MutationObserver + screencast)
setMessages([])blank).Fix
shiki-html-cache.ts— module LRU (400) of highlighted HTML keyed on (lang, code). Cache hit renders colored HTML synchronously in the mount commit; remounts can never show the plain→highlighted swap. Chat path drops the react-shiki component (right-rail preview + diff-lines unchanged). Fail-open on tokenize errors.Tests
6 new shiki-cache tests (sync remount hit, single tokenize per fence, text fallback, fail-open, LRU) + preload-hydrates-stash test + stash tests rewritten against the exported cap. Full desktop suite 165 files green, tsc clean, eslint clean.
E2E
Post-deploy switch loop + swap-observer re-run on the MBP to follow in comments.