@@ -26,8 +26,9 @@ import {
2626 Loader2 ,
2727 Clock ,
2828 Check ,
29+ X ,
2930} from 'lucide-react' ;
30- import { CubeLoading } from '../../component-library' ;
31+ import { CubeLoading , IconButton } from '../../component-library' ;
3132import type { ToolCardProps } from '../types/flow-chat' ;
3233import { BaseToolCard , ToolCardHeader } from './BaseToolCard' ;
3334import { useSnapshotState } from '../../tools/snapshot_system/hooks/useSnapshotState' ;
@@ -45,6 +46,7 @@ import { useToolCardHeightContract } from './useToolCardHeightContract';
4546import { hasNonFileUriScheme } from '@/shared/utils/pathUtils' ;
4647import { notificationService } from '@/shared/notification-system' ;
4748import { useGitState } from '@/tools/git/hooks/useGitState' ;
49+ import { ToolCardHeaderActions } from './ToolCardHeaderActions' ;
4850import './FileOperationToolCard.scss' ;
4951
5052const log = createLogger ( 'FileOperationToolCard' ) ;
@@ -59,10 +61,20 @@ export const FileOperationToolCard: React.FC<FileOperationToolCardProps> = ({
5961 toolItem,
6062 config,
6163 sessionId,
62- onOpenInEditor
64+ onOpenInEditor,
65+ onConfirm,
66+ onReject,
6367} ) => {
6468 const { t } = useTranslation ( 'flow-chat' ) ;
65- const { toolCall, toolResult, status, isParamsStreaming, partialParams } = toolItem ;
69+ const {
70+ toolCall,
71+ toolResult,
72+ status,
73+ isParamsStreaming,
74+ partialParams,
75+ requiresConfirmation,
76+ userConfirmed,
77+ } = toolItem ;
6678 const toolId = toolItem . id ?? toolCall ?. id ;
6779
6880 const [ isErrorExpanded , setIsErrorExpanded ] = useState ( false ) ;
@@ -128,6 +140,13 @@ export const FileOperationToolCard: React.FC<FileOperationToolCardProps> = ({
128140 const contentPreview = getContent ( ) ;
129141
130142 const isFailed = status === 'error' || ( toolResult && 'success' in toolResult && ! toolResult . success ) ;
143+ const showConfirmationActions = Boolean (
144+ requiresConfirmation &&
145+ ! userConfirmed &&
146+ status !== 'completed' &&
147+ status !== 'cancelled' &&
148+ status !== 'error'
149+ ) ;
131150
132151 const fileName = currentFilePath ?
133152 ( currentFilePath . split ( / [ / \\ ] / ) . pop ( ) || t ( 'context.file' ) ) :
@@ -443,7 +462,7 @@ export const FileOperationToolCard: React.FC<FileOperationToolCardProps> = ({
443462
444463 if (
445464 ( e . target as HTMLElement ) . closest (
446- '.file-op-diff-pill, .file-op-open-full-button' ,
465+ '.file-op-diff-pill, .file-op-open-full-button, .tool-card-header-actions ' ,
447466 )
448467 ) {
449468 return ;
@@ -468,6 +487,18 @@ export const FileOperationToolCard: React.FC<FileOperationToolCardProps> = ({
468487 toolItem . toolName ,
469488 ] ) ;
470489
490+ const handleConfirmClick = useCallback ( ( e : React . MouseEvent < HTMLButtonElement > ) => {
491+ e . preventDefault ( ) ;
492+ e . stopPropagation ( ) ;
493+ onConfirm ?.( toolCall ?. input ) ;
494+ } , [ onConfirm , toolCall ?. input ] ) ;
495+
496+ const handleRejectClick = useCallback ( ( e : React . MouseEvent < HTMLButtonElement > ) => {
497+ e . preventDefault ( ) ;
498+ e . stopPropagation ( ) ;
499+ onReject ?.( ) ;
500+ } , [ onReject ] ) ;
501+
471502 const handleOpenFullCodeClick = useCallback ( ( e : React . MouseEvent ) => {
472503 e . preventDefault ( ) ;
473504 e . stopPropagation ( ) ;
@@ -720,12 +751,11 @@ export const FileOperationToolCard: React.FC<FileOperationToolCardProps> = ({
720751 const expandedContent = renderExpandedContent ( ) ;
721752 const hasExpandableContent =
722753 ! isFailed &&
723- ! isDeleteTool &&
724754 Boolean ( expandedContent ) ;
725755
726756 const isCardContentExpanded =
727- ! isDeleteTool &&
728757 ! isFailed &&
758+ ! isDeleteTool &&
729759 isContentExpanded ;
730760
731761 const renderHeader = ( ) => {
@@ -793,12 +823,34 @@ export const FileOperationToolCard: React.FC<FileOperationToolCardProps> = ({
793823 )
794824 }
795825 extra = {
796- < >
826+ < ToolCardHeaderActions className = "file-op-header-actions" >
797827 { isParamsStreaming && ( status === 'preparing' || status === 'streaming' ) && (
798828 < span className = "params-streaming-indicator" >
799829 { currentFilePath ? t ( 'toolCards.file.receivingParams' ) : t ( 'toolCards.file.analyzing' ) }
800830 </ span >
801831 ) }
832+ { showConfirmationActions && (
833+ < >
834+ < IconButton
835+ className = "tool-card-header-action file-op-header-action file-op-confirm-btn"
836+ variant = "success"
837+ size = "xs"
838+ onClick = { handleConfirmClick }
839+ tooltip = { t ( 'toolCards.mcp.confirmExecute' ) }
840+ >
841+ < Check size = { 12 } />
842+ </ IconButton >
843+ < IconButton
844+ className = "tool-card-header-action file-op-header-action file-op-reject-btn"
845+ variant = "danger"
846+ size = "xs"
847+ onClick = { handleRejectClick }
848+ tooltip = { t ( 'toolCards.mcp.cancel' ) }
849+ >
850+ < X size = { 12 } />
851+ </ IconButton >
852+ </ >
853+ ) }
802854 { canOpenFullCode && (
803855 < Tooltip content = { t ( 'toolCards.file.openFullCodeHint' ) } placement = "top" >
804856 < button
@@ -811,7 +863,7 @@ export const FileOperationToolCard: React.FC<FileOperationToolCardProps> = ({
811863 </ button >
812864 </ Tooltip >
813865 ) }
814- </ >
866+ </ ToolCardHeaderActions >
815867 }
816868 statusIcon = { isDeleteTool ? null : renderStatusIcon ( ) }
817869 />
@@ -829,6 +881,28 @@ export const FileOperationToolCard: React.FC<FileOperationToolCardProps> = ({
829881 < CompactToolCardHeader
830882 icon = { getDeleteStatusIcon ( ) }
831883 content = { renderDeleteContent ( ) }
884+ extra = { showConfirmationActions ? (
885+ < ToolCardHeaderActions className = "file-op-header-actions" >
886+ < IconButton
887+ className = "tool-card-header-action file-op-header-action file-op-confirm-btn"
888+ variant = "success"
889+ size = "xs"
890+ onClick = { handleConfirmClick }
891+ tooltip = { t ( 'toolCards.mcp.confirmExecute' ) }
892+ >
893+ < Check size = { 12 } />
894+ </ IconButton >
895+ < IconButton
896+ className = "tool-card-header-action file-op-header-action file-op-reject-btn"
897+ variant = "danger"
898+ size = "xs"
899+ onClick = { handleRejectClick }
900+ tooltip = { t ( 'toolCards.mcp.cancel' ) }
901+ >
902+ < X size = { 12 } />
903+ </ IconButton >
904+ </ ToolCardHeaderActions >
905+ ) : undefined }
832906 />
833907 }
834908 />
@@ -846,6 +920,7 @@ export const FileOperationToolCard: React.FC<FileOperationToolCardProps> = ({
846920 expandedContent = { expandedContent }
847921 errorContent = { isFailed && isErrorExpanded ? renderErrorContent ( ) : null }
848922 isFailed = { isFailed }
923+ requiresConfirmation = { showConfirmationActions }
849924 headerExpandAffordance = { hasExpandableContent }
850925 headerAffordanceKind = "expand"
851926 />
0 commit comments