This commit is contained in:
@@ -1,5 +1,5 @@
|
|||||||
import { join, sep } from 'node:path'
|
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 { watch, type FSWatcher } from 'chokidar'
|
||||||
import { getName, getRoot, openDialog } from './project'
|
import { getName, getRoot, openDialog } from './project'
|
||||||
import { readAll, readProjectFile, readTree, writeProjectFile } from './fs-service'
|
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)
|
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<boolean> {
|
||||||
|
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 {
|
function startWatcher(): void {
|
||||||
if (watcher) { watcher.close(); watcher = null }
|
if (watcher) { watcher.close(); watcher = null }
|
||||||
const root = getRoot()
|
const root = getRoot()
|
||||||
@@ -52,13 +88,7 @@ function startWatcher(): void {
|
|||||||
function registerIpc(): void {
|
function registerIpc(): void {
|
||||||
ipcMain.handle('project:current', () => ({ root: getRoot(), name: getName() }))
|
ipcMain.handle('project:current', () => ({ root: getRoot(), name: getName() }))
|
||||||
ipcMain.handle('project:open', async (e) => {
|
ipcMain.handle('project:open', async (e) => {
|
||||||
const win = BrowserWindow.fromWebContents(e.sender)
|
await openFolderFlow(BrowserWindow.fromWebContents(e.sender))
|
||||||
const next = await openDialog(win)
|
|
||||||
if (next) {
|
|
||||||
await resolveConfig(getRoot())
|
|
||||||
startWatcher()
|
|
||||||
startConfigWatcher()
|
|
||||||
}
|
|
||||||
return { root: getRoot(), name: getName() }
|
return { root: getRoot(), name: getName() }
|
||||||
})
|
})
|
||||||
|
|
||||||
@@ -134,6 +164,7 @@ function createWindow(): void {
|
|||||||
|
|
||||||
app.whenReady().then(async () => {
|
app.whenReady().then(async () => {
|
||||||
app.setName('Helder')
|
app.setName('Helder')
|
||||||
|
Menu.setApplicationMenu(buildAppMenu())
|
||||||
registerIpc()
|
registerIpc()
|
||||||
await resolveConfig(getRoot())
|
await resolveConfig(getRoot())
|
||||||
startWatcher()
|
startWatcher()
|
||||||
|
|||||||
@@ -375,7 +375,8 @@ export function App(): React.ReactElement {
|
|||||||
</div>
|
</div>
|
||||||
<Splitter onDelta={(dx) => setRightW((w) => clamp(w - dx, 280, 780))} />
|
<Splitter onDelta={(dx) => setRightW((w) => clamp(w - dx, 280, 780))} />
|
||||||
|
|
||||||
<RightColumn width={rightW} />
|
{/* keyed by root so the PTYs respawn in the new cwd when the project switches */}
|
||||||
|
{proj.ready && <RightColumn key={proj.root ?? 'none'} width={rightW} />}
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
{/* status bar */}
|
{/* status bar */}
|
||||||
|
|||||||
@@ -278,6 +278,7 @@ export function Editor({ tabs, active, mode, setMode, onActivate, onClose, onCon
|
|||||||
<div style={{ opacity: 0.5 }}>{Icon.file({ width: 30, height: 30 })}</div>
|
<div style={{ opacity: 0.5 }}>{Icon.file({ width: 30, height: 30 })}</div>
|
||||||
<div className="big">No file open</div>
|
<div className="big">No file open</div>
|
||||||
<div className="klist">
|
<div className="klist">
|
||||||
|
<div><span>Open folder</span><kbd>⌘ O</kbd></div>
|
||||||
<div><span>Search files & content</span><kbd>⌘ F</kbd></div>
|
<div><span>Search files & content</span><kbd>⌘ F</kbd></div>
|
||||||
<div><span>Copy reference</span><kbd>right-click</kbd></div>
|
<div><span>Copy reference</span><kbd>right-click</kbd></div>
|
||||||
<div><span>Pass on to Agent</span><kbd>right-click</kbd></div>
|
<div><span>Pass on to Agent</span><kbd>right-click</kbd></div>
|
||||||
|
|||||||
Reference in New Issue
Block a user