import React from 'react'; import { cn } from '@/lib/utils'; import { getLanguageFromExtension } from '@/lib/toolHelpers'; import { useThemeSystem } from '@/contexts/useThemeSystem'; import { getMarkdownSyntaxVars } from '@/components/chat/markdown/markdownSyntaxVars'; import { useWorkerHighlightedLines, type WorkerHighlightedLinesResult, } from '@/components/code/useWorkerHighlightedLines'; import { parseDiffToUnified } from './message/toolRenderers'; // Keep the line's layout stable while a cold worker request finishes. Plain // text appears only if highlighting fails, avoiding a visible color flash. interface CodeLineContentProps { content: string; html: string | undefined; status: WorkerHighlightedLinesResult['status']; } const CodeLineContent: React.FC = ({ content, html, status }) => { if (status === 'ready' && html !== undefined) { return ; } if (status === 'loading') { return {content}; } return {content}; }; interface DiffPreviewProps { diff: string; filePath?: string; } export const DiffPreview: React.FC = ({ diff, filePath }) => { const { currentTheme } = useThemeSystem(); const syntaxVars = React.useMemo(() => getMarkdownSyntaxVars(currentTheme), [currentTheme]); const hunks = React.useMemo(() => parseDiffToUnified(diff), [diff]); const language = getLanguageFromExtension(filePath || hunks[0]?.file) || 'text'; // Flatten all rendered lines so the whole diff tokenizes in one worker call. const flatContent = React.useMemo( () => hunks.flatMap((hunk) => hunk.lines.map((line) => line.content)).join('\n'), [hunks], ); const highlighted = useWorkerHighlightedLines(flatContent, language); let lineCursor = 0; return (
{hunks.map((hunk, hunkIdx) => (
{`${hunk.file || filePath?.split('/').pop() || 'file'} (line ${hunk.oldStart})`}
{hunk.lines.map((line, lineIdx) => { const html = highlighted.lines?.[lineCursor]; lineCursor += 1; return (
{line.lineNumber || ''}
); })}
))}
); }; interface WritePreviewProps { content: string; filePath?: string; } export const WritePreview: React.FC = ({ content, filePath }) => { const { currentTheme } = useThemeSystem(); const syntaxVars = React.useMemo(() => getMarkdownSyntaxVars(currentTheme), [currentTheme]); const lines = React.useMemo(() => content.split('\n'), [content]); const language = getLanguageFromExtension(filePath ?? '') || 'text'; const displayPath = filePath?.split('/').pop() || 'New file'; const lineCount = Math.max(lines.length, 1); const headerLineLabel = lineCount === 1 ? 'line 1' : `lines 1-${lineCount}`; const highlighted = useWorkerHighlightedLines(content, language); return (
{`${displayPath} (${headerLineLabel})`}
{lines.map((line, lineIdx) => (
{lineIdx + 1}
))}
); };