From 8c6d0fb35aac6363f57d349816af588c49f73fbd Mon Sep 17 00:00:00 2001 From: Claude Date: Sun, 26 Oct 2025 05:57:17 +0000 Subject: [PATCH] Fix folder navigation in GitStatus to properly navigate to directories MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit When clicking on untracked folders in GitStatus, the app now correctly navigates to the directory in the file browser instead of showing a 404 error. Changes: - Modified GitStatusResponse to use FileListItem[] instead of string[] for file arrays - Updated gitService.ts to check if paths are files or directories using filesystem stats - Updated GitStatus.tsx to handle directory clicks by navigating to the directory - Added onNavigateToDirectory prop to GitStatus component - Wired up directory navigation in App.tsx to switch to file browser and set current directory - Directories now display with a trailing slash (/) in the UI for clarity 🤖 Generated with [Claude Code](https://claude.com/claude-code) Co-Authored-By: Claude --- client/src/App.tsx | 6 ++++ client/src/components/GitStatus.tsx | 37 ++++++++++++++++------ server/services/gitService.ts | 49 +++++++++++++++++++++++++---- shared/git.ts | 7 +++-- 4 files changed, 81 insertions(+), 18 deletions(-) diff --git a/client/src/App.tsx b/client/src/App.tsx index 1aee99c..60d2110 100644 --- a/client/src/App.tsx +++ b/client/src/App.tsx @@ -436,6 +436,12 @@ function App() { onBackToBrowser={handleBackToBrowser} onOpenGitDiff={openGitDiffPage} onOpenFile={openFilePage} + onNavigateToDirectory={(path) => { + window.history.pushState({ page: "list" }, "", "/"); + setRoute({ page: "list" }); + resetFileViewer(); + setCurrentDirectory(path); + }} /> ) : isGitDiffRoute ? ( diff --git a/client/src/components/GitStatus.tsx b/client/src/components/GitStatus.tsx index 848b899..707e3dc 100644 --- a/client/src/components/GitStatus.tsx +++ b/client/src/components/GitStatus.tsx @@ -6,18 +6,21 @@ import type { StageAllResponse, } from "@shared/git"; import type { ErrorResponse } from "@shared/http"; +import type { FileListItem } from "@shared/files"; import Toolbar from "@/components/Toolbar"; type GitStatusProps = { onBackToBrowser: () => void; onOpenGitDiff: () => void; onOpenFile: (path: string) => void; + onNavigateToDirectory: (path: string) => void; }; const GitStatus = ({ onBackToBrowser, onOpenGitDiff, onOpenFile, + onNavigateToDirectory, }: GitStatusProps) => { const [status, setStatus] = useState(null); const [isLoading, setIsLoading] = useState(true); @@ -157,9 +160,21 @@ const GitStatus = ({ } }; - const renderFileList = (files: string[], title: string, color: string) => { + const renderFileList = ( + files: FileListItem[], + title: string, + color: string, + ) => { if (files.length === 0) return null; + const handleItemClick = (item: FileListItem) => { + if (item.kind === "directory") { + onNavigateToDirectory(item.path); + } else { + onOpenFile(item.path); + } + }; + return (

    - {files.map((file, index) => ( + {files.map((item, index) => (
  • ))} @@ -265,16 +280,20 @@ const GitStatus = ({ Unstaged ({status.unstaged.length})

    - {status.unstaged.map((file, index) => ( + {status.unstaged.map((item, index) => (
  • ))} diff --git a/server/services/gitService.ts b/server/services/gitService.ts index fb2ff11..87612a8 100644 --- a/server/services/gitService.ts +++ b/server/services/gitService.ts @@ -1,10 +1,36 @@ import type { GitStatusResponse, GitDiffResponse } from "../../shared/git"; +import type { FileListItem } from "../../shared/files"; import { exec } from "child_process"; import { promisify } from "util"; import { resolveFromRoot } from "../utils/paths"; +import { stat } from "fs/promises"; +import { join } from "path"; const execAsync = promisify(exec); +/** + * Helper function to convert a file path to a FileListItem by checking if it's a file or directory + */ +const pathToFileListItem = async ( + filePath: string, + projectRoot: string, +): Promise => { + try { + const fullPath = join(projectRoot, filePath); + const stats = await stat(fullPath); + return { + path: filePath, + kind: stats.isDirectory() ? "directory" : "file", + }; + } catch { + // If we can't stat the file, assume it's a file + return { + path: filePath, + kind: "file", + }; + } +}; + /** * Gets the current git status including branch, ahead/behind, and file statuses */ @@ -39,9 +65,9 @@ export const getGitStatus = async (): Promise => { cwd: projectRoot, }); - const staged: string[] = []; - const unstaged: string[] = []; - const untracked: string[] = []; + const stagedPaths: string[] = []; + const unstagedPaths: string[] = []; + const untrackedPaths: string[] = []; const lines = statusOutput.split("\n").filter((line) => line.length > 0); for (const line of lines) { @@ -53,17 +79,28 @@ export const getGitStatus = async (): Promise => { const unstagedStatus = status[1]; if (stagedStatus === "?" && unstagedStatus === "?") { - untracked.push(filePath); + untrackedPaths.push(filePath); } else { if (stagedStatus !== " " && stagedStatus !== "?") { - staged.push(filePath); + stagedPaths.push(filePath); } if (unstagedStatus !== " " && unstagedStatus !== "?") { - unstaged.push(filePath); + unstagedPaths.push(filePath); } } } + // Convert paths to FileListItems with kind information + const staged = await Promise.all( + stagedPaths.map((path) => pathToFileListItem(path, projectRoot)), + ); + const unstaged = await Promise.all( + unstagedPaths.map((path) => pathToFileListItem(path, projectRoot)), + ); + const untracked = await Promise.all( + untrackedPaths.map((path) => pathToFileListItem(path, projectRoot)), + ); + return { branch, ahead, diff --git a/shared/git.ts b/shared/git.ts index 6b53116..6eb1ac0 100644 --- a/shared/git.ts +++ b/shared/git.ts @@ -1,12 +1,13 @@ import type { SuccessResponse, TextRequest } from "./http"; +import type { FileListItem } from "./files"; export interface GitStatusResponse { branch: string; ahead: number; behind: number; - staged: string[]; - unstaged: string[]; - untracked: string[]; + staged: FileListItem[]; + unstaged: FileListItem[]; + untracked: FileListItem[]; } export interface GitDiffResponse {