OpenCode v2 Perf — What Kolu Can Adapt
OpenCode Desktop v2's "10×" is a streaming-markdown number. Mapped onto Kolu's actual surfaces, most of it has no consumer — but every technique that does has now shipped — R1 Pierre 1.2.10 + Shiki 4.2.0 (#1360), R2 the highlight worker pool (#1363), and R3+R4 the canvas-gesture p99 harness + rAF-coalesced pan/zoom.
A read of OpenCode Desktop v2’s perf release (≈4 → 45.7 FPS under a 30× CPU throttle) against Kolu’s real architecture. Method: 13 agents — external research ▸ code-map ▸ synthesize ▸ adversarially verify; every load-bearing fact re-checked against source + the npm registry.
The 10× is workload-specific
OpenCode’s six techniques: (1) migrate the message timeline to TanStack
Virtual with chat anchoring (anchorTo:'end', followOnAppend); (2) flicker
/ scroll-jump fixes (overflow-anchor:none + a manual rAF visual-anchor, and
an absolute — not delta — scrollbar-thumb mapping); (3) bump Pierre 1.2.10 +
Shiki 4.2.0; (4) Shiki → Web Worker (stream-tokenize the new suffix, reply
with compact [content, style] tuples); (5) append-only assumptions (a
text-delta accumulator bug that was the bogus 4-FPS baseline, plus freeze-all-
prior-messages-only-the-last-mutates); (6) perf-trace CPU work — profile
under 30× throttle, optimize p95/p99 frame time, and log rejected
experiments (their token-batching and height-estimator ideas regressed the tail).
The headline number is a dev-profiling figure on a long, growing list of variable-height markdown/diff messages. That list is the thing being virtualized, anchored, and worker-highlighted — it’s the whole game.
Why most of it has no consumer in Kolu
Kolu’s perf-relevant surfaces, colored by verdict — green = done / already covered / N-A, teal = the live agent stream:
- #1, #2 (virtualize + scroll-anchor a message list) — no growing list to
virtualize. Code/diffs are already windowed inside Pierre’s
CodeView(inverse-sticky, binary-searched position→line, browseroverflow-anchoralready disabled, own scrollbar). The flicker/scroll fixes live inside Pierre — Kolu inherited the latest of them via the 1.2.10 bump (R1, #1360). - #5 (append-only token accumulator) — no streaming text-delta accumulator.
Agent state arrives as snapshot-then-full-replacement;
Markdown.tsxtakes a complete string and re-renders only when the whole string changes. - #4 markdown worker —
highlight.tsis already the good version: a dynamic-imported singleton, highlighting each completed block once (never per-token). A markdown Shiki worker here would be over-engineering. The worker win is the diff path (R2), not markdown.
What transfers
| Adaptation | Impact | Effort | Status | |
|---|---|---|---|---|
| R1 | Upgrade @pierre/diffs 1.2.1 → 1.2.10 (+ Shiki 4.2.0) | high | medium | shipped · #1360 |
| R2 | Pierre worker pool + shiki-js engine for the diff path | high | medium | shipped · #1363 |
| R3 | CPU-throttle gesture work harness (per-event burst) | medium | medium | shipped · #1368 |
| R4 | rAF-coalesce the canvas wheel pan/zoom write-storm | medium | low | shipped · #1368 |
Low R1 — Bump @pierre/diffs to 1.2.10 + Shiki 4.2.0 — SHIPPED (#1360)
Done
#1360 . ^1.2.1 → ^1.2.10 across solid-pierre, client,
transcript-html; shiki ^3.23.0 → ^4.2.0 in solid-markdown (single shared
copy). Deps-only — no source code changed. The flagged breaking-change risks
were all non-issues: fileGap is used nowhere (the fileGap→spacing rename
never bit), pierreTheme.ts was already on the --*-override convention, and the
PIERRE_DIFFS_LINE_HEIGHT=16 virtualizer contract (#1026) held — proven green by
typecheck, solid-markdown/solid-pierre unit tests, and nix build. Brings
the ≈2.3× faster parsePatchFiles and the worker substrate R2 needs. (pnpmDeps
hash refreshed; pierre/SKILL.md pin still reads 1.2.1 — a stray to fix.)
Low R2 — Pierre worker pool + shiki-js, for the diff path only — SHIPPED (#1363)
Done
#1363 . The 1.2.10 type defs settled the open question: the
engine is a CodeView option (preferredHighlighter: 'shiki-js', in
CODE_VIEW_DIFF_OPTION_KEYS), and worker offload is the CodeView constructor’s
2nd arg (workerManager), built once via getOrCreateWorkerPoolSingleton.
So solid-pierre now owns a session-lived worker-pool singleton (workerPool.ts)
and hands it to every CodeView; plain ASTs paint synchronously while highlighted
tokens stream back off-thread. Kolu supplies the workerFactory
(new Worker(new URL('@pierre/diffs/worker/worker.js', import.meta.url), { type: 'module' })), so the worker bundles through the client’s Vite — which needed
worker.format: 'es' since Pierre’s worker code-splits its Shiki grammars (the
default iife can’t). Browser-only (created in onMount), so the SSR
transcript-html path never spawns a Worker. Pool size 2; never torn down. The
transitive @shikijs/transformers@3 duplicate (a @shikijs/[email protected] beside
the 4.x engine) remains Pierre’s choice — worth watching if it regresses.
Low R3 — CPU-throttle gesture-work harness — SHIPPED (#1368)
Done
#1368 . Shipped as scripts/gesture-p99/
(dependency-free CDP over Node’s built-in WebSocket). It drives the real canvas
and gates R4. Two findings turned the methodology: (1) a headless/CDP Chrome’s rAF
isn’t vsync-capped — it fires ~1:1 with input dispatch (verified: gesture intents
== rAF flushes, headless and headful-under-xvfb), so an rAF-paced fling can’t
show coalescing there; and (2) Chrome 143 dropped HeadlessExperimental.beginFrame,
so manual frame-clocking is out. The harness therefore measures the thing R4
changes directly — per-event main-thread work — via a synchronous burst of K
WheelEvents, immune to the frame scheduler. Write-up:
canvas-gesture-p99.md.
Medium R4 — rAF-coalesce the canvas wheel write-storm — SHIPPED (#1368)
Done
#1368 . OpenCode’s real principle — per-frame work proportional to new data — applied to
Kolu’s actual hot loop. useCanvasViewport.ts now accumulates the frame’s pan
delta (sum) and zoom factor (product toward the last anchor) and applies them once
per requestAnimationFrame instead of per raw wheel event. Feel-neutral for the
cases that actually occur: a pure-pan or pure-zoom frame lands on the
exact per-frame state the per-event path reached (applyGestureBatch telescopes
the math, clamping per event). A mixed pan+zoom frame — only possible when a
pointer-drag pan overlaps a ctrl+wheel zoom, since a wheel event is pan xor
zoom — uses a canonical zoom-then-pan order: a deliberate, bounded, non-accumulating
approximation rather than re-walking the per-event list (which is the work R4 deletes).
All three cases are pinned in transforms.test.ts, since canvas gestures have no
e2e coverage. #1308 deferred this as latent/benign;
R3 confirmed the deferral’s exact condition. On a 16-tile canvas under throttle, a
60-event zoom burst went from an 865 ms main-thread freeze (≈52 dropped 60 Hz
frames) at 6× → 1.3 ms, with tile writes down 60× (2,880 → 48 — the
coalescing ratio). gestures.ts (ownership / preventDefault) stays synchronous
and untouched; only the state-write defers.
Sequencing: R1 ✓ → R2 ✓ → R3 ✓ (the harness gated R4) → R4 ✓ (measured, not guessed).
Confirmed facts & the resolved question
The verification pass settled every load-bearing claim — and R1 (#1360) has since moved the version facts forward:
@pierre/diffsnow 1.2.10 across all 3 packages;shikinow 4.2.0 (single shared copy);@pierre/trees1.0.0-beta.4 (already latest, left untouched). shipped #1360- The Shiki 4 bump was deliberate, not forced: Pierre takes
shikias a regular dep andsolid-markdownshares it, so bumping only one would split the workspace into two shiki copies. verified - 1.2.10 ships the three
./workerexports (the R2 substrate);fileGapused nowhere;pierreTheme.tsalready on--*-override. verified - shiki 4 was a no-op for our usage:
createHighlighter/codeToHtmlsignatures and the--shiki-light/--shiki-darkdual-theme output unchanged. verified