Skip to content

Commit 85e4a87

Browse files
authored
feat: detect workspace folder (#105)
1 parent 8ba4612 commit 85e4a87

File tree

4 files changed

+68
-1
lines changed

4 files changed

+68
-1
lines changed

src/main/preload.ts

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -59,6 +59,8 @@ contextBridge.exposeInMainWorld("electronAPI", {
5959
ipcRenderer.invoke("select-directory"),
6060
searchDirectories: (query: string, searchRoot?: string): Promise<string[]> =>
6161
ipcRenderer.invoke("search-directories", query, searchRoot),
62+
findReposDirectory: (): Promise<string | null> =>
63+
ipcRenderer.invoke("find-repos-directory"),
6264
validateRepo: (directoryPath: string): Promise<boolean> =>
6365
ipcRenderer.invoke("validate-repo", directoryPath),
6466
checkWriteAccess: (directoryPath: string): Promise<boolean> =>

src/main/services/git.ts

Lines changed: 42 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,7 @@
11
import { type ChildProcess, exec } from "node:child_process";
22
import fs from "node:fs";
3+
import os from "node:os";
4+
import path from "node:path";
35
import { promisify } from "node:util";
46
import { type BrowserWindow, type IpcMainInvokeEvent, ipcMain } from "electron";
57

@@ -87,6 +89,42 @@ const getCurrentBranch = async (
8789
}
8890
};
8991

92+
export const findReposDirectory = async (): Promise<string | null> => {
93+
const platform = os.platform();
94+
95+
if (platform === "win32") {
96+
return null;
97+
}
98+
99+
const homeDir = os.homedir();
100+
101+
const excludedPaths = [
102+
"Library",
103+
"Applications",
104+
".Trash",
105+
"Music",
106+
"Movies",
107+
"Pictures",
108+
"Desktop",
109+
"Downloads",
110+
];
111+
112+
const excludeArgs = excludedPaths
113+
.map((p) => `-path "${path.join(homeDir, p)}"`)
114+
.join(" -o ");
115+
116+
const command = `find "${homeDir}" -maxdepth 4 \\( ${excludeArgs} \\) -prune -o -type d -name .git -print 2>/dev/null | sed 's|/.git$||' | xargs -n1 dirname 2>/dev/null | sort | uniq -c | sort -rn | head -1 | awk '$1 >= 3 {print $2}'`;
117+
118+
try {
119+
const { stdout } = await execAsync(command);
120+
const result = stdout.trim();
121+
122+
return result || null;
123+
} catch {
124+
return null;
125+
}
126+
};
127+
90128
export const detectSSHError = (output: string): string | undefined => {
91129
if (
92130
output.includes("successfully authenticated") ||
@@ -113,6 +151,10 @@ export const detectSSHError = (output: string): string | undefined => {
113151
export function registerGitIpc(
114152
getMainWindow: () => BrowserWindow | null,
115153
): void {
154+
ipcMain.handle("find-repos-directory", async (): Promise<string | null> => {
155+
return findReposDirectory();
156+
});
157+
116158
ipcMain.handle(
117159
"validate-repo",
118160
async (

src/renderer/features/auth/components/AuthScreen.tsx

Lines changed: 23 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -14,7 +14,7 @@ import {
1414
} from "@radix-ui/themes";
1515
import type { CloudRegion } from "@shared/types/oauth";
1616
import { useMutation } from "@tanstack/react-query";
17-
import { useState } from "react";
17+
import { useEffect, useState } from "react";
1818
import { IS_DEV } from "@/constants/environment";
1919

2020
export const getErrorMessage = (error: unknown) => {
@@ -33,13 +33,35 @@ export const getErrorMessage = (error: unknown) => {
3333

3434
return message;
3535
};
36+
37+
const detectWorkspacePath = async () => {
38+
try {
39+
const detectedPath = await window.electronAPI.findReposDirectory();
40+
if (detectedPath) {
41+
return detectedPath;
42+
}
43+
} catch (error) {
44+
console.error("Failed to detect repos directory:", error);
45+
}
46+
47+
return null;
48+
};
49+
3650
export function AuthScreen() {
3751
const [region, setRegion] = useState<CloudRegion>("us");
3852
const [workspace, setWorkspace] = useState("~/workspace");
3953
const [workspaceError, setWorkspaceError] = useState<string | null>(null);
4054

4155
const { loginWithOAuth, setDefaultWorkspace } = useAuthStore();
4256

57+
useEffect(() => {
58+
detectWorkspacePath().then((path) => {
59+
if (path) {
60+
setWorkspace(path);
61+
}
62+
});
63+
}, []);
64+
4365
const authMutation = useMutation({
4466
mutationFn: async ({
4567
selectedRegion,

src/renderer/types/electron.d.ts

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -27,6 +27,7 @@ export interface IElectronAPI {
2727
) => Promise<{ success: boolean; data?: OAuthTokenResponse; error?: string }>;
2828
selectDirectory: () => Promise<string | null>;
2929
searchDirectories: (query: string, searchRoot?: string) => Promise<string[]>;
30+
findReposDirectory: () => Promise<string | null>;
3031
validateRepo: (directoryPath: string) => Promise<boolean>;
3132
checkWriteAccess: (directoryPath: string) => Promise<boolean>;
3233
detectRepo: (directoryPath: string) => Promise<{

0 commit comments

Comments
 (0)