diff --git a/packages/app/src/components/file-explorer-pane.tsx b/packages/app/src/components/file-explorer-pane.tsx index 5d952e135..9575eb891 100644 --- a/packages/app/src/components/file-explorer-pane.tsx +++ b/packages/app/src/components/file-explorer-pane.tsx @@ -167,23 +167,32 @@ export function FileExplorerPane({ if (hasInitializedRef.current) { return; } + // Mark initialized eagerly so concurrent effect re-runs don't double-fetch. + // If the root listing fails (e.g. client not yet connected), we reset the + // flag so the next time requestDirectoryListing is recreated (when client + // becomes available) this effect retries automatically. hasInitializedRef.current = true; void requestDirectoryListing(".", { recordHistory: false, setCurrentPath: false, - }); - const persistedPaths = - usePanelStore.getState().expandedPathsByWorkspace[workspaceStateKey ?? ""]; - if (persistedPaths) { - for (const path of persistedPaths) { - if (path !== ".") { - void requestDirectoryListing(path, { - recordHistory: false, - setCurrentPath: false, - }); + }).then((succeeded) => { + if (!succeeded) { + hasInitializedRef.current = false; + return; + } + const persistedPaths = + usePanelStore.getState().expandedPathsByWorkspace[workspaceStateKey ?? ""]; + if (persistedPaths) { + for (const path of persistedPaths) { + if (path !== ".") { + void requestDirectoryListing(path, { + recordHistory: false, + setCurrentPath: false, + }); + } } } - } + }); }, [hasWorkspaceScope, requestDirectoryListing, workspaceStateKey]); // Expand ancestor directories when a file is selected (e.g., from an inline path click) diff --git a/packages/app/src/hooks/use-file-explorer-actions.ts b/packages/app/src/hooks/use-file-explorer-actions.ts index 766b1f978..f23c4056e 100644 --- a/packages/app/src/hooks/use-file-explorer-actions.ts +++ b/packages/app/src/hooks/use-file-explorer-actions.ts @@ -82,9 +82,12 @@ export function useFileExplorerActions(params: { serverId: string } & FileExplor ); const requestDirectoryListing = useCallback( - async (path: string, options?: { recordHistory?: boolean; setCurrentPath?: boolean }) => { + async ( + path: string, + options?: { recordHistory?: boolean; setCurrentPath?: boolean }, + ): Promise => { if (!workspaceStateKey) { - return; + return false; } const normalizedPath = path && path.length > 0 ? path : "."; const shouldSetCurrentPath = options?.setCurrentPath ?? true; @@ -113,7 +116,7 @@ export function useFileExplorerActions(params: { serverId: string } & FileExplor lastError: "Workspace is unavailable", pendingRequest: null, })); - return; + return false; } if (!client) { @@ -123,7 +126,7 @@ export function useFileExplorerActions(params: { serverId: string } & FileExplor lastError: "Host is not connected", pendingRequest: null, })); - return; + return false; } try { @@ -150,6 +153,7 @@ export function useFileExplorerActions(params: { serverId: string } & FileExplor return nextState; }); + return true; } catch (error) { updateExplorerState((state) => ({ ...state, @@ -157,6 +161,7 @@ export function useFileExplorerActions(params: { serverId: string } & FileExplor lastError: error instanceof Error ? error.message : "Failed to list directory", pendingRequest: null, })); + return false; } }, [client, normalizedWorkspaceRoot, updateExplorerState, workspaceStateKey],