diff --git a/apps/desktop/package.json b/apps/desktop/package.json index e8e015f..76b0d38 100644 --- a/apps/desktop/package.json +++ b/apps/desktop/package.json @@ -17,6 +17,9 @@ "fix": "biome check --write", "typecheck": "tsc --noEmit" }, + "dependencies": { + "tree-kill": "catalog:" + }, "devDependencies": { "@furtherverse/tsconfig": "workspace:*", "@tailwindcss/vite": "catalog:", diff --git a/apps/desktop/src/main/index.ts b/apps/desktop/src/main/index.ts index 0e8a056..2f03156 100644 --- a/apps/desktop/src/main/index.ts +++ b/apps/desktop/src/main/index.ts @@ -2,11 +2,13 @@ import { spawn } from 'node:child_process' import { createServer } from 'node:net' import { join } from 'node:path' import { app, BrowserWindow, shell } from 'electron' +import killProcessTree from 'tree-kill' const DEV_SERVER_URL = 'http://localhost:3000' let mainWindow: BrowserWindow | null = null let serverProcess: ReturnType | null = null +let isQuitting = false const getAvailablePort = (): Promise => new Promise((resolve, reject) => { @@ -37,7 +39,7 @@ const waitForServer = async ( timeoutMs = 15_000, ): Promise => { const start = Date.now() - while (Date.now() - start < timeoutMs) { + while (Date.now() - start < timeoutMs && !isQuitting) { if (await isServerReady(url)) return true await new Promise((resolve) => setTimeout(resolve, 200)) } @@ -56,7 +58,11 @@ const stopServerProcess = () => { return } - runningServer.kill() + killProcessTree(runningServer.pid, (error?: Error) => { + if (error) { + console.error('Failed to stop server process:', error) + } + }) } const spawnServer = (port: number): string => { @@ -84,11 +90,17 @@ const spawnServer = (port: number): string => { return `http://127.0.0.1:${port}` } -const getServerUrl = async (): Promise => { +const getServerUrl = async (): Promise => { if (!app.isPackaged) { return DEV_SERVER_URL } + const port = await getAvailablePort() + + if (isQuitting) { + return null + } + return spawnServer(port) } @@ -120,9 +132,18 @@ const createWindow = async () => { mainWindow.show() const serverUrl = await getServerUrl() + if (!serverUrl || isQuitting || !mainWindow || mainWindow.isDestroyed()) { + stopServerProcess() + return + } console.log(`Waiting for server at ${serverUrl}...`) const ready = await waitForServer(serverUrl) + if (isQuitting || !mainWindow || mainWindow.isDestroyed()) { + stopServerProcess() + return + } + if (!ready) { console.error( app.isPackaged @@ -144,21 +165,19 @@ app.whenReady().then(createWindow) app.on('window-all-closed', () => { if (process.platform !== 'darwin') { + isQuitting = true stopServerProcess() - if (process.platform === 'win32') { - app.exit(0) - return - } app.quit() } }) app.on('activate', () => { - if (BrowserWindow.getAllWindows().length === 0) { + if (!isQuitting && BrowserWindow.getAllWindows().length === 0) { createWindow() } }) app.on('before-quit', () => { + isQuitting = true stopServerProcess() }) diff --git a/bun.lock b/bun.lock index 8979e46..6b0253c 100644 --- a/bun.lock +++ b/bun.lock @@ -13,6 +13,9 @@ "apps/desktop": { "name": "@furtherverse/desktop", "version": "1.0.0", + "dependencies": { + "tree-kill": "catalog:", + }, "devDependencies": { "@furtherverse/tsconfig": "workspace:*", "@tailwindcss/vite": "catalog:", @@ -118,6 +121,7 @@ "react-dom": "^19.2.4", "systeminformation": "^5.30.7", "tailwindcss": "^4.1.18", + "tree-kill": "^1.2.2", "turbo": "^2.7.5", "typescript": "^5.9.3", "uuid": "^13.0.0", @@ -1244,6 +1248,8 @@ "to-regex-range": ["to-regex-range@5.0.1", "", { "dependencies": { "is-number": "^7.0.0" } }, "sha512-65P7iz6X5yEr1cwcgvQxbbIw7Uk3gOy5dIdtZ4rDveLqhrdJP+Li/Hx6tyK0NEb+2GCyneCMJiGqrADCSNk8sQ=="], + "tree-kill": ["tree-kill@1.2.2", "", { "bin": { "tree-kill": "cli.js" } }, "sha512-L0Orpi8qGpRG//Nd+H90vFB+3iHnue1zSSGmNOOCh1GLJ7rUKVwV2HvijphGQS2UmhUZewS9VgvxYIdgr+fG1A=="], + "truncate-utf8-bytes": ["truncate-utf8-bytes@1.0.2", "", { "dependencies": { "utf8-byte-length": "^1.0.1" } }, "sha512-95Pu1QXQvruGEhv62XCMO3Mm90GscOCClvrIUwCM0PYOXK3kaF3l3sIHxx71ThJfcbM2O5Au6SO3AWCSEfW4mQ=="], "tsconfck": ["tsconfck@3.1.6", "", { "peerDependencies": { "typescript": "^5.0.0" }, "optionalPeers": ["typescript"], "bin": { "tsconfck": "bin/tsconfck.js" } }, "sha512-ks6Vjr/jEw0P1gmOVwutM3B7fWxoWBL2KRDb1JfqGVawBmO5UsvmWOQFGHBPl5yxYz4eERr19E6L7NMv+Fej4w=="], diff --git a/package.json b/package.json index 073073f..dab56d8 100644 --- a/package.json +++ b/package.json @@ -61,6 +61,7 @@ "react-dom": "^19.2.4", "systeminformation": "^5.30.7", "tailwindcss": "^4.1.18", + "tree-kill": "^1.2.2", "turbo": "^2.7.5", "typescript": "^5.9.3", "uuid": "^13.0.0",