/** * AgentMail Electron 主进程。 * * 职责: * - 创建 BrowserWindow 加载前端(dev=http://localhost:5173,prod=../dist/index.html) * - 系统托盘(Phase 4 完整实现,这里先预留) * - SSE 长连接由主进程维持(Phase 2),这里先只做窗口生命周期 * * 设计约束: * - 渲染进程复用 web/src 的 React 代码,零改动 * - 只通过 contextBridge 暴露必要 API,不放开 nodeIntegration */ const { app, BrowserWindow, Tray, Menu, nativeImage, ipcMain } = require('electron'); const path = require('node:path'); // 开发模式:ELECTRON_START_URL 环境变量指向 vite dev server const DEV_URL = process.env.ELECTRON_START_URL || 'http://localhost:5173'; const isDev = !!process.env.ELECTRON_START_URL || !app.isPackaged; let mainWindow = null; let tray = null; /** 创建主窗口 */ function createMainWindow() { mainWindow = new BrowserWindow({ width: 1280, height: 800, minWidth: 375, minHeight: 640, title: 'AgentMail', icon: path.join(__dirname, '..', 'src', 'icons', 'tray-icon.png'), webPreferences: { preload: path.join(__dirname, 'preload.js'), contextIsolation: true, nodeIntegration: false, sandbox: true, }, }); if (isDev) { mainWindow.loadURL(DEV_URL); } else { mainWindow.loadFile(path.join(__dirname, '..', 'dist', 'index.html')); } // 点关闭按钮时隐藏到托盘而不是退出(Phase 4 行为,但用户要求进托盘,直接做) mainWindow.on('close', (e) => { if (!app.isQuitting) { e.preventDefault(); mainWindow.hide(); } }); mainWindow.on('closed', () => { mainWindow = null; }); } /** 系统托盘(Phase 4,这里是骨架:进托盘 + 重新显示) */ function createTray() { // 用 16x16 像素的图标;缺文件时降级为 nativeImage.createEmpty() let iconPath = path.join(__dirname, '..', 'src', 'icons', 'tray-icon.png'); let icon = nativeImage.createFromPath(iconPath); if (icon.isEmpty()) { icon = nativeImage.createEmpty(); } tray = new Tray(icon); tray.setToolTip('AgentMail'); const contextMenu = Menu.buildFromTemplate([ { label: '打开 AgentMail', click: () => { if (!mainWindow) createMainWindow(); mainWindow.show(); mainWindow.focus(); }, }, { type: 'separator' }, { label: '退出', click: () => { app.isQuitting = true; app.quit(); }, }, ]); tray.setContextMenu(contextMenu); // 单击托盘图标显示窗口(Windows 常用行为) tray.on('click', () => { if (!mainWindow) createMainWindow(); mainWindow.show(); mainWindow.focus(); }); } // Electron 生命周期 app.whenReady().then(() => { createMainWindow(); createTray(); app.on('activate', () => { // macOS 点击 Dock 图标重新建窗口 if (BrowserWindow.getAllWindows().length === 0) createMainWindow(); else if (mainWindow) mainWindow.show(); }); }); app.on('window-all-closed', () => { // 托盘应用:关窗口不退出(macOS 惯例 + 用户要求的"进托盘"行为) // 除非显式退出 }); app.on('before-quit', () => { app.isQuitting = true; }); // IPC:渲染进程问主进程要 Gateway 地址(Phase 2 用) ipcMain.handle('get-gateway-url', () => { return process.env.AGENTMAIL_GATEWAY_URL || 'http://127.0.0.1:8180'; });