@@ -81,6 +81,8 @@ function getMarginLeft(depth: number) {
8181 return `${ depth * 16 + 4 } px`
8282}
8383
84+ const PREFETCH_INTENT_DELAY_MS = 180
85+
8486interface FileExplorerProps {
8587 currentPath : string | null
8688 githubContents : GitHubFileNode [ ] | undefined
@@ -101,6 +103,7 @@ export function FileExplorer({
101103 const [ sidebarWidth , setSidebarWidth ] = React . useState ( 220 )
102104 const [ isResizing , setIsResizing ] = React . useState ( false )
103105 const MIN_SIDEBAR_WIDTH = 60
106+ const prefetchTimeoutRef = React . useRef < number | null > ( null )
104107
105108 // Initialize expandedFolders with root-level folders
106109 const [ expandedFolders , setExpandedFolders ] = React . useState < Set < string > > (
@@ -190,6 +193,26 @@ export function FileExplorer({
190193 }
191194 } , [ isResizing , sidebarWidth ] )
192195
196+ const clearPrefetchIntent = React . useCallback ( ( ) => {
197+ if ( prefetchTimeoutRef . current !== null ) {
198+ window . clearTimeout ( prefetchTimeoutRef . current )
199+ prefetchTimeoutRef . current = null
200+ }
201+ } , [ ] )
202+
203+ const schedulePrefetchIntent = React . useCallback (
204+ ( path : string ) => {
205+ clearPrefetchIntent ( )
206+ prefetchTimeoutRef . current = window . setTimeout ( ( ) => {
207+ prefetchTimeoutRef . current = null
208+ prefetchFileContent ( path )
209+ } , PREFETCH_INTENT_DELAY_MS )
210+ } ,
211+ [ clearPrefetchIntent , prefetchFileContent ] ,
212+ )
213+
214+ React . useEffect ( ( ) => clearPrefetchIntent , [ clearPrefetchIntent ] )
215+
193216 const toggleFolder = ( path : string ) => {
194217 setExpandedFolders ( ( prev ) => {
195218 const next = new Set ( prev )
@@ -223,6 +246,8 @@ export function FileExplorer({
223246 files = { githubContents }
224247 libraryColor = { libraryColor }
225248 prefetchFileContent = { prefetchFileContent }
249+ schedulePrefetchIntent = { schedulePrefetchIntent }
250+ clearPrefetchIntent = { clearPrefetchIntent }
226251 setCurrentPath = { setCurrentPath }
227252 toggleFolder = { toggleFolder }
228253 />
@@ -246,6 +271,8 @@ const RenderFileTree = (props: {
246271 libraryColor : string
247272 toggleFolder : ( path : string ) => void
248273 prefetchFileContent : ( file : string ) => void
274+ schedulePrefetchIntent : ( file : string ) => void
275+ clearPrefetchIntent : ( ) => void
249276 expandedFolders : Set < string >
250277 currentPath : string | null
251278 setCurrentPath : ( file : string ) => void
@@ -282,15 +309,25 @@ const RenderFileTree = (props: {
282309 < div style = { { paddingLeft : getMarginLeft ( file . depth ) } } >
283310 < button
284311 onClick = { ( ) => {
312+ props . clearPrefetchIntent ( )
285313 if ( file . type === 'dir' ) {
286314 props . toggleFolder ( file . path )
287315 } else {
288316 props . setCurrentPath ( file . path )
289317 }
290318 } }
291- onMouseEnter = { ( ) =>
292- file . type !== 'dir' && props . prefetchFileContent ( file . path )
293- }
319+ onMouseEnter = { ( ) => {
320+ if ( file . type !== 'dir' && props . currentPath !== file . path ) {
321+ props . schedulePrefetchIntent ( file . path )
322+ }
323+ } }
324+ onMouseLeave = { props . clearPrefetchIntent }
325+ onFocus = { ( ) => {
326+ if ( file . type !== 'dir' && props . currentPath !== file . path ) {
327+ props . schedulePrefetchIntent ( file . path )
328+ }
329+ } }
330+ onBlur = { props . clearPrefetchIntent }
294331 className = { twMerge (
295332 `px-2 py-1.5 text-left w-full flex items-center gap-2 text-sm rounded transition-colors duration-200 min-w-0` ,
296333 props . currentPath === file . path
0 commit comments