From 0d84793a4807c04d57bec1cd0f31f9b858591e39 Mon Sep 17 00:00:00 2001 From: ssdfasd <2156608475@qq.com> Date: Fri, 13 Mar 2026 00:27:36 +0800 Subject: [PATCH] Add electron packaging for single exe server distribution --- web/electron/main.js | 137 ++++++++++++++++++++++++-------------- web/electron/package.json | 31 +++++++-- 2 files changed, 115 insertions(+), 53 deletions(-) diff --git a/web/electron/main.js b/web/electron/main.js index 05c92f6..ce8759a 100644 --- a/web/electron/main.js +++ b/web/electron/main.js @@ -1,68 +1,107 @@ -const { app, BrowserWindow } = require('electron'); +const { app, Tray, Menu, nativeImage } = require('electron'); const path = require('path'); +const fs = require('fs'); const { spawn } = require('child_process'); -let mainWindow = null; -let serverProcess = null; +let tray = null; function startServer() { console.log('Starting OpenChamber server...'); - serverProcess = spawn('bun', ['run', 'start:web'], { - cwd: path.join(__dirname, '..'), - stdio: 'inherit', - shell: true, - detached: false - }); - - serverProcess.on('error', (err) => { - console.error('Server error:', err); - }); - - serverProcess.on('exit', (code) => { - console.log('Server exited with code:', code); - }); + let serverPath; + let cwdPath; + let execPath; + + if (app.isPackaged) { + serverPath = path.join(process.resourcesPath, 'server', 'index.js'); + cwdPath = path.join(process.resourcesPath); + execPath = path.join(process.resourcesPath, 'nodejs', 'node.exe'); + } else { + serverPath = path.join(__dirname, '..', 'server', 'index.js'); + cwdPath = path.join(__dirname, '..'); + execPath = 'node'; + } + + console.log('Server path:', serverPath); + console.log('CWD:', cwdPath); + console.log('Exec:', execPath); + + const env = { ...process.env }; + env.OPENCHAMBER_PORT = '3000'; + env.OPENCODE_SKIP_START = 'true'; + + // 创建批处理脚本 + const batContent = `"${execPath}" "${serverPath}"`; + const batPath = path.join(cwdPath, 'start-server.bat'); + fs.writeFileSync(batPath, batContent); + + console.log('Bat path:', batPath); + + try { + const child = spawn('cmd.exe', ['/c', batPath], { + cwd: cwdPath, + stdio: 'inherit', + env: env, + detached: false + }); + + child.on('error', (err) => { + console.error('Server error:', err); + }); + + child.on('exit', (code) => { + console.log('Server exited:', code); + }); + } catch (e) { + console.error('Failed to start:', e); + } } -function createWindow() { - mainWindow = new BrowserWindow({ - width: 800, - height: 600, - show: false, - webPreferences: { - nodeIntegration: false, - contextIsolation: true +function createTray() { + try { + let iconPath; + if (app.isPackaged) { + iconPath = path.join(process.resourcesPath, 'public', 'favicon.png'); + } else { + iconPath = path.join(__dirname, '..', 'public', 'favicon.png'); } - }); - - mainWindow.loadURL('http://localhost:3000'); - - mainWindow.once('ready-to-show', () => { - mainWindow.show(); - }); - - mainWindow.on('closed', () => { - mainWindow = null; - }); + + let icon; + try { + icon = nativeImage.createFromPath(iconPath); + if (icon.isEmpty()) { + icon = nativeImage.createEmpty(); + } + } catch (e) { + icon = nativeImage.createEmpty(); + } + + tray = new Tray(icon); + + const contextMenu = Menu.buildFromTemplate([ + { label: 'OpenChamber Server', enabled: false }, + { type: 'separator' }, + { label: 'Open http://localhost:3000', click: () => { + require('electron').shell.openExternal('http://localhost:3000'); + }}, + { type: 'separator' }, + { label: 'Quit', click: () => { + app.quit(); + }} + ]); + + tray.setToolTip('OpenChamber Server'); + tray.setContextMenu(contextMenu); + } catch (e) { + console.error('Failed to create tray:', e); + } } app.whenReady().then(() => { startServer(); - - setTimeout(() => { - createWindow(); - }, 3000); + createTray(); }); app.on('window-all-closed', () => { - if (serverProcess) { - serverProcess.kill(); - } app.quit(); }); - -app.on('before-quit', () => { - if (serverProcess) { - serverProcess.kill(); - } -}); diff --git a/web/electron/package.json b/web/electron/package.json index 83b7744..6440dc4 100644 --- a/web/electron/package.json +++ b/web/electron/package.json @@ -17,12 +17,35 @@ }, "files": [ "main.js", - "../dist/**/*", - "../server/**/*", - "../bin/**/*", - "../public/**/*", "package.json" ], + "extraResources": [ + { + "from": "../dist", + "to": "dist", + "filter": ["**/*"] + }, + { + "from": "../server", + "to": "server", + "filter": ["**/*"] + }, + { + "from": "../bin", + "to": "bin", + "filter": ["**/*"] + }, + { + "from": "../public", + "to": "public", + "filter": ["**/*"] + }, + { + "from": "D:\\Program Files\\nodejs", + "to": "nodejs", + "filter": ["node.exe"] + } + ], "win": { "target": "portable" },