From 886be83af5aaaf6acf4b6d9d89b6a3cbd40f7980 Mon Sep 17 00:00:00 2001 From: Developer Date: Wed, 10 Jun 2026 13:09:42 +0000 Subject: [PATCH] fix: enable folder navigation in workspace file browser FilesTab in WorkspaceDetailPage was returning early for directories with no action, making folders unclickable. Changes: - use-workspace-files.ts: add currentPath state and navigateTo() function; refresh() now passes currentPath to listWorkspaceFiles API - WorkspaceDetailPage.tsx FilesTab: handleSelect now calls navigateTo() for directories; added navigateUp() button using '..' when not at root - Clear selected file/editor state when changing directories Quality gates: tsc --noEmit pass, npm run build pass, 82/82 tests pass --- apps/web/src/hooks/use-workspace-files.ts | 14 ++++++++--- apps/web/src/pages/WorkspaceDetailPage.tsx | 28 ++++++++++++++++++++-- 2 files changed, 37 insertions(+), 5 deletions(-) diff --git a/apps/web/src/hooks/use-workspace-files.ts b/apps/web/src/hooks/use-workspace-files.ts index 4ac8073..3201a28 100644 --- a/apps/web/src/hooks/use-workspace-files.ts +++ b/apps/web/src/hooks/use-workspace-files.ts @@ -11,11 +11,13 @@ import { export interface UseWorkspaceFilesResult { entries: FileEntry[]; content: string | null; + currentPath: string; loading: boolean; error: string | null; refresh: () => Promise; loadFile: (path: string) => Promise; saveFile: (path: string, content: string, message?: string) => Promise; + navigateTo: (path: string) => void; } export function useWorkspaceFiles( @@ -23,6 +25,7 @@ export function useWorkspaceFiles( ): UseWorkspaceFilesResult { const [entries, setEntries] = useState([]); const [content, setContent] = useState(null); + const [currentPath, setCurrentPath] = useState(""); const [loading, setLoading] = useState(true); const [error, setError] = useState(null); @@ -30,14 +33,19 @@ export function useWorkspaceFiles( setLoading(true); setError(null); try { - const data = await listWorkspaceFiles(workspaceId); + const data = await listWorkspaceFiles(workspaceId, currentPath); setEntries(data); } catch (err) { setError(err instanceof Error ? err.message : "Failed to load files"); } finally { setLoading(false); } - }, [workspaceId]); + }, [workspaceId, currentPath]); + + const navigateTo = useCallback((path: string) => { + setCurrentPath(path); + setContent(null); + }, []); const loadFile = useCallback( async (path: string) => { @@ -64,5 +72,5 @@ export function useWorkspaceFiles( refresh(); }, [refresh]); - return { entries, content, loading, error, refresh, loadFile, saveFile }; + return { entries, content, currentPath, loading, error, refresh, loadFile, saveFile, navigateTo }; } diff --git a/apps/web/src/pages/WorkspaceDetailPage.tsx b/apps/web/src/pages/WorkspaceDetailPage.tsx index 2d3056d..1ad23e0 100644 --- a/apps/web/src/pages/WorkspaceDetailPage.tsx +++ b/apps/web/src/pages/WorkspaceDetailPage.tsx @@ -150,7 +150,7 @@ function MobileTabBar({ /* ─── Files Tab ─── */ function FilesTab({ workspaceId }: { workspaceId: string }) { - const { entries, content, loadFile, saveFile, loading, error } = + const { entries, content, currentPath, loadFile, saveFile, loading, error, navigateTo } = useWorkspaceFiles(workspaceId); const { status, commit, push, pull, fetch } = useWorkspaceGit(workspaceId); const [selectedPath, setSelectedPath] = useState(null); @@ -159,13 +159,28 @@ function FilesTab({ workspaceId }: { workspaceId: string }) { const [commitMessage, setCommitMessage] = useState(""); const handleSelect = (entry: FileEntry) => { - if (entry.type === "directory") return; + if (entry.type === "directory") { + setSelectedPath(null); + setIsEditing(false); + setEditContent(null); + navigateTo(entry.path); + return; + } setSelectedPath(entry.path); setIsEditing(false); setEditContent(null); loadFile(entry.path); }; + const navigateUp = () => { + if (!currentPath) return; + const parentPath = currentPath.split("/").slice(0, -1).join("/"); + navigateTo(parentPath); + setSelectedPath(null); + setIsEditing(false); + setEditContent(null); + }; + const handleEdit = () => { if (content !== null) { setEditContent(content); @@ -224,6 +239,15 @@ function FilesTab({ workspaceId }: { workspaceId: string }) { )}
+ {currentPath && ( + + )} {loading &&

Loading...

} {error &&

{error}

} {entries.map((entry) => (