Deep Electron implementation knowledge: main/renderer process communication, contextBridge patterns, BrowserWindow lifecycle, native menu integration, and security considerations.
61
71%
Does it follow best practices?
Run evals on this skill
Adds up to 20 points to the overall score
View guide
Passed
No findings from the security scan
Fix and improve this skill with Tessl
tessl review fix ./.github/skills/electron-internals/SKILL.mdcontextBridge.exposeInMainWorld()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");ipcRenderer directly.bind()-attached in preloadeval(), no remote module usageclass 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
},
});
}
}StudioWindow created// 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
}
});storageBridge (list, all, get, put, delete)DesktopLayoutLoader (packages/suite-desktop/src/renderer/services/DesktopLayoutLoader.ts) wraps these calls.foxe files in extension directoryDesktopExtensionLoader (filesystem type) reads directly via bridgelichtblick://open?url=https://example.com/recording.mcapfoxglove scheme:
app.setAsDefaultProtocolClient("foxglove") (packages/suite-desktop/src/main/index.ts)lichtblick:// scheme — the open-url handler and
second-instance argv filter both match arg.startsWith("lichtblick://")lichtblick://open?... and lichtblick://signin-completeopen-url and forwards to the existing window⚠️ The protocol-client registration argument (
"foxglove") differs from the URL scheme the app actually parses (lichtblick://). Do not assume they are the same string.
desktop/electronBuilderConfig.js — electron-builder configurationdesktop/webpack.config.ts — webpack for main/preload/renderer.dmg (macOS), .exe/.msi (Windows), .deb/.AppImage (Linux)show: false + ready-to-show event for smooth startup0900ce3
If you maintain this skill, you can claim it as your own. Once claimed, you can manage eval scenarios, bundle related skills, attach documentation or rules, and ensure cross-agent compatibility.