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 {