关于 electron-wrapper
为现有 Bun Web 应用提供封装为 Electron 桌面应用的实施指南,覆盖主进程、预加载桥接、应用适配与构建发布。适用于希望保留 Web 服务逻辑,同时增加原生窗口和系统功能的开发者。
主要功能
- 01Electron 与 Bun 双运行时架构
- 02启动 Bun 服务并加载本地页面
- 03预加载桥接与 IPC 管理
- 04Web 应用适配 Electron 环境
- 05构建、签名与 CI 发布指导
适用场景
- 01将现有 Bun Web 服务封装为桌面应用
- 02为应用添加窗口管理和原生菜单
- 03配置 Electron 桌面应用的构建与发布流程
安装
复制下面的命令,在终端中运行。
npx skills add https:/ / github.com/ brianlovin/ agent-config --skill electron-wrapper安装后在 Claude Code 中输入/electron-wrapper确认已加载。
包含的文件
除 SKILL.md 外,安装时一起下载的参考资料和脚本。
references/build-and-distribute.mdmain-process.mdpitfalls.mdproject-setup.mdweb-adaptation.md详细介绍
Bun Web 应用的 Electron 封装
本技能将指导你使用 Electron 把现有的 Bun Web 服务器封装为原生桌面应用。它基于经过验证的实现,解决了所有主要集成难题。
架构
“Electron 负责外壳,Bun 负责服务”——两个运行时协同工作:
- Electron/Node.js 负责窗口管理、原生菜单、自动更新和 IPC
- Bun 运行实际的 Web 服务器及所有应用逻辑
Electron 主进程会启动打包后的 Bun 二进制文件来运行服务器,然后在 BrowserWindow 中加载 http://localhost:{port}。你的 Web 应用无需知道或关心自己运行在 Electron 中——它只是一张网页,还可以选择使用 window.electronAPI 桥接原生功能。
这种架构意味着:
- 无需更改服务器代码(它仍是标准的 Bun HTTP 服务器)
- Web 应用在浏览器和 Electron 中的运行方式完全相同
- Electron 只处理浏览器无法完成的任务:窗口外框、系统托盘、自动更新、文件系统访问
- 使用两个独立的
node_modules——Electron 使用 npm,Web 应用使用 Bun
第 1 阶段:项目设置
在现有 Bun Web 应用旁创建 Electron 子项目。
需要创建的内容:
electron/目录,其中包含独立的package.json(使用 npm,而非 Bun)、两个 tsconfig(主进程使用 ESM,预加载使用 CJS)、electron-builder.yml和 macOS 权限配置- 用于打包服务器的
scripts/build-server.ts - 用于下载特定平台 Bun 二进制文件的
scripts/download-bun.ts - 父项目中的更改:新增脚本、tsconfig 排除项和 .gitignore 条目
第 2 阶段:主进程
构建 Electron 主进程——包括入口点、服务器启动、窗口管理、自动更新器和预加载桥接。
需要创建的文件:
| 文件 | 用途 |
|---|---|
electron/src/main/index.ts | 应用生命周期、开发/生产模式、单实例锁、IPC 处理器 |
electron/src/main/bun-server.ts | 启动打包后的 Bun、选择端口、轮询健康状态、注入环境变量 |
electron/src/main/window.ts | BrowserWindow 配置、窗口边界持久化、安全设置、导航防护 |
electron/src/main/updater.ts | electron-updater 设置、将事件转发给渲染进程 |
electron/src/preload/index.ts | 使用 invoke/on 模式并支持取消订阅的 contextBridge API |
关键决策:
- 开发模式使用
ELECTRON_DEV_URL环境变量连接外部开发服务器(不在内部启动 Bun) autoDownload: false——让用户选择何时下载更新- 预加载脚本只公开特定方法,绝不公开原始
ipcRenderer - 使用
electron-store持久化窗口边界
第 3 阶段:Web 应用适配
调整现有 Web 应用,使其能够检测并响应 Electron 环境,同时仍可作为独立 Web 应用正常运行。
Web 应用需要进行的更改:
| 更改 | 详情 |
|---|---|
| Electron 检测工具 | isElectron()、getElectronPlatform()、isMacElectron()、applyElectronDocumentAttributes() |
| 类型声明 | 将 window.electronAPI 的所有属性设为可选 |
| CSS 拖拽区域 | .app-window-drag/.app-window-no-drag 类,自动排除交互元素 |
| 交通灯间距 | --electron-traffic-left CSS 变量(macOS 上为 72px,其他平台为 0px) |
| 存储路径 | 使用环境变量指定数据目录,并在未设置时回退到 CWD |
| 静态资源服务 | 在生产模式下使用环境变量指定静态资源目录 |
| 自动更新钩子 | 提供下载/安装控制功能的 useElectronUpdater() React 钩子 |
| 更新通知 | 显示“可用 → 正在下载 → 就绪”状态的胶囊组件 |
| 功能门控 | 在 Electron 中禁用演示模式和托管功能 |
阶段 4:构建与分发
打包所有内容、设置 CI/CD 并处理代码签名。
构建流水线:
- 构建 Web 应用(
bun run build) - 将服务打包为单个文件(
Bun.build()→resources/server/index.js) - 下载特定平台的 Bun 二进制文件 →
resources/bun/{platform}-{arch}/ - 编译 Electron TypeScript(分两次:主进程 ESM + 预加载 CJS)
- electron-builder 使用
extraResources打包所有内容
CI/CD:
- GitHub Actions 由版本标签触发(例如
v*、clippy-v*) - 矩阵构建:在
macos-14上构建 macOS arm64/x64,在windows-latest上构建 Windows x64 - 构建步骤中使用
--publish never,单独的发布任务会创建 GitHub 草稿版本 - 在 CI 中导入 Apple 证书并进行公证
代码签名:
- macOS:Developer ID Application 证书,导出为 base64
.p12 - 通过 Apple ID + app 专用密码进行公证
- 需要 5 个 GitHub 密钥:
APPLE_CERTIFICATE、APPLE_CERTIFICATE_PASSWORD、APPLE_ID、APPLE_PASSWORD、APPLE_TEAM_ID
图标:
- macOS:使用
sips+iconutil将源 PNG 转换为.icns - Windows:使用
png-to-iconpm 包转换为.ico
发布版本
**切勿在本地构建发布产物。**CI 拥有签名证书和公证凭据。本地构建会生成未签名的应用,macOS Gatekeeper 会阻止其运行。
发布工作流:
- 在
electron/package.json中更新版本号,提交更改并合并到 main - 查找 CI 工作流所需的标签格式:
grep -A2 'tags:' .github/workflows/*.yml - 为 main 上已合并的提交打标签:
git tag <pattern><version> origin/main git push origin <pattern><version> - 监控 CI:
gh run list --workflow=<workflow>.yml --limit=1 - 在 GitHub 上审核并发布草稿版本
常见错误:
- 在本地运行
electron-builder --publish always——不会进行公证 - 使用
gh release create和本地构建产物——未签名 - 在版本号更新合并前打标签——构建使用的版本号不正确
- 为功能分支而非
origin/main打标签
完整详情请参阅 pitfalls.md §13。
关键注意事项
快速参考列表——症状和代码示例详见 pitfalls.md。
| # | 注意事项 | 一句话修复方法 |
|---|---|---|
| 1 | ESM/CJS 冲突 | 设置 "type": "module",并对 CJS 包使用默认导入模式 |
| 2 | 预加载脚本必须使用 CJS | 使用单独的 tsconfig,并设置 "module": "CommonJS" |
| 3 | __dirname 不可用 | 使用 fileURLToPath(import.meta.url) 填充实现 |
| 4 | 开发模式 MIME 错误 | 通过 ELECTRON_DEV_URL 连接到外部开发服务 |
| 5 | Bun 版本不匹配 | 在下载脚本中固定版本,并与开发环境版本保持一致 |
| 6 | nvm PATH 问题 | 对生成的进程使用 bash -lc |
| 7 | 存储路径错误 | 使用环境变量 + app.getPath("userData") |
| 8 | 打开时出现白屏 | 使用 show: false + ready-to-show + 深色 backgroundColor |
| 11 | ${platform} ≠ process.platform | 将 Bun extraResources 放在 mac:/win: 部分中,并使用 darwin-/win32- 前缀 |
| 12 | Bun 工作区会提升依赖 | 使用 esbuild + createRequire banner 打包主进程,或对 electron 目录使用 npm |
| 13 | 本地构建不会进行公证 | 始终通过 CI 标签发布,切勿在本地使用 electron-builder --publish |
开发工作流
electron:dev 命令会运行完整的开发环境:
npm run dev
├── concurrently
│ ├── dev:web → cd .. && bun run dev (Bun dev server with HMR)
│ └── dev:electron
│ ├── wait-on http://localhost:3005 (wait for dev server)
│ ├── npm run build (compile TS)
│ └── ELECTRON_DEV_URL=... electron . (launch Electron)
- Web 开发服务会运行 HMR——更改会立即生效
- Electron 会连接到开发服务,而不是启动自己的 Bun
- 修改预加载脚本和主进程后,需要重启
electron:dev - Web 应用的更改会自动热重载
若要在本地测试接近生产环境的行为:
bun run electron:pack # builds everything, packages without installer
# Output in electron/release/
自定义检查清单
将此项目改用于新项目时,请更新以下项目专属值:
-
electron-builder.yml中的应用名称(productName、appId) -
window.ts中的窗口标题 -
dev:electron脚本和dev脚本中的开发服务器端口 - 环境变量名称(例如,
APP_DATA_DIR、APP_STATIC_DIR) - 窗口配置中的
backgroundColor,使其与应用主题相匹配 -
electron-builder.yml的 mac 部分中的category -
electron/package.json中的仓库 URL -
download-bun.ts中的 Bun 版本 -
electron/assets/中的图标资源