1- import { memo } from "react"
2- import { FileCode } from "lucide-react"
1+ import { memo , useState , useEffect , useCallback } from "react"
2+ import { CheckCircle } from "lucide-react"
33
44import { useAppTranslation } from "@src/i18n/TranslationContext"
55import { ToolUseBlock , ToolUseBlockHeader } from "../common/ToolUseBlock"
6+ import { Checkbox } from "../ui/checkbox"
7+ import { Button } from "../ui/button"
68import { vscode } from "@src/utils/vscode"
79import { removeLeadingNonAlphanumeric } from "@src/utils/removeLeadingNonAlphanumeric"
810
@@ -22,40 +24,59 @@ interface BatchFilePermissionProps {
2224
2325export const BatchFilePermission = memo ( ( { files = [ ] , onPermissionResponse, ts } : BatchFilePermissionProps ) => {
2426 const { t } = useAppTranslation ( )
27+ const [ individualPermissions , setIndividualPermissions ] = useState < { [ key : string ] : boolean } > ( { } )
28+ const [ isSubmitting , setIsSubmitting ] = useState ( false )
29+
30+ // Initialize permissions as true when files change
31+ useEffect ( ( ) => {
32+ const initialPermissions : { [ key : string ] : boolean } = { }
33+ files . forEach ( ( file ) => {
34+ initialPermissions [ file . path ] = true
35+ } )
36+ setIndividualPermissions ( initialPermissions )
37+ setIsSubmitting ( false )
38+ } , [ files , ts ] )
39+
40+ const handleIndividualPermission = useCallback ( ( filePath : string , checked : boolean ) => {
41+ setIndividualPermissions ( ( prev ) => ( {
42+ ...prev ,
43+ [ filePath ] : checked ,
44+ } ) )
45+ } , [ ] )
46+
47+ const handleSubmitIndividual = useCallback ( ( ) => {
48+ setIsSubmitting ( true )
49+ const response : { [ key : string ] : boolean } = { }
50+ files . forEach ( ( file ) => {
51+ response [ file . key ] = individualPermissions [ file . path ] ?? true
52+ } )
53+ onPermissionResponse ?.( response )
54+ } , [ files , individualPermissions , onPermissionResponse ] )
2555
2656 // Don't render if there are no files or no response handler
2757 if ( ! files ?. length || ! onPermissionResponse ) {
2858 return null
2959 }
3060
31- const headerStyle = {
32- display : "flex" ,
33- alignItems : "center" ,
34- fontSize : "13px" ,
35- marginBottom : 5 ,
36- gap : 8 ,
37- opacity : 0.8 ,
38- }
39-
40- const toolIcon = ( type : string ) => {
41- const size = 15
42- const color = "var(--vscode-foreground)"
43- switch ( type ) {
44- case "file-code" :
45- return < FileCode size = { size } color = { color } />
46- default :
47- return null
48- }
49- }
61+ // Check if all files are checked or all are unchecked
62+ const checkedCount = Object . values ( individualPermissions ) . filter ( ( v ) => v === true ) . length
63+ const allChecked = checkedCount === files . length
64+ const allUnchecked = checkedCount === 0
65+ const showSubmitButton = ! allChecked && ! allUnchecked
5066
5167 return (
5268 < div style = { { paddingTop : 5 } } >
5369 { /* Individual files */ }
54- < div style = { { display : "flex" , flexDirection : "column" , gap : 20 } } >
55- { files . map ( ( file , index ) => {
70+ < div style = { { display : "flex" , flexDirection : "column" , gap : 0 } } >
71+ { files . map ( ( file ) => {
5672 return (
57- < div key = { `${ file . path } -${ ts } ` } >
58- < ToolUseBlock >
73+ < div key = { `${ file . path } -${ ts } ` } style = { { display : "flex" , alignItems : "center" , gap : 8 } } >
74+ < Checkbox
75+ checked = { individualPermissions [ file . path ] !== false }
76+ onCheckedChange = { ( checked ) => handleIndividualPermission ( file . path , checked as boolean ) }
77+ variant = "description"
78+ />
79+ < ToolUseBlock style = { { flex : 1 } } >
5980 < ToolUseBlockHeader
6081 onClick = { ( ) => vscode . postMessage ( { type : "openFile" , text : file . content } ) } >
6182 { file . path ?. startsWith ( "." ) && < span > .</ span > }
@@ -75,6 +96,20 @@ export const BatchFilePermission = memo(({ files = [], onPermissionResponse, ts
7596 } ) }
7697 </ div >
7798
99+ { /* Submit button - only show when there's a mix of checked/unchecked */ }
100+ { showSubmitButton && (
101+ < div style = { { marginTop : 12 } } >
102+ < Button
103+ variant = "default"
104+ size = "default"
105+ style = { { width : "100%" } }
106+ onClick = { handleSubmitIndividual }
107+ disabled = { isSubmitting } >
108+ < CheckCircle className = "w-4 h-4" />
109+ { t ( "chat:batchFilePermission.approveSelected" ) }
110+ </ Button >
111+ </ div >
112+ ) }
78113 </ div >
79114 )
80115} )
0 commit comments