perf: lazy-load highlight.js in ToolCall for better code-splitting
ToolCall.tsx previously imported highlight.js statically, pulling the entire hljs bundle into the main Chat chunk and preventing Vite from splitting it into the lazy markdown chunk. Now hljs is dynamically imported and loaded on first use. This: - Removes the Vite build warning about mixed static/dynamic imports - Reduces the Chat chunk by ~1.6 KB - Consolidates hljs into the lazy markdown chunk - Renders unhighlighted text instantly, then re-renders with syntax highlighting once hljs loads (typically < 100ms)
This commit is contained in:
@@ -1,6 +1,5 @@
|
||||
import { useState, useCallback, useMemo, useEffect, useRef, memo } from 'react';
|
||||
import { ChevronRight, ChevronDown, Check, Copy, WrapText, AlignLeft } from 'lucide-react';
|
||||
import hljs from '../lib/highlight';
|
||||
import { copyToClipboard } from '../lib/clipboard';
|
||||
import { useT } from '../hooks/useLocale';
|
||||
import { useTheme } from '../hooks/useTheme';
|
||||
@@ -124,11 +123,16 @@ function isStructured(text: string): boolean {
|
||||
return termHits / lines.length > 0.3;
|
||||
}
|
||||
|
||||
/** Highlight code using highlight.js, returns HTML string or null */
|
||||
// Lazy-loaded highlight.js instance (keeps hljs out of the main bundle)
|
||||
let _hljs: typeof import('highlight.js/lib/core').default | null = null;
|
||||
const _hljsPromise = import('../lib/highlight').then(m => { _hljs = m.default; return m.default; });
|
||||
|
||||
/** Highlight code using highlight.js, returns HTML string or null.
|
||||
* Returns null until hljs is loaded (callers should re-render after load). */
|
||||
function highlightCode(text: string): string | null {
|
||||
if (!text || !isStructured(text)) return null;
|
||||
if (!text || !isStructured(text) || !_hljs) return null;
|
||||
try {
|
||||
const result = hljs.highlightAuto(text);
|
||||
const result = _hljs.highlightAuto(text);
|
||||
return result.value;
|
||||
} catch {
|
||||
return null;
|
||||
@@ -178,7 +182,11 @@ function CopyButton({ text }: { text: string }) {
|
||||
}
|
||||
|
||||
export function HighlightedPre({ text, className, wrap }: { text: string; className: string; wrap?: boolean }) {
|
||||
const highlighted = useMemo(() => highlightCode(text), [text]);
|
||||
const [hljsReady, setHljsReady] = useState(!!_hljs);
|
||||
useEffect(() => {
|
||||
if (!_hljs) { _hljsPromise.then(() => setHljsReady(true)); }
|
||||
}, []);
|
||||
const highlighted = useMemo(() => highlightCode(text), [text, hljsReady]); // eslint-disable-line react-hooks/exhaustive-deps
|
||||
const wrapClass = wrap ? 'whitespace-pre-wrap break-words overflow-x-hidden' : '';
|
||||
|
||||
if (highlighted) {
|
||||
|
||||
Reference in New Issue
Block a user