<< All versions
Skill v1.0.1
currentAutomated scan100/100lichtblick-suite/lichtblick/electron-internals
+3 new
──Details
PublishedAugust 18, 2026 at 11:06 PM
Content Hashsha256:591f421ced3baefa...
Git SHA355014d99402
Bump Typepatch
──Files
Files (1 file, 5.4 KB)
SKILL.md5.4 KBactive
SKILL.md · 146 lines · 5.4 KB
version: "1.0.1" 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:
typescript
// packages/suite-desktop/src/preload/index.tscontextBridge.exposeInMainWorld("ctxbridge", ctx); // main app/context API (Desktop)contextBridge.exposeInMainWorld("menuBridge", menuBridge); // native menu event subscriptioncontextBridge.exposeInMainWorld("storageBridge", storageBridge); // local file storage CRUDcontextBridge.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.) |
typescript
// renderer — consuming a bridgeconst 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)
typescript
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
typescript
// Main process builds menu templateconst template: MenuItemConstructorOptions[] = [{ label: "File", submenu: [{ label: "Open File...", click: () => sendToRenderer("open-file") },]},];// Renderer receives via menuBridgemenuBridge.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 match arg.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 appactually 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