Files
MailUI4Agents/web/electron/main.cjs
JianFeeeee dc5b9e440f pi-ele Phase 1: Electron 骨架(主进程+托盘+preload,可复用 web/src)
- web/electron/main.cjs: BrowserWindow 加载 dist/index.html(prod)或 vite dev server
  托盘(进托盘/退出/单击恢复),关闭按钮隐藏到托盘而非退出(用户要求)
- web/electron/preload.cjs: contextBridge 暴露 gatewayUrl/versions/platform
- web/package.json: 加 main 指向 main.cjs、dev:electron / build:win / build:linux 脚本、
  electron 44.2.0 + electron-builder 26.15.3 依赖、build 配置(NSIS/AppImage/deb)
- .gitignore: /release/

验证(headless + xvfb):
- Electron 加载 Gateway WebUI 成功(title=AgentMail, body 渲染)
- Tray API 可用,BrowserWindow 正常
- prod 模式真实 main.cjs 无报错
2026-09-07 07:46:07 +08:00

124 lines
3.4 KiB
JavaScript
Raw Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

/**
* AgentMail Electron 主进程。
*
* 职责:
* - 创建 BrowserWindow 加载前端dev=http://localhost:5173prod=../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';
});