This commit is contained in:
@@ -1,5 +1,6 @@
|
|||||||
import { join, resolve, sep } from 'node:path'
|
import { join, resolve, sep } from 'node:path'
|
||||||
import { readFileSync, statSync } from 'node:fs'
|
import { readFileSync, statSync } from 'node:fs'
|
||||||
|
import { spawn } from 'node:child_process'
|
||||||
import { app, dialog, shell, BrowserWindow, ipcMain, Menu } from 'electron'
|
import { app, dialog, shell, BrowserWindow, ipcMain, Menu } from 'electron'
|
||||||
import { watch, type FSWatcher } from 'chokidar'
|
import { watch, type FSWatcher } from 'chokidar'
|
||||||
import { addRecentProject, getName, getRecentProjects, getRoot, openDialog, setRoot } from './project'
|
import { addRecentProject, getName, getRecentProjects, getRoot, openDialog, setRoot } from './project'
|
||||||
@@ -27,6 +28,32 @@ function broadcast(channel: string): void {
|
|||||||
for (const w of BrowserWindow.getAllWindows()) w.webContents.send(channel)
|
for (const w of BrowserWindow.getAllWindows()) w.webContents.send(channel)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Each Helder window is one project, and one project owns global main-process
|
||||||
|
* state (root + FS/git/config watchers + PTYs). To run several projects beside
|
||||||
|
* each other we therefore spawn a *separate, detached* Helder process per window
|
||||||
|
* rather than opening a second BrowserWindow in this process.
|
||||||
|
*
|
||||||
|
* Spawning `process.execPath` directly also sidesteps macOS LaunchServices, which
|
||||||
|
* otherwise just re-activates the running instance when the bundle is launched
|
||||||
|
* again from Finder/Dock. `HELDER_PROJECT` points the child at a project (the
|
||||||
|
* child's `resolveInitialRoot` reads it); with no path it lands on the launcher.
|
||||||
|
*/
|
||||||
|
function spawnInstance(projectPath?: string): void {
|
||||||
|
const env = { ...process.env }
|
||||||
|
if (projectPath && safeIsDir(projectPath)) env.HELDER_PROJECT = resolve(projectPath)
|
||||||
|
else delete env.HELDER_PROJECT
|
||||||
|
// Packaged: execPath IS the app, no app path needed. Dev: replay our own argv
|
||||||
|
// (the electron-vite main entry) so the child loads the same app.
|
||||||
|
const args = app.isPackaged ? [] : process.argv.slice(1)
|
||||||
|
const child = spawn(process.execPath, args, { detached: true, stdio: 'ignore', env })
|
||||||
|
child.unref()
|
||||||
|
}
|
||||||
|
|
||||||
|
function safeIsDir(p: string): boolean {
|
||||||
|
try { return statSync(p).isDirectory() } catch { return false }
|
||||||
|
}
|
||||||
|
|
||||||
function startConfigWatcher(): void {
|
function startConfigWatcher(): void {
|
||||||
if (configWatcher) { configWatcher.close(); configWatcher = null }
|
if (configWatcher) { configWatcher.close(); configWatcher = null }
|
||||||
const root = getRoot()
|
const root = getRoot()
|
||||||
@@ -96,6 +123,11 @@ function buildAppMenu(): Menu {
|
|||||||
{
|
{
|
||||||
label: 'File',
|
label: 'File',
|
||||||
submenu: [
|
submenu: [
|
||||||
|
{
|
||||||
|
label: 'New Window',
|
||||||
|
accelerator: 'CmdOrCtrl+Shift+N',
|
||||||
|
click: () => spawnInstance(),
|
||||||
|
},
|
||||||
{
|
{
|
||||||
label: 'Open Folder…',
|
label: 'Open Folder…',
|
||||||
accelerator: 'CmdOrCtrl+O',
|
accelerator: 'CmdOrCtrl+O',
|
||||||
|
|||||||
Reference in New Issue
Block a user