Agent Skills
› lichtblick-suite/lichtblick
› electron-internals
electron-internals
GitHub提供Electron内部机制知识,涵盖主/渲染进程通信、contextBridge模式、BrowserWindow生命周期及原生菜单集成。强调安全规范,如隔离Node访问、禁止直接暴露ipcRenderer,并解析多桥接架构与窗口管理实现细节。
Trigger Scenarios
询问Electron进程架构
配置contextBridge或preload脚本
处理BrowserWindow生命周期
集成原生菜单系统
解决Electron安全限制问题
Install
npx skills add lichtblick-suite/lichtblick --skill electron-internals -g -y
SKILL.md
Frontmatter
{
"name": "electron-internals",
"description": "Deep Electron implementation knowledge: main\/renderer process communication, contextBridge patterns, BrowserWindow lifecycle, native menu integration, and security considerations."
}
Electron Internals Skill
Process Architecture
Main Process
- Node.js environment with full OS access
- Manages BrowserWindow instances
- Handles app lifecycle (startup, quit, focus)
- Single-instance lock prevents multiple app copies
Preload Script
- Runs in renderer context BUT with Node.js access
- Bridge between main and renderer via
contextBridge.exposeInMainWorld() - Must be minimal — every import adds to startup time
Renderer Process
- Standard web environment (Chromium)
- No direct Node.js access (security)
- Communicates with main via exposed bridges
contextBridge Pattern
The preload script (packages/suite-desktop/src/preload/index.ts) exposes four separate
bridges to the renderer — not a single desktopBridge:
// packages/suite-desktop/src/preload/index.ts
contextBridge.exposeInMainWorld("ctxbridge", ctx); // main app/context API (Desktop)
contextBridge.exposeInMainWorld("menuBridge", menuBridge); // native menu event subscription
contextBridge.exposeInMainWorld("storageBridge", storageBridge); // local file storage CRUD
contextBridge.exposeInMainWorld("desktopBridge", desktopBridge); // desktop-specific operations
| Bridge | Type | Purpose |
|---|---|---|
ctxbridge |
Desktop |
Core context API consumed by the renderer app shell |
menuBridge |
NativeMenuBridge |
Subscribe to forwarded native menu events (addIpcEventListener) |
storageBridge |
Storage |
Local file storage: list, all, get, put, delete |
desktopBridge |
Desktop |
Desktop-specific operations (deep links, color scheme, etc.) |
// renderer — consuming a bridge
const desktopBridge = (global as { desktopBridge: Desktop }).desktopBridge;
const storageBridge = (global as { storageBridge: Storage }).storageBridge;
await storageBridge.list("layouts");
Security Rules
- Never expose
ipcRendererdirectly - Class instances do not survive the bridge — only plain functions/objects are exposed (prototypes are lost), which is why storage methods are
.bind()-attached in preload - Each bridge method is a typed, scoped function
- No
eval(), noremotemodule usage - CSP headers prevent inline scripts
BrowserWindow Management (StudioWindow)
class StudioWindow {
#window: BrowserWindow;
constructor() {
this.#window = new BrowserWindow({
webPreferences: {
preload: path.join(__dirname, "preload.js"),
contextIsolation: true,
nodeIntegration: false,
sandbox: false, // needed for preload Node access
},
});
}
}
Window Lifecycle
- App starts →
StudioWindowcreated - Preload runs → bridges exposed
- Renderer loads → React app mounts
- Deep links → forwarded to renderer via bridge
- Close → cleanup, save state, quit
Native Menu Integration
// Main process builds menu template
const template: MenuItemConstructorOptions[] = [
{ label: "File", submenu: [
{ label: "Open File...", click: () => sendToRenderer("open-file") },
]},
];
// Renderer receives via menuBridge
menuBridge.on("menu-event", (event: ForwardedMenuEvent) => {
switch (event) {
case "open-file": // show file picker
}
});
File System Access
Layout / Storage Loading
- Local storage entries are read/written via
storageBridge(list,all,get,put,delete) - The renderer's
DesktopLayoutLoader(packages/suite-desktop/src/renderer/services/DesktopLayoutLoader.ts) wraps these calls
Extension Loading
.foxefiles in extension directoryDesktopExtensionLoader(filesystem type) reads directly via bridge- Supports install/uninstall by copying/deleting files
Deep Links
lichtblick://open?url=https://example.com/recording.mcap
- OS protocol registration uses the legacy
foxglovescheme:app.setAsDefaultProtocolClient("foxglove")(packages/suite-desktop/src/main/index.ts) - Handled deep-link URLs use the
lichtblick://scheme — theopen-urlhandler and second-instance argv filter both matcharg.startsWith("lichtblick://") - Recognized links include
lichtblick://open?...andlichtblick://signin-complete - Second-instance handler re-emits
open-urland forwards to the existing window - Parsed in renderer to open the appropriate data source
⚠️ The protocol-client registration argument (
"foxglove") differs from the URL scheme the app actually parses (lichtblick://). Do not assume they are the same string.
Build & Packaging
desktop/electronBuilderConfig.js— electron-builder configurationdesktop/webpack.config.ts— webpack for main/preload/renderer- Output:
.dmg(macOS),.exe/.msi(Windows),.deb/.AppImage(Linux) - Auto-update via electron-updater (if configured)
Performance Tips
- Preload weight: Keep preload imports minimal — delays window show
- IPC serialization: Large objects are serialized — prefer transferring file paths over file contents
- Window show: Use
show: false+ready-to-showevent for smooth startup - Background throttling: Electron throttles background tabs by default — respect this for power usage
Version History
- cab9317 Current 2026-07-24 12:16


