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
This commit is contained in:
Developer
2026-06-10 13:09:42 +00:00
parent 82091e31a8
commit 886be83af5
2 changed files with 37 additions and 5 deletions
+11 -3
View File
@@ -11,11 +11,13 @@ import {
export interface UseWorkspaceFilesResult {
entries: FileEntry[];
content: string | null;
currentPath: string;
loading: boolean;
error: string | null;
refresh: () => Promise<void>;
loadFile: (path: string) => Promise<void>;
saveFile: (path: string, content: string, message?: string) => Promise<void>;
navigateTo: (path: string) => void;
}
export function useWorkspaceFiles(
@@ -23,6 +25,7 @@ export function useWorkspaceFiles(
): UseWorkspaceFilesResult {
const [entries, setEntries] = useState<FileEntry[]>([]);
const [content, setContent] = useState<string | null>(null);
const [currentPath, setCurrentPath] = useState("");
const [loading, setLoading] = useState(true);
const [error, setError] = useState<string | null>(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 };
}
+26 -2
View File
@@ -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<string | null>(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 }) {
)}
<div className="files-split">
<div className="file-tree">
{currentPath && (
<button
className="tree-entry tree-up"
onClick={navigateUp}
type="button"
>
<Icon name="folder" size="sm" /> ..
</button>
)}
{loading && <p className="muted">Loading...</p>}
{error && <p className="error-text">{error}</p>}
{entries.map((entry) => (