Skip to content

Commit 944cb4e

Browse files
committed
Fixes #5207: Add side-by-side diff preview capability for multi-file apply_diff
- Created SideBySideDiffViewer component with unified and split-view modes - Added view mode toggle to BatchDiffApproval component - Integrated VSCode diff editor color variables for proper theming - Added comprehensive test coverage for the new component - Fixed vitest configuration to support React development mode for testing The implementation provides: - File navigation controls for multi-file operations - Parallel columns showing original vs modified content - Color coding for additions, deletions, and unchanged lines - Summary view showing all affected files - Toggle between traditional search/replace and modern side-by-side views
1 parent 3a8ba27 commit 944cb4e

File tree

5 files changed

+553
-17
lines changed

5 files changed

+553
-17
lines changed

webview-ui/src/components/chat/BatchDiffApproval.tsx

Lines changed: 57 additions & 17 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,8 @@
11
import React, { memo, useState } from "react"
2+
import { Eye, Code } from "lucide-react"
23
import CodeAccordian from "../common/CodeAccordian"
4+
import { SideBySideDiffViewer } from "./SideBySideDiffViewer"
5+
import { StandardTooltip } from "@/components/ui"
36

47
interface FileDiff {
58
path: string
@@ -19,6 +22,7 @@ interface BatchDiffApprovalProps {
1922

2023
export const BatchDiffApproval = memo(({ files = [], ts }: BatchDiffApprovalProps) => {
2124
const [expandedFiles, setExpandedFiles] = useState<Record<string, boolean>>({})
25+
const [viewMode, setViewMode] = useState<"traditional" | "side-by-side">("side-by-side")
2226

2327
if (!files?.length) {
2428
return null
@@ -31,26 +35,62 @@ export const BatchDiffApproval = memo(({ files = [], ts }: BatchDiffApprovalProp
3135
}))
3236
}
3337

38+
const renderTraditionalView = () => (
39+
<div className="flex flex-col gap-0 border border-border rounded-md p-1">
40+
{files.map((file) => {
41+
// Combine all diffs into a single diff string for this file
42+
const combinedDiff = file.diffs?.map((diff) => diff.content).join("\n\n") || file.content
43+
44+
return (
45+
<div key={`${file.path}-${ts}`}>
46+
<CodeAccordian
47+
path={file.path}
48+
code={combinedDiff}
49+
language="diff"
50+
isExpanded={expandedFiles[file.path] || false}
51+
onToggleExpand={() => handleToggleExpand(file.path)}
52+
/>
53+
</div>
54+
)
55+
})}
56+
</div>
57+
)
58+
3459
return (
3560
<div className="pt-[5px]">
36-
<div className="flex flex-col gap-0 border border-border rounded-md p-1">
37-
{files.map((file) => {
38-
// Combine all diffs into a single diff string for this file
39-
const combinedDiff = file.diffs?.map((diff) => diff.content).join("\n\n") || file.content
40-
41-
return (
42-
<div key={`${file.path}-${ts}`}>
43-
<CodeAccordian
44-
path={file.path}
45-
code={combinedDiff}
46-
language="diff"
47-
isExpanded={expandedFiles[file.path] || false}
48-
onToggleExpand={() => handleToggleExpand(file.path)}
49-
/>
50-
</div>
51-
)
52-
})}
61+
{/* View mode toggle */}
62+
<div className="flex items-center justify-between mb-3 p-2 bg-vscode-editorWidget-background border border-vscode-panel-border rounded-md">
63+
<span className="text-sm text-vscode-foreground">Diff Preview</span>
64+
<div className="flex items-center gap-1">
65+
<StandardTooltip content="Traditional search/replace view" side="top">
66+
<button
67+
className={`px-2 py-1 text-xs rounded flex items-center gap-1 ${
68+
viewMode === "traditional"
69+
? "bg-vscode-button-background text-vscode-button-foreground"
70+
: "bg-transparent text-vscode-descriptionForeground hover:bg-vscode-list-hoverBackground"
71+
}`}
72+
onClick={() => setViewMode("traditional")}>
73+
<Code size={12} />
74+
Traditional
75+
</button>
76+
</StandardTooltip>
77+
<StandardTooltip content="Side-by-side diff view" side="top">
78+
<button
79+
className={`px-2 py-1 text-xs rounded flex items-center gap-1 ${
80+
viewMode === "side-by-side"
81+
? "bg-vscode-button-background text-vscode-button-foreground"
82+
: "bg-transparent text-vscode-descriptionForeground hover:bg-vscode-list-hoverBackground"
83+
}`}
84+
onClick={() => setViewMode("side-by-side")}>
85+
<Eye size={12} />
86+
Side-by-Side
87+
</button>
88+
</StandardTooltip>
89+
</div>
5390
</div>
91+
92+
{/* Render based on view mode */}
93+
{viewMode === "side-by-side" ? <SideBySideDiffViewer files={files} ts={ts} /> : renderTraditionalView()}
5494
</div>
5595
)
5696
})

0 commit comments

Comments
 (0)