@@ -3,7 +3,8 @@ import * as d3 from 'd3';
33import { useApi } from '@/hooks/useApi' ;
44import { api } from '@/lib/api' ;
55import type { KindEntry , NodeResponse , NodesListResponse } from '@/types/api' ;
6- import { ChevronRight , Home } from 'lucide-react' ;
6+ import { ChevronRight , Home , X , FileCode } from 'lucide-react' ;
7+ import { useFileSelection } from '@/contexts/FileSelectionContext' ;
78
89// Reuse the kind-color mapping from ExplorerView for consistency
910const KIND_COLORS : Record < string , string > = {
@@ -231,6 +232,8 @@ export default function CodeGraphView() {
231232 const [ drillTotal , setDrillTotal ] = useState ( 0 ) ;
232233 const [ drillLoading , setDrillLoading ] = useState ( false ) ;
233234
235+ const { selectedPath, selectedType, clearSelection } = useFileSelection ( ) ;
236+
234237 const { data : kindsData , loading : kindsLoading } = useApi ( ( ) => api . getKinds ( ) , [ ] ) ;
235238
236239 // Observe container size
@@ -261,16 +264,34 @@ export default function CodeGraphView() {
261264 setDrillNodes ( [ ] ) ;
262265 setDrillTotal ( 0 ) ;
263266 try {
264- const result : NodesListResponse = await api . getNodesByKind ( kind , 200 , 0 ) ;
265- setDrillNodes ( result . nodes ?? [ ] ) ;
266- setDrillTotal ( result . total ?? result . count ?? ( result . nodes ?? [ ] ) . length ) ;
267+ const result : NodesListResponse = await api . getNodesByKind ( kind , 500 , 0 ) ;
268+ const nodes = result . nodes ?? [ ] ;
269+ // If a file/directory is selected, filter nodes to that path
270+ const filtered = selectedPath
271+ ? nodes . filter ( n => {
272+ if ( ! n . file_path ) return false ;
273+ return selectedType === 'directory'
274+ ? n . file_path . startsWith ( selectedPath )
275+ : n . file_path === selectedPath ;
276+ } )
277+ : nodes ;
278+ setDrillNodes ( filtered ) ;
279+ setDrillTotal ( filtered . length ) ;
267280 } catch {
268281 setDrillNodes ( [ ] ) ;
269282 setDrillTotal ( 0 ) ;
270283 } finally {
271284 setDrillLoading ( false ) ;
272285 }
273- } , [ ] ) ;
286+ } , [ selectedPath , selectedType ] ) ;
287+
288+ // Re-run drill if file selection changes while already drilled into a kind
289+ useEffect ( ( ) => {
290+ if ( selectedKind ) {
291+ handleDrillDown ( selectedKind ) ;
292+ }
293+ // eslint-disable-next-line react-hooks/exhaustive-deps
294+ } , [ selectedPath ] ) ;
274295
275296 const handleDrillUp = useCallback ( ( ) => {
276297 setSelectedKind ( null ) ;
@@ -280,52 +301,81 @@ export default function CodeGraphView() {
280301
281302 const kinds : KindEntry [ ] = kindsData ?. kinds ?? [ ] ;
282303
304+ // Friendly display name for the selected path
305+ const selectedLabel = selectedPath
306+ ? selectedPath . split ( '/' ) . pop ( ) ?? selectedPath
307+ : null ;
308+
283309 return (
284310 < div className = "flex flex-col h-full space-y-3" >
285311 { /* Header + breadcrumb */ }
286- < div className = "flex items-center justify-between" >
312+ < div className = "flex items-center justify-between flex-wrap gap-2 " >
287313 < div >
288314 < h1 className = "text-2xl font-bold gradient-text" > Code Graph</ h1 >
289315 < p className = "text-sm text-surface-400 mt-0.5" >
290316 { selectedKind
291- ? drillTotal > drillNodes . length
292- ? `Showing ${ drillNodes . length } of ${ drillTotal } "${ selectedKind } " nodes`
293- : `${ drillNodes . length } nodes of kind " ${ selectedKind } " `
317+ ? drillTotal > 0
318+ ? `${ drillTotal } "${ selectedKind } " nodes${ selectedPath ? ` in ${ selectedLabel } ` : '' } `
319+ : `No " ${ selectedKind } " nodes${ selectedPath ? ` in ${ selectedLabel } ` : '' } `
294320 : `${ kinds . length } node kinds — click a tile to explore` }
295321 </ p >
296322 </ div >
297323
298- { /* Breadcrumb */ }
299- < nav className = "flex items-center gap-1 text-sm" >
300- < button
301- onClick = { handleDrillUp }
302- className = { `flex items-center gap-1 px-2 py-1 rounded transition-colors ${
303- selectedKind
304- ? 'text-brand-400 hover:text-brand-300 hover:bg-surface-800/50 cursor-pointer'
305- : 'text-surface-500 cursor-default'
306- } `}
307- disabled = { ! selectedKind }
308- >
309- < Home className = "w-3.5 h-3.5" />
310- < span > All Kinds</ span >
311- </ button >
312- { selectedKind && (
313- < >
314- < ChevronRight className = "w-3.5 h-3.5 text-surface-600" />
315- < span
316- className = "px-2 py-1 rounded text-brand-300 font-medium"
317- style = { { color : getKindColor ( selectedKind ) } }
318- >
319- { selectedKind . toUpperCase ( ) }
324+ < div className = "flex items-center gap-2" >
325+ { /* Active file filter badge */ }
326+ { selectedPath && (
327+ < div
328+ className = "flex items-center gap-1.5 px-2 py-1 rounded-md text-xs
329+ bg-surface-800/70 border border-surface-700/50 text-surface-300"
330+ data-testid = "file-filter-badge"
331+ >
332+ < FileCode className = "w-3 h-3 text-brand-400 shrink-0" />
333+ < span className = "truncate max-w-[200px]" title = { selectedPath } >
334+ { selectedLabel }
320335 </ span >
321- </ >
336+ < button
337+ onClick = { clearSelection }
338+ aria-label = "Clear file filter"
339+ className = "text-surface-500 hover:text-surface-300 ml-0.5"
340+ >
341+ < X className = "w-3 h-3" />
342+ </ button >
343+ </ div >
322344 ) }
323- </ nav >
345+
346+ { /* Breadcrumb */ }
347+ < nav className = "flex items-center gap-1 text-sm" aria-label = "Graph breadcrumb" >
348+ < button
349+ onClick = { handleDrillUp }
350+ className = { `flex items-center gap-1 px-2 py-1 rounded transition-colors ${
351+ selectedKind
352+ ? 'text-brand-400 hover:text-brand-300 hover:bg-surface-800/50 cursor-pointer'
353+ : 'text-surface-500 cursor-default'
354+ } `}
355+ disabled = { ! selectedKind }
356+ >
357+ < Home className = "w-3.5 h-3.5" />
358+ < span > All Kinds</ span >
359+ </ button >
360+ { selectedKind && (
361+ < >
362+ < ChevronRight className = "w-3.5 h-3.5 text-surface-600" />
363+ < span
364+ className = "px-2 py-1 rounded text-brand-300 font-medium"
365+ style = { { color : getKindColor ( selectedKind ) } }
366+ >
367+ { selectedKind . toUpperCase ( ) }
368+ </ span >
369+ </ >
370+ ) }
371+ </ nav >
372+ </ div >
324373 </ div >
325374
326375 { /* Treemap container */ }
327376 < div
328377 ref = { containerRef }
378+ data-testid = "graph-container"
329379 className = "flex-1 rounded-xl border border-surface-800/50 bg-surface-900/40 overflow-hidden"
330380 >
331381 { ( kindsLoading || drillLoading ) && (
0 commit comments