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>
40 lines
1.3 KiB
TypeScript
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);
|
|
}
|
|
});
|