electrobun-best-practices
Compare original and translation side by side
🇺🇸
Original
English🇨🇳
Translation
ChineseElectrobun Best Practices
Electrobun最佳实践
Electrobun builds cross-platform desktop apps with TypeScript and Bun. This skill gives safe defaults, typed RPC patterns, and operational guidance for build/update/distribution.
Electrobun 基于 TypeScript 和 Bun 构建跨平台桌面应用。本技能提供了构建/更新/分发环节的安全默认配置、类型化RPC模式以及操作指导。
Pair with TypeScript Best Practices
搭配TypeScript最佳实践使用
Always load alongside this skill.
typescript-best-practices请始终同时加载技能。
typescript-best-practicesVersion and Freshness
版本与时效性
Electrobun APIs evolve quickly. Before relying on advanced options or platform-specific behavior, verify against current docs and CLI output.
Electrobun API迭代速度较快。在使用高级选项或平台特定功能前,请务必对照最新文档和CLI输出进行验证。
Architecture
架构
Electrobun apps run as Bun apps:
- Bun process (main): imports from
electrobun/bun - Browser context (views): imports from
electrobun/view - Shared types: RPC schemas shared between both contexts
IPC between bun and browser contexts uses postMessage, FFI, and (in some paths) encrypted WebSockets.
Electrobun应用以Bun应用的形式运行:
- Bun进程(主进程):从导入模块
electrobun/bun - 浏览器上下文(视图层):从导入模块
electrobun/view - 共享类型:主进程与视图层之间共享的RPC模式定义
Bun进程与浏览器上下文之间的IPC通信使用postMessage、FFI,部分场景下会使用加密WebSocket。
Quick Start
快速开始
bash
bunx electrobun init
bun install
bun startRecommended scripts:
json
{
"scripts": {
"start": "electrobun run",
"dev": "electrobun dev",
"dev:watch": "electrobun dev --watch",
"build:dev": "bun install && electrobun build",
"build:canary": "electrobun build --env=canary",
"build:stable": "electrobun build --env=stable"
}
}bash
bunx electrobun init
bun install
bun start推荐脚本配置:
json
{
"scripts": {
"start": "electrobun run",
"dev": "electrobun dev",
"dev:watch": "electrobun dev --watch",
"build:dev": "bun install && electrobun build",
"build:canary": "electrobun build --env=canary",
"build:stable": "electrobun build --env=stable"
}
}Secure Defaults
安全默认配置
Use this baseline for untrusted or third-party content:
typescript
import { BrowserWindow } from "electrobun/bun";
const win = new BrowserWindow({
title: "External Content",
url: "https://example.com",
sandbox: true, // disables RPC, events still work
partition: "persist:external",
});
win.webview.setNavigationRules([
"^*", // block everything by default
"*://example.com/*", // allow only trusted domain(s)
"^http://*", // enforce HTTPS
]);
win.webview.on("will-navigate", (e) => {
console.log("nav", e.data.url, "allowed", e.data.allowed);
});Security checklist:
- Use for untrusted content.
sandbox: true - Apply strict navigation allowlists.
- Use separate values for isolation.
partition - Validate all payloads from
host-messagepreload scripts.<electrobun-webview> - Do not write to at runtime; use
PATHS.RESOURCES_FOLDER.Utils.paths.userData
以下是处理不可信或第三方内容的基准配置:
typescript
import { BrowserWindow } from "electrobun/bun";
const win = new BrowserWindow({
title: "External Content",
url: "https://example.com",
sandbox: true, // 禁用RPC,事件仍可正常工作
partition: "persist:external",
});
win.webview.setNavigationRules([
"^*", // 默认阻止所有请求
"*://example.com/*", // 仅允许可信域名
"^http://*", // 强制使用HTTPS
]);
win.webview.on("will-navigate", (e) => {
console.log("nav", e.data.url, "allowed", e.data.allowed);
});安全检查清单:
- 处理不可信内容时启用。
sandbox: true - 应用严格的导航白名单规则。
- 使用独立的值实现隔离。
partition - 验证来自预加载脚本的所有
<electrobun-webview>负载。host-message - 运行时请勿写入;请使用
PATHS.RESOURCES_FOLDER。Utils.paths.userData
Typed RPC (Minimal Pattern)
类型化RPC(最简模式)
typescript
// src/shared/types.ts
import type { RPCSchema } from "electrobun/bun";
export type MyRPC = {
bun: RPCSchema<{
requests: {
getUser: { params: { id: string }; response: { name: string } };
};
messages: {
logToBun: { msg: string };
};
}>;
webview: RPCSchema<{
requests: {
updateUI: { params: { html: string }; response: boolean };
};
messages: {
notify: { text: string };
};
}>;
};typescript
// bun side
import { BrowserView, BrowserWindow } from "electrobun/bun";
import type { MyRPC } from "../shared/types";
const rpc = BrowserView.defineRPC<MyRPC>({
handlers: {
requests: {
getUser: ({ id }) => ({ name: `user-${id}` }),
},
messages: {
logToBun: ({ msg }) => console.log(msg),
},
},
});
const win = new BrowserWindow({
title: "App",
url: "views://mainview/index.html",
rpc,
});
await win.webview.rpc.updateUI({ html: "<p>Hello</p>" });typescript
// browser side
import { Electroview } from "electrobun/view";
import type { MyRPC } from "../shared/types";
const rpc = Electroview.defineRPC<MyRPC>({
handlers: {
requests: {
updateUI: ({ html }) => {
document.body.innerHTML = html;
return true;
},
},
messages: {
notify: ({ text }) => console.log(text),
},
},
});
const electroview = new Electroview({ rpc });
await electroview.rpc.request.getUser({ id: "1" });
electroview.rpc.send.logToBun({ msg: "hello" });typescript
// src/shared/types.ts
import type { RPCSchema } from "electrobun/bun";
export type MyRPC = {
bun: RPCSchema<{
requests: {
getUser: { params: { id: string }; response: { name: string } };
};
messages: {
logToBun: { msg: string };
};
}>;
webview: RPCSchema<{
requests: {
updateUI: { params: { html: string }; response: boolean };
};
messages: {
notify: { text: string };
};
}>;
};typescript
// bun side
import { BrowserView, BrowserWindow } from "electrobun/bun";
import type { MyRPC } from "../shared/types";
const rpc = BrowserView.defineRPC<MyRPC>({
handlers: {
requests: {
getUser: ({ id }) => ({ name: `user-${id}` }),
},
messages: {
logToBun: ({ msg }) => console.log(msg),
},
},
});
const win = new BrowserWindow({
title: "App",
url: "views://mainview/index.html",
rpc,
});
await win.webview.rpc.updateUI({ html: "<p>Hello</p>" });typescript
// browser side
import { Electroview } from "electrobun/view";
import type { MyRPC } from "../shared/types";
const rpc = Electroview.defineRPC<MyRPC>({
handlers: {
requests: {
updateUI: ({ html }) => {
document.body.innerHTML = html;
return true;
},
},
messages: {
notify: ({ text }) => console.log(text),
},
},
});
const electroview = new Electroview({ rpc });
await electroview.rpc.request.getUser({ id: "1" });
electroview.rpc.send.logToBun({ msg: "hello" });Events and Shutdown
事件与关机处理
Use for shutdown cleanup instead of relying on for async work.
before-quitprocess.on("exit")typescript
import Electrobun from "electrobun/bun";
Electrobun.events.on("before-quit", async (e) => {
await saveState();
// e.response = { allow: false }; // optional: cancel quit
});Important caveat:
- Linux currently has a caveat where some system-initiated quit paths (for example Ctrl+C/window-manager/taskbar quit) may not fire . Programmatic quit via
before-quit/Utils.quit()is reliable.process.exit()
请使用事件进行关机清理操作,而非依赖处理异步任务。
before-quitprocess.on("exit")typescript
import Electrobun from "electrobun/bun";
Electrobun.events.on("before-quit", async (e) => {
await saveState();
// e.response = { allow: false }; // 可选:取消关机操作
});重要注意事项:
- 目前Linux平台存在一个限制:部分由系统触发的关机流程(例如Ctrl+C/窗口管理器/任务栏关闭)可能不会触发事件。通过
before-quit/Utils.quit()执行的程序化关机则是可靠的。process.exit()
Common Patterns
常见模式
- Keyboard shortcuts (copy/paste/undo): define an Edit with role-based items.
ApplicationMenu - Tray-only app: set , then drive UX from
runtime.exitOnLastWindowClosed: false.Tray - Multi-account isolation: use separate values per account.
partition - Chromium consistency: set and
bundleCEF: truein platform config.defaultRenderer: "cef"
- 键盘快捷键(复制/粘贴/撤销):定义一个包含基于角色项的编辑。
ApplicationMenu - 仅托盘应用:设置,然后通过
runtime.exitOnLastWindowClosed: false驱动用户交互。Tray - 多账号隔离:为每个账号使用独立的值。
partition - Chromium一致性:在平台配置中设置和
bundleCEF: true。defaultRenderer: "cef"
Troubleshooting
故障排查
- RPC calls fail unexpectedly:
- Check whether the target webview is sandboxed (disables RPC).
sandbox: true - Confirm shared RPC types match both bun and browser handlers.
- Check whether the target webview is sandboxed (
- Navigation blocks legitimate URLs:
- Review ordering; last match wins.
setNavigationRules - Keep first only when you intentionally run strict allowlist mode.
^*
- Review
- Updater says no update:
- Verify and uploaded
release.baseUrlnaming (artifacts/).{channel}-{os}-{arch}-... - Confirm channel/build env alignment (vs
canary).stable
- Verify
- User sessions leak across accounts:
- Use explicit per-account partitions and manage cookies via .
Session.fromPartition(...)
- Use explicit per-account partitions and manage cookies via
- Build hooks not running:
- Ensure hook paths are correct and executable via Bun.
- Inspect hook env vars (for example ,
ELECTROBUN_BUILD_ENV,ELECTROBUN_OS).ELECTROBUN_ARCH
- RPC调用意外失败:
- 检查目标webview是否启用了沙箱模式(会禁用RPC)。
sandbox: true - 确认主进程与浏览器进程的共享RPC类型定义一致。
- 检查目标webview是否启用了沙箱模式(
- 导航阻止了合法URL:
- 检查的规则顺序;最后匹配的规则生效。
setNavigationRules - 只有当你有意启用严格白名单模式时,才将放在首位。
^*
- 检查
- 更新器提示无可用更新:
- 验证和已上传的
release.baseUrl命名格式(artifacts/)。{channel}-{os}-{arch}-... - 确认渠道/构建环境一致(vs
canary)。stable
- 验证
- 用户会话在多账号间泄露:
- 为每个账号使用明确的独立分区,并通过管理Cookie。
Session.fromPartition(...)
- 为每个账号使用明确的独立分区,并通过
- 构建钩子未运行:
- 确保钩子路径正确且可通过Bun执行。
- 检查钩子环境变量(例如、
ELECTROBUN_BUILD_ENV、ELECTROBUN_OS)。ELECTROBUN_ARCH
Reference Files
参考文档
- Build config, artifacts, and hooks: reference/build-config.md
- BrowserWindow, BrowserView, and webview tag APIs: reference/window-and-webview.md
- Menus, tray, events, updater, utils/session APIs: reference/platform-apis.md
- 构建配置、产物与钩子:reference/build-config.md
- BrowserWindow、BrowserView与webview标签API:reference/window-and-webview.md
- 菜单、托盘、事件、更新器、工具类/会话API:reference/platform-apis.md