From 1f9f0b6390e157e88221848e5b5b94d5ffce5be5 Mon Sep 17 00:00:00 2001 From: iliya Date: Sat, 21 Mar 2026 12:39:19 +0200 Subject: [PATCH] feat: syntax highlighting in diff preview + fix Allow All button style MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit - DiffViewer: add syntaxHighlight prop — maps diff lines to highlighted HTML via highlightLines(), passed to DiffLineRow - ToolApprovalDiffPreview: enable syntaxHighlight for file diffs - Allow All button: proper border style matching Deny button sizing --- .../components/chat/viewers/DiffViewer.tsx | 49 ++++++++++++++++--- .../team/ToolApprovalDiffPreview.tsx | 1 + .../components/team/ToolApprovalSheet.tsx | 8 +-- 3 files changed, 47 insertions(+), 11 deletions(-) diff --git a/src/renderer/components/chat/viewers/DiffViewer.tsx b/src/renderer/components/chat/viewers/DiffViewer.tsx index edf6a1f0..947fee84 100644 --- a/src/renderer/components/chat/viewers/DiffViewer.tsx +++ b/src/renderer/components/chat/viewers/DiffViewer.tsx @@ -1,4 +1,4 @@ -import React from 'react'; +import React, { useMemo } from 'react'; import { CODE_BG, @@ -19,6 +19,7 @@ import { TAG_TEXT, } from '@renderer/constants/cssVariables'; import { getBaseName } from '@renderer/utils/pathUtils'; +import { highlightLines } from '@renderer/utils/syntaxHighlighter'; import { formatTokens } from '@shared/utils/tokenFormatting'; import { diffLines as semanticDiffLines } from 'diff'; import { Pencil } from 'lucide-react'; @@ -33,6 +34,7 @@ interface DiffViewerProps { newString: string; // The new text maxHeight?: string; // CSS max-height class (default: "max-h-96") tokenCount?: number; // Optional token count to display in header + syntaxHighlight?: boolean; // Enable syntax highlighting via highlight.js } interface DiffLine { @@ -259,9 +261,10 @@ function inferLanguage(fileName: string): string { interface DiffLineRowProps { line: DiffLine; + highlightedHtml?: string; } -const DiffLineRow: React.FC = ({ line }): React.JSX.Element => { +const DiffLineRow: React.FC = ({ line, highlightedHtml }): React.JSX.Element => { // Theme-aware styles using CSS variables const getStyles = ( type: DiffLine['type'] @@ -312,10 +315,18 @@ const DiffLineRow: React.FC = ({ line }): React.JSX.Element => {style.prefix} - {/* Content */} - - {line.content ?? ' '} - + {/* Content — optionally syntax-highlighted via hljs (HTML-escaped, safe) */} + {highlightedHtml !== undefined ? ( + + ) : ( + + {line.content ?? ' '} + + )} ); }; @@ -330,6 +341,7 @@ export const DiffViewer: React.FC = ({ newString, maxHeight = 'max-h-96', tokenCount, + syntaxHighlight = false, }): React.JSX.Element => { // Compute diff const oldLines = oldString.split(/\r?\n/); @@ -337,6 +349,29 @@ export const DiffViewer: React.FC = ({ const diffLines = generateDiff(oldLines, newLines); const stats = computeStats(diffLines); + // Syntax highlighting: build a map from content line → highlighted HTML + const highlightMap = useMemo(() => { + if (!syntaxHighlight) return null; + const oldHtml = highlightLines(oldString, fileName); + const newHtml = highlightLines(newString, fileName); + // Map each diff line to its highlighted HTML by tracking old/new line indices + const map = new Map(); + let oldIdx = 0; + let newIdx = 0; + for (let i = 0; i < diffLines.length; i++) { + const line = diffLines[i]; + if (line.type === 'removed') { + map.set(i, oldHtml[oldIdx++] ?? ''); + } else if (line.type === 'added') { + map.set(i, newHtml[newIdx++] ?? ''); + } else { + map.set(i, oldHtml[oldIdx++] ?? ''); + newIdx++; + } + } + return map; + }, [syntaxHighlight, oldString, newString, fileName, diffLines]); + // Infer language from file extension const detectedLanguage = inferLanguage(fileName); @@ -396,7 +431,7 @@ export const DiffViewer: React.FC = ({
{diffLines.map((line, index) => ( - + ))} {diffLines.length === 0 && (
diff --git a/src/renderer/components/team/ToolApprovalDiffPreview.tsx b/src/renderer/components/team/ToolApprovalDiffPreview.tsx index 7912503c..bd9f01b0 100644 --- a/src/renderer/components/team/ToolApprovalDiffPreview.tsx +++ b/src/renderer/components/team/ToolApprovalDiffPreview.tsx @@ -144,6 +144,7 @@ export const ToolApprovalDiffPreview: React.FC = ( oldString={diff.oldString} newString={diff.newString} maxHeight="max-h-[300px]" + syntaxHighlight />
)} diff --git a/src/renderer/components/team/ToolApprovalSheet.tsx b/src/renderer/components/team/ToolApprovalSheet.tsx index 0d3ff62c..83d1c977 100644 --- a/src/renderer/components/team/ToolApprovalSheet.tsx +++ b/src/renderer/components/team/ToolApprovalSheet.tsx @@ -288,21 +288,21 @@ export const ToolApprovalSheet: React.FC = () => {