@@ -156,14 +156,15 @@ export const FileTree: React.FC<FileTreeProps> = ({
156156 } , [ annotationCountMap ] ) ;
157157
158158 const tree = useMemo ( ( ) => buildFileTree ( files ) , [ files ] ) ;
159+ const allFolderPaths = useMemo ( ( ) => getAllFolderPaths ( tree ) , [ tree ] ) ;
159160
160- const [ expandedFolders , setExpandedFolders ] = useState < Set < string > > ( ( ) => new Set ( getAllFolderPaths ( tree ) ) ) ;
161+ const [ expandedFolders , setExpandedFolders ] = useState < Set < string > > ( ( ) => new Set ( allFolderPaths ) ) ;
161162 const [ prevTree , setPrevTree ] = useState ( tree ) ;
162163
163164 // Expand all folders when tree changes (initial render + diff switch)
164165 if ( tree !== prevTree ) {
165166 setPrevTree ( tree ) ;
166- setExpandedFolders ( new Set ( getAllFolderPaths ( tree ) ) ) ;
167+ setExpandedFolders ( new Set ( allFolderPaths ) ) ;
167168 }
168169
169170 // Auto-expand ancestors of the active file so j/k nav always reveals the target
@@ -192,6 +193,12 @@ export const FileTree: React.FC<FileTreeProps> = ({
192193 } ) ;
193194 } , [ ] ) ;
194195
196+ const areAllFoldersExpanded = allFolderPaths . length > 0 && allFolderPaths . every ( path => expandedFolders . has ( path ) ) ;
197+
198+ const handleToggleAllFolders = useCallback ( ( ) => {
199+ setExpandedFolders ( areAllFoldersExpanded ? new Set ( ) : new Set ( allFolderPaths ) ) ;
200+ } , [ allFolderPaths , areAllFoldersExpanded ] ) ;
201+
195202 return (
196203 < aside className = "border-r border-border/50 bg-card/30 flex flex-col flex-shrink-0 overflow-hidden" style = { { width : width ?? 256 } } >
197204 { /* Header */ }
@@ -224,24 +231,22 @@ export const FileTree: React.FC<FileTreeProps> = ({
224231 </ button >
225232 ) }
226233 < button
227- onClick = { ( ) => setExpandedFolders ( new Set ( getAllFolderPaths ( tree ) ) ) }
228- className = "p-1 rounded transition-colors hover:bg-muted text-muted-foreground"
229- title = "Expand all folders"
230- >
231- < svg className = "w-3.5 h-3.5" fill = "none" viewBox = "0 0 24 24" stroke = "currentColor" strokeWidth = { 2 } >
232- < path strokeLinecap = "round" strokeLinejoin = "round" d = "M5 8l7-6 7 6" />
233- < path strokeLinecap = "round" strokeLinejoin = "round" d = "M5 16l7 6 7-6" />
234- </ svg >
235- </ button >
236- < button
237- onClick = { ( ) => setExpandedFolders ( new Set ( ) ) }
238- className = "p-1 rounded transition-colors hover:bg-muted text-muted-foreground"
239- title = "Collapse all folders"
234+ onClick = { handleToggleAllFolders }
235+ disabled = { allFolderPaths . length === 0 }
236+ className = "p-1 rounded transition-colors hover:bg-muted text-muted-foreground disabled:opacity-50 disabled:cursor-not-allowed"
237+ title = { areAllFoldersExpanded ? 'Collapse all folders' : 'Expand all folders' }
240238 >
241- < svg className = "w-3.5 h-3.5" fill = "none" viewBox = "0 0 24 24" stroke = "currentColor" strokeWidth = { 2 } >
242- < path strokeLinecap = "round" strokeLinejoin = "round" d = "M5 2l7 6 7-6" />
243- < path strokeLinecap = "round" strokeLinejoin = "round" d = "M5 22l7-6 7 6" />
244- </ svg >
239+ { areAllFoldersExpanded ? (
240+ < svg className = "w-3.5 h-3.5" fill = "none" viewBox = "0 0 24 24" stroke = "currentColor" strokeWidth = { 2 } >
241+ < path strokeLinecap = "round" strokeLinejoin = "round" d = "M5 2l7 6 7-6" />
242+ < path strokeLinecap = "round" strokeLinejoin = "round" d = "M5 22l7-6 7 6" />
243+ </ svg >
244+ ) : (
245+ < svg className = "w-3.5 h-3.5" fill = "none" viewBox = "0 0 24 24" stroke = "currentColor" strokeWidth = { 2 } >
246+ < path strokeLinecap = "round" strokeLinejoin = "round" d = "M5 8l7-6 7 6" />
247+ < path strokeLinecap = "round" strokeLinejoin = "round" d = "M5 16l7 6 7-6" />
248+ </ svg >
249+ ) }
245250 </ button >
246251 { onToggleHideViewed && (
247252 < button
0 commit comments