Files
Codewalkers/apps/web/src/components/review/highlight-worker.ts
Lukas May 0608900a53 feat: move syntax highlighting off main thread via Web Worker pool
Adds a 2-worker pool in use-syntax-highlight.ts so shiki tokenisation
runs off the main thread. Callers continue to receive null while the
worker is in flight and a LineTokenMap once it resolves — no caller
changes needed.

Fallback: if Worker construction is blocked (e.g. CSP), the hook falls
back to the existing createHighlighter singleton but processes 200 lines
at a time, yielding between chunks via scheduler.yield()/setTimeout(0)
to avoid long tasks.

Also adds worker.format:'es' to vite.config.ts (required when the app
uses code-splitting) and covers all paths with Vitest tests.

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-03-06 19:39:31 +01:00

40 lines
1.3 KiB
TypeScript

import type { ThemedToken } from 'shiki';
export interface HighlightRequest {
id: string;
filePath: string;
language: string; // resolved lang name (e.g. "typescript") or "text"
code: string; // full joined content of new-side lines to highlight
lineNumbers: number[]; // new-side line numbers to map tokens back to
}
export interface HighlightResponse {
id: string;
tokens: Array<{ lineNumber: number; tokens: ThemedToken[] }>;
error?: string;
}
self.addEventListener('message', async (event: MessageEvent<HighlightRequest>) => {
const { id, language, code, lineNumbers } = event.data;
try {
const { codeToTokens } = await import('shiki');
const result = await codeToTokens(code, {
lang: language as Parameters<typeof codeToTokens>[1]['lang'],
theme: 'github-dark-default',
});
const tokens: HighlightResponse['tokens'] = result.tokens.map((lineTokens, idx) => ({
lineNumber: lineNumbers[idx] ?? idx,
tokens: lineTokens,
}));
const response: HighlightResponse = { id, tokens };
self.postMessage(response);
} catch (err) {
const response: HighlightResponse = {
id,
tokens: [],
error: err instanceof Error ? err.message : String(err),
};
self.postMessage(response);
}
});