Skip to content

Commit 88f4786

Browse files
committed
feat(code): add comment-to-agent in diff viewer
1 parent 5a46d67 commit 88f4786

9 files changed

Lines changed: 548 additions & 191 deletions

File tree

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

Lines changed: 10 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -4,12 +4,13 @@ import {
44
extractCloudFileDiff,
55
type ParsedToolCall,
66
} from "@features/task-detail/utils/cloudToolChanges";
7-
import type { FileDiffOptions } from "@pierre/diffs";
8-
import { MultiFileDiff } from "@pierre/diffs/react";
97
import { Flex, Spinner, Text } from "@radix-ui/themes";
108
import type { ChangedFile, Task } from "@shared/types";
119
import type { AcpMessage } from "@shared/types/session-events";
1210
import { useMemo } from "react";
11+
import { useReviewComment } from "../hooks/useReviewComment";
12+
import type { DiffOptions, OnCommentCallback } from "../types";
13+
import { InteractiveFileDiff } from "./InteractiveFileDiff";
1314
import {
1415
DeferredDiffPlaceholder,
1516
DiffFileHeader,
@@ -33,6 +34,7 @@ export function CloudReviewPage({ taskId, task }: CloudReviewPageProps) {
3334
changedFiles,
3435
isLoading,
3536
} = useCloudChangedFiles(taskId, task);
37+
const onComment = useReviewComment(taskId);
3638
const events = session?.events ?? EMPTY_EVENTS;
3739
const summary = useMemo(() => buildCloudEventSummary(events), [events]);
3840

@@ -117,6 +119,7 @@ export function CloudReviewPage({ taskId, task }: CloudReviewPageProps) {
117119
options={diffOptions}
118120
collapsed={isCollapsed}
119121
onToggle={() => toggleFile(file.path)}
122+
onComment={onComment}
120123
/>
121124
</div>
122125
);
@@ -131,12 +134,14 @@ function CloudFileDiff({
131134
options,
132135
collapsed,
133136
onToggle,
137+
onComment,
134138
}: {
135139
file: ChangedFile;
136140
toolCalls: Map<string, ParsedToolCall>;
137-
options: FileDiffOptions<unknown>;
141+
options: DiffOptions;
138142
collapsed: boolean;
139143
onToggle: () => void;
144+
onComment: OnCommentCallback;
140145
}) {
141146
const diff = useMemo(
142147
() => extractCloudFileDiff(toolCalls, file.path),
@@ -154,10 +159,11 @@ function CloudFileDiff({
154159
);
155160

156161
return (
157-
<MultiFileDiff
162+
<InteractiveFileDiff
158163
oldFile={oldFile}
159164
newFile={newFile}
160165
options={{ ...options, collapsed }}
166+
onComment={onComment}
161167
renderCustomHeader={(fd) => (
162168
<DiffFileHeader
163169
fileDiff={fd}
Lines changed: 74 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,74 @@
1+
import { useCallback, useRef } from "react";
2+
3+
interface CommentAnnotationProps {
4+
onSubmit: (text: string) => void;
5+
onCancel: () => void;
6+
}
7+
8+
export function CommentAnnotation({
9+
onSubmit,
10+
onCancel,
11+
}: CommentAnnotationProps) {
12+
const textareaRef = useRef<HTMLTextAreaElement>(null);
13+
14+
const setTextareaRef = useCallback((el: HTMLTextAreaElement | null) => {
15+
(
16+
textareaRef as React.MutableRefObject<HTMLTextAreaElement | null>
17+
).current = el;
18+
if (el) {
19+
requestAnimationFrame(() => el.focus());
20+
}
21+
}, []);
22+
23+
const handleSubmit = useCallback(() => {
24+
const text = textareaRef.current?.value?.trim();
25+
if (text) {
26+
onSubmit(text);
27+
}
28+
}, [onSubmit]);
29+
30+
const handleKeyDown = useCallback(
31+
(e: React.KeyboardEvent) => {
32+
if (e.key === "Enter" && (e.metaKey || e.ctrlKey)) {
33+
e.preventDefault();
34+
handleSubmit();
35+
}
36+
if (e.key === "Escape") {
37+
e.preventDefault();
38+
onCancel();
39+
}
40+
},
41+
[handleSubmit, onCancel],
42+
);
43+
44+
return (
45+
<div
46+
data-comment-annotation=""
47+
className="whitespace-normal rounded-md border border-[var(--gray-5)] bg-[var(--gray-2)] px-2 py-2.5"
48+
>
49+
<textarea
50+
ref={setTextareaRef}
51+
placeholder="Describe the changes you'd like..."
52+
onKeyDown={handleKeyDown}
53+
className="w-full resize-none rounded border border-[var(--gray-6)] bg-[var(--color-background)] p-1.5 font-inherit text-[13px] text-[var(--gray-12)] leading-normal outline-none"
54+
style={{ minHeight: 48 }}
55+
/>
56+
<div className="mt-1.5 flex items-center gap-1.5">
57+
<button
58+
type="button"
59+
onClick={handleSubmit}
60+
className="cursor-pointer rounded border-none bg-[var(--accent-9)] px-2.5 py-0.5 font-medium text-[var(--gray-1)] text-xs leading-[18px]"
61+
>
62+
Send to agent
63+
</button>
64+
<button
65+
type="button"
66+
onClick={onCancel}
67+
className="cursor-pointer border-none bg-transparent px-2 py-0.5 text-[var(--gray-9)] text-xs leading-[18px]"
68+
>
69+
Cancel
70+
</button>
71+
</div>
72+
</div>
73+
);
74+
}

0 commit comments

Comments
 (0)