diff --git a/src/main/index.ts b/src/main/index.ts index 64c937b..adcbd09 100644 --- a/src/main/index.ts +++ b/src/main/index.ts @@ -1,5 +1,5 @@ import { join, sep } from 'node:path' -import { app, dialog, shell, BrowserWindow, ipcMain } from 'electron' +import { app, dialog, shell, BrowserWindow, ipcMain, Menu } from 'electron' import { watch, type FSWatcher } from 'chokidar' import { getName, getRoot, openDialog } from './project' import { readAll, readProjectFile, readTree, writeProjectFile } from './fs-service' @@ -34,6 +34,42 @@ function startConfigWatcher(): void { configWatcher.on('add', reload).on('change', reload).on('unlink', reload) } +/** Show the folder picker; if a new folder is chosen, switch the project + * (config + watchers). Returns whether the project changed. */ +async function openFolderFlow(win: BrowserWindow | null): Promise { + const next = await openDialog(win) + if (!next) return false + await resolveConfig(getRoot()) + startWatcher() + startConfigWatcher() + return true +} + +function buildAppMenu(): Menu { + const template: Electron.MenuItemConstructorOptions[] = [ + ...(isMac ? [{ role: 'appMenu' as const }] : []), + { + label: 'File', + submenu: [ + { + label: 'Open Folder…', + accelerator: 'CmdOrCtrl+O', + click: (_m, win) => { + const bw = win instanceof BrowserWindow ? win : BrowserWindow.getFocusedWindow() + openFolderFlow(bw).then((changed) => { if (changed) broadcast('project:changed') }) + }, + }, + { type: 'separator' }, + isMac ? { role: 'close' } : { role: 'quit' }, + ], + }, + { role: 'editMenu' }, + { role: 'viewMenu' }, + { role: 'windowMenu' }, + ] + return Menu.buildFromTemplate(template) +} + function startWatcher(): void { if (watcher) { watcher.close(); watcher = null } const root = getRoot() @@ -52,13 +88,7 @@ function startWatcher(): void { function registerIpc(): void { ipcMain.handle('project:current', () => ({ root: getRoot(), name: getName() })) ipcMain.handle('project:open', async (e) => { - const win = BrowserWindow.fromWebContents(e.sender) - const next = await openDialog(win) - if (next) { - await resolveConfig(getRoot()) - startWatcher() - startConfigWatcher() - } + await openFolderFlow(BrowserWindow.fromWebContents(e.sender)) return { root: getRoot(), name: getName() } }) @@ -134,6 +164,7 @@ function createWindow(): void { app.whenReady().then(async () => { app.setName('Helder') + Menu.setApplicationMenu(buildAppMenu()) registerIpc() await resolveConfig(getRoot()) startWatcher() diff --git a/src/renderer/src/App.tsx b/src/renderer/src/App.tsx index 1f98cdf..700df3a 100644 --- a/src/renderer/src/App.tsx +++ b/src/renderer/src/App.tsx @@ -375,7 +375,8 @@ export function App(): React.ReactElement { setRightW((w) => clamp(w - dx, 280, 780))} /> - + {/* keyed by root so the PTYs respawn in the new cwd when the project switches */} + {proj.ready && } {/* status bar */} diff --git a/src/renderer/src/editor.tsx b/src/renderer/src/editor.tsx index 8adde65..f1e1245 100644 --- a/src/renderer/src/editor.tsx +++ b/src/renderer/src/editor.tsx @@ -278,6 +278,7 @@ export function Editor({ tabs, active, mode, setMode, onActivate, onClose, onCon
{Icon.file({ width: 30, height: 30 })}
No file open
+
Open folder⌘ O
Search files & content⌘ F
Copy referenceright-click
Pass on to Agentright-click