perf(markdown): move code highlighting off the main thread into a Shiki worker

Tokenize closed code blocks in a dedicated Shiki Web Worker instead of calling
the shared highlighter synchronously on the UI thread. This removes the
one-shot main-thread highlight stall when a code fence closes on a large block.

Streaming behavior is unchanged: the open (streaming) fence still renders as
plain text and is highlighted once on close. On any worker failure the block
keeps its escaped plain code — highlighting never falls back onto the main
thread.

- Add markdownShikiThemeDefinition (dependency-free CSS-variable theme) so the
  worker can use the theme without pulling in @pierre/diffs / React.
- Add markdown-worker-protocol, markdown-shiki.worker, and the main-thread
  markdown-worker client.
- Route highlightCodeBlocks through the worker; keep the size/VSCode line guard
  and mermaid skip on the main thread.
- Add shiki as a direct dependency (was transitive via @pierre/diffs).
This commit is contained in:
Bohdan Triapitsyn
2026-06-16 01:07:43 +03:00
parent 9ea557d23e
commit 464c4ac0ca
8 changed files with 249 additions and 131 deletions
@@ -0,0 +1,53 @@
import MarkdownShikiWorkerUrl from './markdown-shiki.worker.ts?worker&url';
import type { MarkdownWorkerRequest, MarkdownWorkerResponse } from './markdown-worker-protocol';
// Main-thread client for the markdown Shiki worker. Moves syntax tokenization
// off the UI thread: a closed code block is shipped to the worker, which returns
// ready-to-splice Shiki HTML. On any failure (no worker support, worker crash,
// tokenization error) the promise resolves to `null` and the caller keeps the
// escaped plain-text code — highlighting never falls back onto the main thread.
let worker: Worker | undefined;
let nextId = 0;
const pending = new Map<number, (html: string | null) => void>();
const failAll = (): void => {
pending.forEach((resolve) => resolve(null));
pending.clear();
worker?.terminate();
worker = undefined;
};
const getWorker = (): Worker | undefined => {
if (worker) return worker;
if (typeof window === 'undefined' || typeof Worker === 'undefined') return undefined;
try {
worker = new Worker(MarkdownShikiWorkerUrl, { type: 'module' });
} catch {
return undefined;
}
worker.onmessage = (event: MessageEvent<MarkdownWorkerResponse>) => {
const resolve = pending.get(event.data.id);
if (!resolve) return;
pending.delete(event.data.id);
resolve(event.data.type === 'highlight' ? event.data.html : null);
};
worker.onerror = failAll;
worker.onmessageerror = failAll;
worker.postMessage({ type: 'init' } satisfies MarkdownWorkerRequest);
return worker;
};
/**
* Highlight a complete code block in the worker. Resolves to Shiki `<pre>` HTML,
* or `null` if highlighting is unavailable or failed (caller keeps plain code).
*/
export const highlightCodeInWorker = (code: string, lang: string): Promise<string | null> => {
const instance = getWorker();
if (!instance) return Promise.resolve(null);
const id = ++nextId;
return new Promise<string | null>((resolve) => {
pending.set(id, resolve);
instance.postMessage({ type: 'highlight', id, code, lang } satisfies MarkdownWorkerRequest);
});
};