/** * HoloLake Desktop · Electron 主进程 * * 职责: * 1. 创建应用窗口 * 2. 启动知识库后端服务器(Express + Git 引擎) * 3. 管理应用生命周期 * * 架构: * Electron Main Process * ├── 启动 Express 服务器(Git 引擎层,端口 3890) * ├── 创建 BrowserWindow(加载 Vite 前端或打包后的 dist) * └── 数据存储:~/Library/Application Support/HoloLake Era/data/ */ import { app, BrowserWindow, shell, dialog } from 'electron'; import path from 'path'; import { spawn, ChildProcess } from 'child_process'; // ─── 配置 ─── const isDev = process.env.NODE_ENV !== 'production'; const SERVER_PORT = 3890; const CLIENT_PORT = 5180; // 数据目录:macOS 标准位置 const DATA_DIR = path.join(app.getPath('userData'), 'data'); const KB_REPO_PATH = path.join(DATA_DIR, 'knowledge-base'); // ─── 后端服务器 ─── let serverProcess: ChildProcess | null = null; function startServer(): Promise { return new Promise((resolve, reject) => { // 在开发模式下,服务器由 concurrently 启动 if (isDev && process.env.KB_SERVER_RUNNING === '1') { resolve(); return; } const serverScript = path.resolve(__dirname, '../../guanghu-knowledge-base/server/index.ts'); serverProcess = spawn('npx', ['tsx', serverScript], { env: { ...process.env, KB_PORT: String(SERVER_PORT), KB_REPO_PATH, NODE_ENV: 'production', }, cwd: path.resolve(__dirname, '../..'), stdio: ['pipe', 'pipe', 'pipe'], }); serverProcess.stdout?.on('data', (data: Buffer) => { const msg = data.toString().trim(); console.log(`[KB Server] ${msg}`); if (msg.includes('已启动')) resolve(); }); serverProcess.stderr?.on('data', (data: Buffer) => { console.error(`[KB Server Error] ${data.toString().trim()}`); }); serverProcess.on('error', (err: Error) => { console.error('服务器启动失败:', err); reject(err); }); serverProcess.on('exit', (code: number | null) => { console.log(`[KB Server] 退出,code=${code}`); serverProcess = null; }); // 超时兜底 setTimeout(() => resolve(), 5000); }); } function stopServer(): void { if (serverProcess) { serverProcess.kill('SIGTERM'); serverProcess = null; } } // ─── 窗口 ─── let mainWindow: BrowserWindow | null = null; function createWindow(): void { mainWindow = new BrowserWindow({ width: 1280, height: 860, minWidth: 800, minHeight: 600, titleBarStyle: 'hiddenInset', trafficLightPosition: { x: 16, y: 16 }, backgroundColor: '#0d1117', webPreferences: { preload: path.join(__dirname, 'preload.js'), contextIsolation: true, nodeIntegration: false, }, }); if (isDev) { mainWindow.loadURL(`http://localhost:${CLIENT_PORT}`); mainWindow.webContents.openDevTools({ mode: 'detach' }); } else { mainWindow.loadFile(path.join(__dirname, '../dist/index.html')); } // 外部链接用系统浏览器打开 mainWindow.webContents.setWindowOpenHandler(({ url }) => { if (url.startsWith('http')) shell.openExternal(url); return { action: 'deny' }; }); mainWindow.on('closed', () => { mainWindow = null; }); } // ─── 应用生命周期 ─── app.whenReady().then(async () => { try { await startServer(); createWindow(); } catch (err) { dialog.showErrorBox('启动失败', `知识库引擎启动失败:${err}`); app.quit(); } app.on('activate', () => { if (BrowserWindow.getAllWindows().length === 0) createWindow(); }); }); app.on('window-all-closed', () => { stopServer(); if (process.platform !== 'darwin') app.quit(); }); app.on('before-quit', () => { stopServer(); });