@@ -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
8586const 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
586596const 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[] => {
705719const 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<{
894915const 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 (
0 commit comments