Skip to content

Commit bd33d76

Browse files
committed
Add diff opening for ACP file links
1 parent a902c62 commit bd33d76

7 files changed

Lines changed: 92 additions & 20 deletions

File tree

anycode/App.tsx

Lines changed: 3 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -76,7 +76,7 @@ const App: React.FC = () => {
7676
isConnected,
7777
followEnabled,
7878
openFile: editors.openFile,
79-
openChangedFileWithDiff: editors.openChangedFileWithDiff,
79+
openFileDiff: editors.openFileDiff,
8080
onAgentStarted: () => {
8181
setRightPanelVisible(true);
8282
setDiffEnabled(true);
@@ -370,7 +370,7 @@ const App: React.FC = () => {
370370
<ChangesPanel
371371
files={git.changedFiles}
372372
branch={git.gitBranch}
373-
onFileClick={editors.openChangedFileWithDiff}
373+
onFileClick={editors.openFileDiff}
374374
onRefresh={git.fetchGitStatus}
375375
onCommit={git.commit}
376376
onPush={git.push}
@@ -436,6 +436,7 @@ const App: React.FC = () => {
436436
onToggleFollow={toggleFollowMode}
437437
onPermissionResponse={agents.sendPermissionResponse}
438438
onOpenFile={editors.openFile}
439+
onOpenFileDiff={editors.openFileDiff}
439440
/>
440441
);
441442

anycode/components/agent/AcpDialog.tsx

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -145,6 +145,7 @@ interface AcpDialogProps {
145145
followEnabled?: boolean;
146146
onToggleFollow?: () => void;
147147
onOpenFile?: (path: string, line?: number, column?: number) => void;
148+
onOpenFileDiff?: (path: string, line?: number, column?: number) => void;
148149
}
149150

150151
const AcpDialogComponent: React.FC<AcpDialogProps> = ({
@@ -182,6 +183,7 @@ const AcpDialogComponent: React.FC<AcpDialogProps> = ({
182183
followEnabled = false,
183184
onToggleFollow,
184185
onOpenFile,
186+
onOpenFileDiff,
185187
}) => {
186188
const [inputValue, setInputValue] = useState('');
187189
const { expanded: expandedToolCalls, toggle: toggleToolCall } = useExpandableItems();
@@ -289,6 +291,7 @@ const AcpDialogComponent: React.FC<AcpDialogProps> = ({
289291
onPermissionResponse={handlePermissionResponse}
290292
onUndoMessage={handleUndoMessage}
291293
onOpenFile={onOpenFile}
294+
onOpenFileDiff={onOpenFileDiff}
292295
/>
293296
</div>
294297
</div>

anycode/components/agent/AcpMessage.css

Lines changed: 18 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -316,6 +316,24 @@
316316
gap: 6px;
317317
}
318318

319+
.acp-tool-call-diff-link {
320+
align-self: flex-start;
321+
border: none;
322+
padding: 0;
323+
background: transparent;
324+
color: var(--accent-color, #7ab7ff);
325+
font: inherit;
326+
font-size: 12px;
327+
font-weight: 600;
328+
cursor: pointer;
329+
text-decoration: underline;
330+
text-align: left;
331+
}
332+
333+
.acp-tool-call-diff-link:hover {
334+
color: var(--accent-color-hover, #9ac8ff);
335+
}
336+
319337
.acp-tool-call-diff-added {
320338
color: #4cc38a;
321339
}

anycode/components/agent/AcpMessage.tsx

Lines changed: 58 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -80,6 +80,7 @@ interface AcpMessageProps {
8080
onPermissionResponse?: (permissionId: string, optionId: string) => void;
8181
onUndo?: () => void;
8282
onOpenFile?: (path: string, line?: number, column?: number) => void;
83+
onOpenFileDiff?: (path: string, line?: number, column?: number) => void;
8384
}
8485

8586
const ToolCallMessage: React.FC<{
@@ -88,7 +89,8 @@ const ToolCallMessage: React.FC<{
8889
toolUpdates?: AcpToolUpdateMessage[];
8990
isExpanded: boolean;
9091
onToggle: () => void;
91-
}> = ({ message, toolResult, toolUpdates, isExpanded, onToggle }) => {
92+
onOpenFileDiff?: (path: string, line?: number, column?: number) => void;
93+
}> = ({ message, toolResult, toolUpdates, isExpanded, onToggle, onOpenFileDiff }) => {
9294

9395
const displayCommand = message.command?.trim() || message.name;
9496
const toolCallView = React.useMemo(
@@ -145,6 +147,14 @@ const ToolCallMessage: React.FC<{
145147
<div className="acp-tool-call-diffs">
146148
{toolCallView.diffs.map((diffEntry, index) => (
147149
<div key={`${diffEntry.path}-${index}`} className="acp-tool-call-diff">
150+
<button
151+
type="button"
152+
className="acp-tool-call-diff-link"
153+
onClick={() => onOpenFileDiff?.(diffEntry.path)}
154+
title={`Open ${diffEntry.path} in diff mode`}
155+
>
156+
{getFileNameFromPath(diffEntry.path)}
157+
</button>
148158
<DiffCodeBlock diff={diffEntry} />
149159
</div>
150160
))}
@@ -585,11 +595,13 @@ const parseMarkdownFileHref = (href: string): ParsedFileLink | null => {
585595

586596
const MarkdownLink: React.FC<React.ComponentProps<'a'> & {
587597
onOpenFile?: (path: string, line?: number, column?: number) => void;
598+
onOpenFileDiff?: (path: string, line?: number, column?: number) => void;
588599
}> = ({
589600
children,
590601
href,
591602
onClick,
592603
onOpenFile,
604+
onOpenFileDiff,
593605
...props
594606
}) => {
595607
const parsedFileLink = href ? parseMarkdownFileHref(href) : null;
@@ -600,9 +612,11 @@ const MarkdownLink: React.FC<React.ComponentProps<'a'> & {
600612
return;
601613
}
602614

603-
if (parsedFileLink && onOpenFile) {
615+
const openFileLink = onOpenFileDiff ?? onOpenFile;
616+
617+
if (parsedFileLink && openFileLink) {
604618
event.preventDefault();
605-
onOpenFile(parsedFileLink.path, parsedFileLink.line, parsedFileLink.column);
619+
openFileLink(parsedFileLink.path, parsedFileLink.line, parsedFileLink.column);
606620
return;
607621
}
608622

@@ -705,11 +719,18 @@ const parseMarkdownParts = (content: string): MarkdownPart[] => {
705719
const MarkdownTextBlock: React.FC<{
706720
content: string;
707721
onOpenFile?: (path: string, line?: number, column?: number) => void;
708-
}> = ({ content, onOpenFile }) => (
722+
onOpenFileDiff?: (path: string, line?: number, column?: number) => void;
723+
}> = ({ content, onOpenFile, onOpenFileDiff }) => (
709724
<ReactMarkdown
710725
remarkPlugins={[remarkGfm, remarkBreaks]}
711726
components={{
712-
a: ({ node: _node, ...props }) => <MarkdownLink {...props} onOpenFile={onOpenFile} />,
727+
a: ({ node: _node, ...props }) => (
728+
<MarkdownLink
729+
{...props}
730+
onOpenFile={onOpenFile}
731+
onOpenFileDiff={onOpenFileDiff}
732+
/>
733+
),
713734
code: MarkdownInlineCode,
714735
}}
715736
>
@@ -894,7 +915,8 @@ const DiffCodeBlock: React.FC<{
894915
const StreamingMarkdownContent: React.FC<{
895916
content: string;
896917
onOpenFile?: (path: string, line?: number, column?: number) => void;
897-
}> = ({ content, onOpenFile }) => (
918+
onOpenFileDiff?: (path: string, line?: number, column?: number) => void;
919+
}> = ({ content, onOpenFile, onOpenFileDiff }) => (
898920
<div className="acp-message-markdown">
899921
{parseMarkdownParts(content).map((part, index) => {
900922
if (part.kind === 'code') {
@@ -909,7 +931,12 @@ const StreamingMarkdownContent: React.FC<{
909931
}
910932

911933
return (
912-
<MarkdownTextBlock key={`text-${index}`} content={part.content} onOpenFile={onOpenFile} />
934+
<MarkdownTextBlock
935+
key={`text-${index}`}
936+
content={part.content}
937+
onOpenFile={onOpenFile}
938+
onOpenFileDiff={onOpenFileDiff}
939+
/>
913940
);
914941
})}
915942
</div>
@@ -919,10 +946,15 @@ const TextMessage: React.FC<{
919946
message: AcpUserMessage | AcpAssistantMessage;
920947
onUndo?: () => void;
921948
onOpenFile?: (path: string, line?: number, column?: number) => void;
922-
}> = ({ message, onUndo, onOpenFile }) => (
949+
onOpenFileDiff?: (path: string, line?: number, column?: number) => void;
950+
}> = ({ message, onUndo, onOpenFile, onOpenFileDiff }) => (
923951
<div className={`acp-message acp-message-${message.role}`}>
924952
<div className="acp-message-content acp-message-content-with-actions">
925-
<StreamingMarkdownContent content={message.content} onOpenFile={onOpenFile} />
953+
<StreamingMarkdownContent
954+
content={message.content}
955+
onOpenFile={onOpenFile}
956+
onOpenFileDiff={onOpenFileDiff}
957+
/>
926958
{message.role === 'user' && onUndo && (
927959
<div className="acp-message-actions">
928960
<button className="acp-undo-button" onClick={onUndo} title="Undo">
@@ -1054,6 +1086,7 @@ export const AcpMessage: React.FC<AcpMessageProps> = ({
10541086
onPermissionResponse,
10551087
onUndo,
10561088
onOpenFile,
1089+
onOpenFileDiff,
10571090
}) => {
10581091
switch (message.role) {
10591092
case 'tool_call':
@@ -1065,6 +1098,7 @@ export const AcpMessage: React.FC<AcpMessageProps> = ({
10651098
toolUpdates={toolUpdates}
10661099
isExpanded={isExpanded}
10671100
onToggle={onToggle}
1101+
onOpenFileDiff={onOpenFileDiff}
10681102
/>
10691103
);
10701104
case 'tool_result':
@@ -1086,9 +1120,22 @@ export const AcpMessage: React.FC<AcpMessageProps> = ({
10861120
/>
10871121
);
10881122
case 'user':
1089-
return <TextMessage message={message} onUndo={onUndo} onOpenFile={onOpenFile} />;
1123+
return (
1124+
<TextMessage
1125+
message={message}
1126+
onUndo={onUndo}
1127+
onOpenFile={onOpenFile}
1128+
onOpenFileDiff={onOpenFileDiff}
1129+
/>
1130+
);
10901131
case 'assistant':
1091-
return <TextMessage message={message} onOpenFile={onOpenFile} />;
1132+
return (
1133+
<TextMessage
1134+
message={message}
1135+
onOpenFile={onOpenFile}
1136+
onOpenFileDiff={onOpenFileDiff}
1137+
/>
1138+
);
10921139
case 'thought':
10931140
if (!onToggle) return null;
10941141
return (

anycode/components/agent/AcpMessages.tsx

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -23,6 +23,7 @@ interface AcpMessagesProps {
2323
onPermissionResponse: (permissionId: string, optionId: string) => void;
2424
onUndoMessage?: (message: AcpUserMessage) => void;
2525
onOpenFile?: (path: string, line?: number, column?: number) => void;
26+
onOpenFileDiff?: (path: string, line?: number, column?: number) => void;
2627
}
2728

2829
const AcpMessagesComponent: React.FC<AcpMessagesProps> = ({
@@ -39,6 +40,7 @@ const AcpMessagesComponent: React.FC<AcpMessagesProps> = ({
3940
onPermissionResponse,
4041
onUndoMessage,
4142
onOpenFile,
43+
onOpenFileDiff,
4244
}) => {
4345
if (messages.length === 0) {
4446
return (
@@ -151,6 +153,7 @@ const AcpMessagesComponent: React.FC<AcpMessagesProps> = ({
151153
toolUpdates={toolUpdates}
152154
onPermissionResponse={onPermissionResponse}
153155
onOpenFile={onOpenFile}
156+
onOpenFileDiff={onOpenFileDiff}
154157
onUndo={
155158
message.role === 'user' && onUndoMessage
156159
? () => onUndoMessage(message)

anycode/hooks/useAgents.ts

Lines changed: 4 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -20,7 +20,7 @@ type UseAgentsParams = {
2020
isConnected: boolean;
2121
followEnabled: boolean;
2222
openFile: (path: string, line?: number, column?: number) => void;
23-
openChangedFileWithDiff: (path: string) => void;
23+
openFileDiff: (path: string, line?: number, column?: number) => void;
2424
onAgentStarted?: () => void;
2525
};
2626

@@ -29,7 +29,7 @@ export const useAgents = ({
2929
isConnected,
3030
followEnabled,
3131
openFile,
32-
openChangedFileWithDiff,
32+
openFileDiff,
3333
onAgentStarted,
3434
}: UseAgentsParams) => {
3535
const [acpSessions, setAcpSessions] = useState<Map<string, AcpSession>>(new Map());
@@ -219,7 +219,7 @@ export const useAgents = ({
219219
if (matchingToolCall?.locations && matchingToolCall.locations.length > 0) {
220220
const loc = matchingToolCall.locations[0];
221221
openFile(loc.path, loc.line, 0);
222-
openChangedFileWithDiff(loc.path);
222+
openFileDiff(loc.path, loc.line, 0);
223223
}
224224
}
225225
}
@@ -281,7 +281,7 @@ export const useAgents = ({
281281
messages: [...existing.messages, messageToAdd],
282282
};
283283
});
284-
}, [openFile, openChangedFileWithDiff, updateSession]);
284+
}, [openFile, openFileDiff, updateSession]);
285285

286286
const handleAcpHistory = useCallback((data: { agent_id: string; history: AcpMessage[] }) => {
287287
const reversedHistory = [...data.history].reverse();

anycode/hooks/useEditors.ts

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -383,8 +383,8 @@ export const useEditors = ({ wsRef, isConnected, diffEnabled, onFileClosed }: Us
383383
editor.applyChange({ edits });
384384
}, []);
385385

386-
const openChangedFileWithDiff = useCallback((path: string) => {
387-
openFile(path);
386+
const openFileDiff = useCallback((path: string, line?: number, column?: number) => {
387+
openFile(path, line, column);
388388

389389
if (wsRef.current && isConnected) {
390390
wsRef.current.emit('git:file-original', { path }, (response: any) => {
@@ -462,7 +462,7 @@ export const useEditors = ({ wsRef, isConnected, diffEnabled, onFileClosed }: Us
462462
closeFile,
463463
saveFile,
464464
openFile,
465-
openChangedFileWithDiff,
465+
openFileDiff,
466466
handleDiagnostics,
467467
handleWatcherEdits,
468468
undoCursor,

0 commit comments

Comments
 (0)