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 构建跨平台桌面应用。此 skill 提供安全默认配置、类型化 RPC 模式,以及构建、更新和分发方面的运维指导。
Pair with TypeScript Best Practices
配合 TypeScript 最佳实践使用
Always load alongside this skill.
typescript-best-practices始终将 与此 skill 一同加载。
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 schema
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, // 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);
});安全检查清单:
- 对不可信内容使用 。
sandbox: true - 应用严格的导航允许列表。
- 使用不同的 值实现隔离。
partition - 验证 preload script 传递的所有
<electrobun-webview>payload。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 }; // optional: cancel quit
});重要注意事项:
- 目前在 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"
- 键盘快捷键(复制、粘贴、撤销):定义一个 Edit 类型的 ,其中包含基于 role 的菜单项。
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 类型与 Bun 和浏览器两侧的 handler 均匹配。
- 检查目标 webview 是否已启用沙箱(
- 合法 URL 被阻止:
- 检查 的顺序;最后一个匹配项生效。
setNavigationRules - 仅当有意启用严格允许列表模式时,才将 放在首位。
^*
- 检查
- 更新器提示没有更新:
- 验证 以及已上传的
release.baseUrl命名(artifacts/)。{channel}-{os}-{arch}-... - 确认 channel 与构建环境一致(与
canary)。stable
- 验证
- 用户会话在不同账户之间泄漏:
- 为每个账户使用明确独立的 partition,并通过 管理 cookie。
Session.fromPartition(...)
- 为每个账户使用明确独立的 partition,并通过
- 构建 hook 未运行:
- 确保 hook 路径正确,并可通过 Bun 执行。
- 检查 hook 环境变量(例如 、
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
- 构建配置、制品和 hook:reference/build-config.md
- BrowserWindow、BrowserView 和 webview 标签 API:reference/window-and-webview.md
- 菜单、托盘、事件、更新器、utils/session API:reference/platform-apis.md