关闭桌面窗口后,进程是否继续运行,是应用需要明确选择的行为。ChatCrystal 当前选择隐藏到托盘;恢复尺寸、恢复窗口和真正退出分别由不同代码负责。本文对照固定版本的 主进程 与 托盘,不把它们概括成完整的跨平台体验保证。主进程和 preload 的基础关系见 Electron 入门。
托盘菜单随运行模式变化
createTray 先销毁旧实例,再创建 16×16 的 nativeImage 和 Tray。它接收模式与回调,而不是把所有菜单固定到 localhost。
| 模式 | 浏览器与搜索目标 | 始终提供的操作 |
|---|---|---|
| onboarding | 没有已连接目标,不添加这两项 | 显示窗口、重新连接、检查更新、退出 |
| local | localBaseUrl |
同上 |
| cloud | cloudBaseUrl |
同上 |
搜索项加载目标的 /search;浏览器项交给 shell.openExternal。双击和“显示 ChatCrystal”使用 showMainWindow,先恢复最小化窗口,再 show、focus。搜索项直接 show、focus、loadURL,没有相同的显式 restore 步骤,不能保证所有入口都按同一路径恢复。
16×16 是该版本采用的缩放尺寸,不意味着任意源图片或平台都能得到清晰图标。Electron Tray 文档 区分平台事件与图标要求,双击事件标注为 macOS/Windows。Linux 桌面环境仍需单独验收;源码注册了事件不等于各平台表现相同。
close 保存状态,再决定是否隐藏
以下是实际 close 处理的片段,依赖主进程已有的变量和函数,不是可单独运行的应用:
win.on('close', (event) => {
saveWindowState(win);
if (!isQuitting) {
event.preventDefault();
win.hide();
}
});
常规关闭时,isQuitting 为 false,窗口保存后隐藏,服务与进程不会因为这次 close 自动结束。托盘“退出”调用 app.quit(),第一次 before-quit 拦截退出并将该标志设为 true;清理结束后的第二次 quit 才继续退出。window-all-closed 的处理器不调用 quit。
隐藏保留当前进程的状态;它不意味着所有数据已经写盘。窗口 JSON 和知识库数据库属于不同保存路径,不能用“窗口关了再打开正常”证明数据库持久化成功。
保存普通尺寸,与恢复坐标是两项检查
状态文件位于 app.getPath('userData')/window-state.json,保存 x、y、width、height 和 isMaximized。非最大化的 resize/move 更新 lastNormalBounds;最大化时优先保存这个缓存,否则退回当前 getBounds。
这能保留已经观察到的普通尺寸,但缓存不是一开始就有值,也不是完整的 fullscreen/minimize 状态模型。Electron 另有 getNormalBounds;当前代码使用的是缓存方案,讨论改进时要分清可用 API 与已实现行为。
读取或 JSON 解析失败会回到默认 1280×800、非最大化。正常读取后,宽高若不是有限数则回到默认值,再限制到至少 1280×800。这是当前默认和最小尺寸,原稿中的 900×600 已不符合实现。保存异常被吞掉,属于尽力保存,没有写入成功提示或重试。
坐标验证只检查保存的左上点是否落在某块显示器 bounds 内,左侧与顶部各允许 50 的容差。都不匹配时清空 x/y,让系统决定位置。它没有按整个窗口与 workArea 的交集检查,也没有把尺寸限制到小屏幕可用区域;坐标字段缺少完整的运行时 schema 校验。因此不能承诺副屏拔掉后整个窗口必定可见。
真正退出有顺序,也有超时
gracefulShutdown 先对活跃的远端导入 worker 调用 kill 并清空引用,再等待本地服务的 shutdown,最后销毁托盘。这里没有等待 worker 的 exit 事件。服务关闭如何处理数据库与 HTTP,见 Fastify 生产静态服务。
before-quit 设置 10 秒定时器,超时调用 app.exit(1);正常完成或清理抛错,都在 finally 清除定时器并再次 quit。事件循环仍能运行时,定时器限制异步清理的等待;它不能抢占阻塞主线程的同步写盘,也不证明所有写入已经完成。requestSingleInstanceLock 和 second-instance 处理避免同一 Electron 应用重复打开窗口,不是对 SQLite 文件的跨 CLI/桌面进程写锁。
用场景验收,而不是只看窗口出现
建议在独立测试用户配置下逐项记录:普通窗口关闭后可从托盘恢复;最小化后分别用显示、双击与搜索入口恢复;最大化关闭后再启动,取消最大化时尺寸是否正确;拔掉副屏后重新启动;状态文件损坏或不可写;服务清理失败以及超过 10 秒。
这些是待执行的验收场景,不是本文声称完成的桌面实测。检查结果应分别记录窗口可见性、状态文件变化与进程退出码,避免用其中一项替代另外两项。