From ff83cc99ab8887c069de3ae3ac629abcb71e8df9 Mon Sep 17 00:00:00 2001 From: Jiayuan Date: Wed, 14 Jan 2026 03:35:43 +0800 Subject: [PATCH] feat: add directory picker for new session dialog - Add Browse button to open native directory picker - Add selectDirectory IPC handler using dialog.showOpenDialog - User can still type path manually or use system picker Co-Authored-By: Claude Opus 4.5 --- src/main/ipc/handlers.ts | 17 ++++++++++++++++- src/preload/index.ts | 3 +++ src/renderer/src/App.tsx | 34 ++++++++++++++++++++++------------ src/shared/electron-api.d.ts | 3 +++ src/shared/ipc-channels.ts | 3 +++ 5 files changed, 47 insertions(+), 13 deletions(-) diff --git a/src/main/ipc/handlers.ts b/src/main/ipc/handlers.ts index ab262bfbf0..839a4181e3 100644 --- a/src/main/ipc/handlers.ts +++ b/src/main/ipc/handlers.ts @@ -2,7 +2,7 @@ * IPC handlers for main process * Registers all IPC handlers for communication with renderer process */ -import { ipcMain } from 'electron' +import { ipcMain, dialog } from 'electron' import { IPC_CHANNELS } from '../../shared/ipc-channels' import { DEFAULT_AGENTS } from '../config/defaults' import type { Conductor } from '../conductor/Conductor' @@ -107,5 +107,20 @@ export function registerIPCHandlers(conductor: Conductor): void { return config }) + // --- Dialog handlers --- + + ipcMain.handle(IPC_CHANNELS.DIALOG_SELECT_DIRECTORY, async () => { + const result = await dialog.showOpenDialog({ + properties: ['openDirectory', 'createDirectory'], + title: 'Select Working Directory', + }) + + if (result.canceled || result.filePaths.length === 0) { + return null + } + + return result.filePaths[0] + }) + console.log('[IPC] All handlers registered') } diff --git a/src/preload/index.ts b/src/preload/index.ts index 9316c0ecb5..c963e2124b 100644 --- a/src/preload/index.ts +++ b/src/preload/index.ts @@ -41,6 +41,9 @@ const electronAPI: ElectronAPI = { updateConfig: (config) => ipcRenderer.invoke(IPC_CHANNELS.CONFIG_UPDATE, config), + // Dialog + selectDirectory: () => ipcRenderer.invoke(IPC_CHANNELS.DIALOG_SELECT_DIRECTORY), + // Event listeners onAgentMessage: (callback) => { const listener = (_event: Electron.IpcRendererEvent, message: unknown) => diff --git a/src/renderer/src/App.tsx b/src/renderer/src/App.tsx index 1b4de504bc..e9b11a4cca 100644 --- a/src/renderer/src/App.tsx +++ b/src/renderer/src/App.tsx @@ -130,18 +130,28 @@ function App(): React.JSX.Element { - setNewSessionCwd(e.target.value)} - placeholder="/path/to/project" - className="mb-4 w-full rounded-lg border border-[var(--color-border)] bg-[var(--color-background)] px-4 py-2 text-[var(--color-text)] outline-none placeholder:text-[var(--color-text-muted)] focus:border-[var(--color-primary)]" - autoFocus - onKeyDown={(e) => { - if (e.key === 'Enter') handleCreateSession() - if (e.key === 'Escape') setShowNewSession(false) - }} - /> +
+ setNewSessionCwd(e.target.value)} + placeholder="Select a directory..." + className="flex-1 rounded-lg border border-[var(--color-border)] bg-[var(--color-background)] px-4 py-2 text-[var(--color-text)] outline-none placeholder:text-[var(--color-text-muted)] focus:border-[var(--color-primary)]" + onKeyDown={(e) => { + if (e.key === 'Enter') handleCreateSession() + if (e.key === 'Escape') setShowNewSession(false) + }} + /> + +