.claude/skills/electron-pro/SKILL.md
Expert Electron desktop application development — main/renderer process architecture, IPC communication, native OS APIs (menus, tray, notifications, dialogs), auto-updates, code signing, packaging with electron-builder/forge, security hardening (contextIsolation, sandbox), and performance optimization. Use for building cross-platform desktop apps.
npx skillsauth add oimiragieo/agent-studio electron-proInstall this skill globally with one command. Works with Claude Code, Cursor, and Windsurf.
3 of 9 scanners reported clean
Some scanners were skipped, did not run, or reported a non-clean status. Review each row below.
Full-stack Electron desktop app development — from architecture through distribution. Covers process model, security hardening, native OS integration, IPC patterns, packaging, and auto-update.
┌─────────────────────────────────────┐
│ Main Process │
│ (Node.js — full system access) │
│ app, BrowserWindow, Menu, Tray │
│ nativeImage, shell, ipcMain │
└──────────────┬──────────────────────┘
│ IPC (structured clone)
┌──────────────┴──────────────────────┐
│ Renderer Process │
│ (Chromium — sandboxed by default) │
│ Web UI: React/Vue/Svelte/vanilla │
│ ipcRenderer (via contextBridge) │
└─────────────────────────────────────┘
│ contextBridge
┌──────────────┴──────────────────────┐
│ Preload Script │
│ Bridge between main and renderer │
│ Exposes safe APIs via contextBridge│
└─────────────────────────────────────┘
// main.js — Always use these security options
const win = new BrowserWindow({
webPreferences: {
preload: path.join(__dirname, 'preload.js'),
contextIsolation: true, // REQUIRED: isolates renderer from preload
sandbox: true, // RECOMMENDED: OS-level sandbox
nodeIntegration: false, // REQUIRED: never expose Node to renderer
webSecurity: true, // REQUIRED: never disable
allowRunningInsecureContent: false,
},
});
// NEVER do this — security violation:
// nodeIntegration: true
// contextIsolation: false
// Use remote: require('@electron/remote') only if absolutely necessary
// preload.js — the ONLY safe bridge to Node
const { contextBridge, ipcRenderer } = require('electron');
contextBridge.exposeInMainWorld('electronAPI', {
// Expose specific, validated methods only
openFile: () => ipcRenderer.invoke('dialog:openFile'),
saveFile: content => {
if (typeof content !== 'string') throw new Error('Invalid content');
return ipcRenderer.invoke('dialog:saveFile', content);
},
onUpdateAvailable: callback => {
// One-way: main → renderer
const listener = (_, data) => callback(data);
ipcRenderer.on('update-available', listener);
return () => ipcRenderer.removeListener('update-available', listener);
},
// Platform info (read-only)
platform: process.platform,
});
// renderer.js — use the exposed API
window.electronAPI.openFile().then(filePath => {
console.log('Selected:', filePath);
});
// main.js — handle IPC calls
const { ipcMain, dialog, app } = require('electron');
// Two-way: renderer calls, main responds
ipcMain.handle('dialog:openFile', async () => {
const { canceled, filePaths } = await dialog.showOpenDialog({
properties: ['openFile'],
filters: [{ name: 'JSON', extensions: ['json'] }],
});
if (canceled) return null;
return filePaths[0];
});
// Validate inputs — never trust renderer
ipcMain.handle('fs:readFile', async (event, filePath) => {
// Validate path is within allowed directories
const allowed = path.join(app.getPath('userData'), 'files');
const resolved = path.resolve(filePath);
if (!resolved.startsWith(allowed)) {
throw new Error('Path traversal denied');
}
return fs.promises.readFile(resolved, 'utf8');
});
// One-way: main → renderer push
win.webContents.send('update-available', { version: '1.2.0' });
// One-way: renderer → main fire-and-forget
// preload: ipcRenderer.send('log', message)
// main: ipcMain.on('log', (event, message) => console.log(message))
const { Menu, MenuItem } = require('electron');
const template = [
{
label: 'File',
submenu: [
{ label: 'New', accelerator: 'CmdOrCtrl+N', click: () => createWindow() },
{ label: 'Open', accelerator: 'CmdOrCtrl+O', click: () => openFileDialog() },
{ type: 'separator' },
{ role: 'quit' },
],
},
{
label: 'Edit',
submenu: [
{ role: 'undo' },
{ role: 'redo' },
{ type: 'separator' },
{ role: 'cut' },
{ role: 'copy' },
{ role: 'paste' },
],
},
// macOS: add app menu as first item
...(process.platform === 'darwin'
? [
{
label: app.name,
submenu: [{ role: 'about' }, { type: 'separator' }, { role: 'hide' }, { role: 'quit' }],
},
]
: []),
];
Menu.setApplicationMenu(Menu.buildFromTemplate(template));
const { Tray, Menu, nativeImage } = require('electron');
const icon = nativeImage.createFromPath(path.join(__dirname, 'assets/tray-icon.png'));
const tray = new Tray(icon);
tray.setToolTip('My App');
tray.setContextMenu(
Menu.buildFromTemplate([
{ label: 'Open', click: () => win.show() },
{ label: 'Quit', click: () => app.quit() },
])
);
tray.on('click', () => (win.isVisible() ? win.hide() : win.show()));
const { Notification } = require('electron');
// Check support first
if (Notification.isSupported()) {
new Notification({
title: 'Build Complete',
body: 'Your project compiled successfully.',
icon: path.join(__dirname, 'assets/icon.png'),
}).show();
}
const { dialog } = require('electron');
// Open file
const { filePaths } = await dialog.showOpenDialog(win, {
title: 'Select Config File',
filters: [{ name: 'JSON', extensions: ['json'] }],
properties: ['openFile'],
});
// Save file
const { filePath } = await dialog.showSaveDialog(win, {
defaultPath: 'export.csv',
filters: [{ name: 'CSV', extensions: ['csv'] }],
});
// Message box
const { response } = await dialog.showMessageBox(win, {
type: 'question',
buttons: ['Yes', 'No'],
message: 'Are you sure you want to delete this?',
});
const { shell } = require('electron');
// Open in default browser/app — safe for user-initiated actions
await shell.openExternal('https://example.com');
// Open file in default app
await shell.openPath('/path/to/file.pdf');
// Reveal in Finder/Explorer
shell.showItemInFolder('/path/to/file');
const { app, BrowserWindow } = require('electron');
let mainWindow = null;
function createWindow() {
mainWindow = new BrowserWindow({
width: 1200,
height: 800,
show: false, // Wait for ready-to-show to avoid flash
webPreferences: {
preload: path.join(__dirname, 'preload.js'),
contextIsolation: true,
nodeIntegration: false,
},
});
mainWindow.loadFile('index.html');
// Or for dev server: mainWindow.loadURL('http://localhost:5173');
mainWindow.once('ready-to-show', () => mainWindow.show());
mainWindow.on('closed', () => {
mainWindow = null;
});
}
app.whenReady().then(() => {
createWindow();
// macOS: re-create on activate if no windows
app.on('activate', () => {
if (BrowserWindow.getAllWindows().length === 0) createWindow();
});
});
// Quit on all windows closed (except macOS)
app.on('window-all-closed', () => {
if (process.platform !== 'darwin') app.quit();
});
const { autoUpdater } = require('electron-updater');
autoUpdater.checkForUpdatesAndNotify();
autoUpdater.on('update-available', info => {
win.webContents.send('update-available', info);
});
autoUpdater.on('update-downloaded', info => {
win.webContents.send('update-downloaded', info);
});
// Triggered by renderer when user clicks "Install"
ipcMain.handle('install-update', () => {
autoUpdater.quitAndInstall();
});
// package.json
{
"build": {
"appId": "com.company.myapp",
"productName": "My App",
"directories": { "output": "dist" },
"mac": {
"category": "public.app-category.productivity",
"hardenedRuntime": true,
"entitlements": "build/entitlements.mac.plist",
"notarize": true
},
"win": {
"target": ["nsis", "portable"],
"signingHashAlgorithms": ["sha256"]
},
"linux": {
"target": ["AppImage", "deb"],
"category": "Utility"
},
"publish": {
"provider": "github",
"owner": "your-org",
"repo": "your-repo"
}
}
}
# Build for current platform
pnpm exec electron-builder
# Build for all platforms (requires cross-platform CI)
pnpm exec electron-builder --mac --win --linux
// Register protocol
app.setAsDefaultProtocolClient('myapp');
// Handle on macOS/Linux
app.on('open-url', (event, url) => {
event.preventDefault();
handleDeepLink(url);
});
// Handle on Windows (second instance)
app.on('second-instance', (event, commandLine) => {
const url = commandLine.find(arg => arg.startsWith('myapp://'));
if (url) handleDeepLink(url);
mainWindow?.focus();
});
// Single instance lock
const gotLock = app.requestSingleInstanceLock();
if (!gotLock) app.quit();
// Lazy load windows
let settingsWindow = null;
function openSettings() {
if (settingsWindow) { settingsWindow.focus(); return; }
settingsWindow = new BrowserWindow({ ... });
settingsWindow.on('closed', () => { settingsWindow = null; });
}
// Background processing — use utility process (Electron 22+)
const { utilityProcess } = require('electron');
const child = utilityProcess.fork(path.join(__dirname, 'worker.js'));
child.postMessage({ task: 'process', data });
child.on('message', ({ result }) => console.log(result));
// Session — block unnecessary requests
win.webContents.session.webRequest.onBeforeRequest(
{ urls: ['https://tracking.example.com/*'] },
(details, callback) => callback({ cancel: true })
);
# Start dev with hot reload (electron-vite recommended)
pnpm exec electron-vite dev
# Or with webpack/parcel
concurrently "pnpm build:renderer --watch" "wait-on http://localhost:5173 && electron ."
# Open DevTools programmatically (dev only)
if (process.env.NODE_ENV === 'development') {
win.webContents.openDevTools();
}
# Debug main process
electron --inspect=9229 .
# Then attach Chrome DevTools at chrome://inspect
nodeIntegration: true — exposes all of Node.js to web content (RCE vector)contextIsolation: false — allows renderer to access preload scope directlywebSecurity: false — disables CORS and mixed content protectionsshell.openExternal(userInput) without validation — SSRF/open redirect vectoreval() or Function() in renderer — CSP bypassremote module — deprecated, insecure, causes memory leakscontextIsolation: true on all windowsnodeIntegration: false on all windowssandbox: true enabledshell.openExternal(untrustedUrl) without validationwebSecurity: true (default, do not disable)tools
Comprehensive biosignal processing toolkit for analyzing physiological data including ECG, EEG, EDA, RSP, PPG, EMG, and EOG signals. Use this skill when processing cardiovascular signals, brain activity, electrodermal responses, respiratory patterns, muscle activity, or eye movements. Applicable for heart rate variability analysis, event-related potentials, complexity measures, autonomic nervous system assessment, psychophysiology research, and multi-modal physiological signal integration.
tools
Comprehensive toolkit for creating, analyzing, and visualizing complex networks and graphs in Python. Use when working with network/graph data structures, analyzing relationships between entities, computing graph algorithms (shortest paths, centrality, clustering), detecting communities, generating synthetic networks, or visualizing network topologies. Applicable to social networks, biological networks, transportation systems, citation networks, and any domain involving pairwise relationships.
data-ai
Molecular featurization for ML (100+ featurizers). ECFP, MACCS, descriptors, pretrained models (ChemBERTa), convert SMILES to features, for QSAR and molecular ML.
development
Run Python code in the cloud with serverless containers, GPUs, and autoscaling. Use when deploying ML models, running batch processing jobs, scheduling compute-intensive tasks, or serving APIs that require GPU acceleration or dynamic scaling.