Skip to content

Commit ce4f443

Browse files
committed
feat(code): add PR diff source
1 parent c12cfc3 commit ce4f443

11 files changed

Lines changed: 299 additions & 148 deletions

File tree

apps/code/src/renderer/features/code-editor/stores/diffViewerStore.ts

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -4,7 +4,7 @@ import { create } from "zustand";
44
import { persist } from "zustand/middleware";
55

66
export type ViewMode = "split" | "unified";
7-
export type DiffSource = "local" | "branch";
7+
export type DiffSource = "local" | "branch" | "pr";
88

99
interface DiffViewerStoreState {
1010
viewMode: ViewMode;

apps/code/src/renderer/features/code-review/components/CloudReviewPage.tsx

Lines changed: 16 additions & 51 deletions
Original file line numberDiff line numberDiff line change
@@ -6,13 +6,8 @@ import { Flex, Spinner, Text } from "@radix-ui/themes";
66
import { useReviewNavigationStore } from "@renderer/features/code-review/stores/reviewNavigationStore";
77
import type { Task } from "@shared/types";
88
import { useMemo } from "react";
9-
import { LazyDiff } from "./LazyDiff";
10-
import { PatchedFileDiff } from "./PatchedFileDiff";
11-
import {
12-
DeferredDiffPlaceholder,
13-
ReviewShell,
14-
useReviewState,
15-
} from "./ReviewShell";
9+
import { RemoteDiffList } from "./RemoteDiffList";
10+
import { ReviewShell, useReviewState } from "./ReviewShell";
1611

1712
interface CloudReviewPageProps {
1813
task: Task;
@@ -52,8 +47,8 @@ export function CloudReviewPage({ task }: CloudReviewPageProps) {
5247
getDeferredReason,
5348
} = useReviewState(reviewFiles, allPaths);
5449

55-
const toolCallDiffs = useMemo(() => {
56-
if (remoteFiles.length > 0) return null;
50+
const toolCallFallbacks = useMemo(() => {
51+
if (remoteFiles.length > 0) return undefined;
5752
const diffs = new Map<
5853
string,
5954
{ oldText: string | null; newText: string | null }
@@ -97,48 +92,18 @@ export function CloudReviewPage({ task }: CloudReviewPageProps) {
9792
onCollapseAll={collapseAll}
9893
onUncollapseFile={uncollapseFile}
9994
>
100-
{reviewFiles.map((file) => {
101-
const isCollapsed = collapsedFiles.has(file.path);
102-
const deferredReason = getDeferredReason(file.path);
103-
104-
if (deferredReason) {
105-
return (
106-
<div key={file.path} data-file-path={file.path}>
107-
<DeferredDiffPlaceholder
108-
filePath={file.path}
109-
linesAdded={file.linesAdded ?? 0}
110-
linesRemoved={file.linesRemoved ?? 0}
111-
reason={deferredReason}
112-
collapsed={isCollapsed}
113-
onToggle={() => toggleFile(file.path)}
114-
onShow={() => revealFile(file.path)}
115-
/>
116-
</div>
117-
);
118-
}
119-
120-
const githubFileUrl = prUrl
121-
? `${prUrl}/files#diff-${file.path.replaceAll("/", "-")}`
122-
: undefined;
123-
124-
return (
125-
<div key={file.path} data-file-path={file.path}>
126-
<LazyDiff>
127-
<PatchedFileDiff
128-
file={file}
129-
taskId={taskId}
130-
prUrl={prUrl}
131-
options={diffOptions}
132-
collapsed={isCollapsed}
133-
onToggle={() => toggleFile(file.path)}
134-
commentThreads={showReviewComments ? commentThreads : undefined}
135-
fallback={toolCallDiffs?.get(file.path) ?? null}
136-
externalUrl={githubFileUrl}
137-
/>
138-
</LazyDiff>
139-
</div>
140-
);
141-
})}
95+
<RemoteDiffList
96+
files={reviewFiles}
97+
taskId={taskId}
98+
prUrl={prUrl}
99+
options={diffOptions}
100+
collapsedFiles={collapsedFiles}
101+
toggleFile={toggleFile}
102+
revealFile={revealFile}
103+
getDeferredReason={getDeferredReason}
104+
commentThreads={showReviewComments ? commentThreads : undefined}
105+
fallbacks={toolCallFallbacks}
106+
/>
142107
</ReviewShell>
143108
);
144109
}

apps/code/src/renderer/features/code-review/components/DiffSourceSelector.tsx

Lines changed: 29 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,9 @@
1-
import { CaretDown, GitBranch, HardDrives } from "@phosphor-icons/react";
1+
import {
2+
CaretDown,
3+
GitBranch,
4+
GitPullRequest,
5+
HardDrives,
6+
} from "@phosphor-icons/react";
27
import {
38
Button,
49
DropdownMenu,
@@ -13,22 +18,29 @@ interface DiffSourceSelectorProps {
1318
taskId: string;
1419
effectiveSource: ResolvedDiffSource;
1520
branchAvailable: boolean;
21+
prSourceAvailable: boolean;
1622
defaultBranch: string | null;
1723
}
1824

1925
export function DiffSourceSelector({
2026
taskId,
2127
effectiveSource,
2228
branchAvailable,
29+
prSourceAvailable,
2330
defaultBranch,
2431
}: DiffSourceSelectorProps) {
2532
const setDiffSource = useDiffViewerStore((s) => s.setDiffSource);
2633

27-
if (!branchAvailable || !defaultBranch) return null;
34+
const anyAlternative = branchAvailable || prSourceAvailable;
35+
if (!anyAlternative) return null;
2836

29-
const Icon = effectiveSource === "branch" ? GitBranch : HardDrives;
30-
const branchLabel = `Branch vs. ${defaultBranch}`;
31-
const triggerLabel = effectiveSource === "branch" ? branchLabel : "Local";
37+
const branchLabel = defaultBranch ? `Branch vs. ${defaultBranch}` : "Branch";
38+
const { icon: Icon, label: triggerLabel } = (() => {
39+
if (effectiveSource === "pr") return { icon: GitPullRequest, label: "PR" };
40+
if (effectiveSource === "branch")
41+
return { icon: GitBranch, label: branchLabel };
42+
return { icon: HardDrives, label: "Local" };
43+
})();
3244

3345
return (
3446
<DropdownMenu>
@@ -56,10 +68,18 @@ export function DiffSourceSelector({
5668
<HardDrives size={12} />
5769
Local changes
5870
</DropdownMenuItem>
59-
<DropdownMenuItem onClick={() => setDiffSource(taskId, "branch")}>
60-
<GitBranch size={12} />
61-
{branchLabel}
62-
</DropdownMenuItem>
71+
{branchAvailable && defaultBranch && (
72+
<DropdownMenuItem onClick={() => setDiffSource(taskId, "branch")}>
73+
<GitBranch size={12} />
74+
{branchLabel}
75+
</DropdownMenuItem>
76+
)}
77+
{prSourceAvailable && (
78+
<DropdownMenuItem onClick={() => setDiffSource(taskId, "pr")}>
79+
<GitPullRequest size={12} />
80+
Pull request
81+
</DropdownMenuItem>
82+
)}
6383
</DropdownMenuContent>
6484
</DropdownMenu>
6585
);
Lines changed: 75 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,75 @@
1+
import type { ChangedFile } from "@shared/types";
2+
import type { DiffOptions } from "../types";
3+
import type { PrCommentThread } from "../utils/prCommentAnnotations";
4+
import { LazyDiff } from "./LazyDiff";
5+
import { PatchedFileDiff } from "./PatchedFileDiff";
6+
import { DeferredDiffPlaceholder, type DeferredReason } from "./ReviewShell";
7+
8+
interface RemoteDiffListProps {
9+
files: ChangedFile[];
10+
taskId: string;
11+
options: DiffOptions;
12+
collapsedFiles: Set<string>;
13+
toggleFile: (path: string) => void;
14+
revealFile: (path: string) => void;
15+
getDeferredReason: (path: string) => DeferredReason | null;
16+
prUrl?: string | null;
17+
commentThreads?: Map<number, PrCommentThread>;
18+
fallbacks?: Map<string, { oldText: string | null; newText: string | null }>;
19+
}
20+
21+
export function RemoteDiffList({
22+
files,
23+
taskId,
24+
options,
25+
collapsedFiles,
26+
toggleFile,
27+
revealFile,
28+
getDeferredReason,
29+
prUrl,
30+
commentThreads,
31+
fallbacks,
32+
}: RemoteDiffListProps) {
33+
return files.map((file) => {
34+
const isCollapsed = collapsedFiles.has(file.path);
35+
const deferredReason = getDeferredReason(file.path);
36+
37+
if (deferredReason) {
38+
return (
39+
<div key={file.path} data-file-path={file.path}>
40+
<DeferredDiffPlaceholder
41+
filePath={file.path}
42+
linesAdded={file.linesAdded ?? 0}
43+
linesRemoved={file.linesRemoved ?? 0}
44+
reason={deferredReason}
45+
collapsed={isCollapsed}
46+
onToggle={() => toggleFile(file.path)}
47+
onShow={() => revealFile(file.path)}
48+
/>
49+
</div>
50+
);
51+
}
52+
53+
const githubFileUrl = prUrl
54+
? `${prUrl}/files#diff-${file.path.replaceAll("/", "-")}`
55+
: undefined;
56+
57+
return (
58+
<div key={file.path} data-file-path={file.path}>
59+
<LazyDiff>
60+
<PatchedFileDiff
61+
file={file}
62+
taskId={taskId}
63+
prUrl={prUrl}
64+
options={options}
65+
collapsed={isCollapsed}
66+
onToggle={() => toggleFile(file.path)}
67+
commentThreads={commentThreads}
68+
fallback={fallbacks?.get(file.path) ?? null}
69+
externalUrl={githubFileUrl}
70+
/>
71+
</LazyDiff>
72+
</div>
73+
);
74+
});
75+
}

0 commit comments

Comments
 (0)