<< All versions
Skill v1.0.1
currentAutomated scan100/100travisjneuman/.claude/electron-desktop
3 files
──Details
PublishedJune 18, 2026 at 01:57 AM
Content Hashsha256:85e67151cb97c0c4...
Git SHA82fe38536409
Bump Typepatch
──Files
Files (1 file, 10.5 KB)
SKILL.md10.5 KBactive
SKILL.md · 494 lines · 10.5 KB
version: "1.0.1" name: electron-desktop description: Desktop application development with Electron for Windows, macOS, and Linux. Use when building cross-platform desktop apps, implementing native OS features, or packaging web apps for desktop.
Electron Desktop Development
Build cross-platform desktop applications using web technologies.
Platforms Supported
| Platform | Architecture | Notes | |
|---|---|---|---|
| Windows | x64, arm64, ia32 | Windows 10+ | |
| macOS | x64, arm64 (Apple Silicon) | macOS 10.15+ | |
| Linux | x64, arm64, armv7l | Most distributions |
Project Structure
my-app/├── src/│ ├── main/│ │ ├── main.ts # Main process│ │ ├── preload.ts # Preload scripts│ │ └── ipc.ts # IPC handlers│ ├── renderer/│ │ ├── index.html│ │ ├── App.tsx│ │ └── components/│ └── shared/│ └── types.ts├── resources/│ ├── icon.icns # macOS│ ├── icon.ico # Windows│ └── icon.png # Linux├── electron-builder.yml├── package.json└── forge.config.ts
Main Process
Entry Point
typescript
// src/main/main.tsimport { app, BrowserWindow, ipcMain } from "electron";import path from "path";let mainWindow: BrowserWindow | null = null;function createWindow() {mainWindow = new BrowserWindow({width: 1200,height: 800,minWidth: 800,minHeight: 600,webPreferences: {preload: path.join(__dirname, "preload.js"),contextIsolation: true,nodeIntegration: false,sandbox: true,},titleBarStyle: "hiddenInset", // macOSframe: process.platform === "darwin", // Windows/Linux custom frameshow: false, // Show when ready});// Load the appif (process.env.NODE_ENV === "development") {mainWindow.loadURL("http://localhost:5173");mainWindow.webContents.openDevTools();} else {mainWindow.loadFile(path.join(__dirname, "../renderer/index.html"));}// Show when ready to prevent flashmainWindow.once("ready-to-show", () => {mainWindow?.show();});mainWindow.on("closed", () => {mainWindow = null;});}app.whenReady().then(createWindow);app.on("window-all-closed", () => {if (process.platform !== "darwin") {app.quit();}});app.on("activate", () => {if (BrowserWindow.getAllWindows().length === 0) {createWindow();}});
Preload Script (Security Bridge)
typescript
// src/main/preload.tsimport { contextBridge, ipcRenderer } from "electron";// Expose safe APIs to renderercontextBridge.exposeInMainWorld("electronAPI", {// File operationsopenFile: () => ipcRenderer.invoke("dialog:openFile"),saveFile: (content: string) => ipcRenderer.invoke("dialog:saveFile", content),// App infogetVersion: () => ipcRenderer.invoke("app:getVersion"),// Window controlsminimize: () => ipcRenderer.send("window:minimize"),maximize: () => ipcRenderer.send("window:maximize"),close: () => ipcRenderer.send("window:close"),// Two-way communicationonUpdateAvailable: (callback: () => void) => {ipcRenderer.on("update:available", callback);return () => ipcRenderer.removeListener("update:available", callback);},});// Type definitions for rendererdeclare global {interface Window {electronAPI: {openFile: () => Promise<string | null>;saveFile: (content: string) => Promise<boolean>;getVersion: () => Promise<string>;minimize: () => void;maximize: () => void;close: () => void;onUpdateAvailable: (callback: () => void) => () => void;};}}
IPC Handlers
typescript
// src/main/ipc.tsimport { ipcMain, dialog, BrowserWindow } from "electron";import fs from "fs/promises";export function setupIPC() {// File dialogsipcMain.handle("dialog:openFile", async () => {const { canceled, filePaths } = await dialog.showOpenDialog({properties: ["openFile"],filters: [{ name: "Text Files", extensions: ["txt", "md"] },{ name: "All Files", extensions: ["*"] },],});if (canceled || filePaths.length === 0) return null;return fs.readFile(filePaths[0], "utf-8");});ipcMain.handle("dialog:saveFile", async (_, content: string) => {const { canceled, filePath } = await dialog.showSaveDialog({filters: [{ name: "Text Files", extensions: ["txt"] }],});if (canceled || !filePath) return false;await fs.writeFile(filePath, content);return true;});// Window controlsipcMain.on("window:minimize", (event) => {BrowserWindow.fromWebContents(event.sender)?.minimize();});ipcMain.on("window:maximize", (event) => {const win = BrowserWindow.fromWebContents(event.sender);if (win?.isMaximized()) {win.unmaximize();} else {win?.maximize();}});ipcMain.on("window:close", (event) => {BrowserWindow.fromWebContents(event.sender)?.close();});}
Renderer Process (React)
Using Exposed APIs
tsx
// src/renderer/App.tsximport { useState, useEffect } from "react";function App() {const [version, setVersion] = useState("");const [content, setContent] = useState("");useEffect(() => {window.electronAPI.getVersion().then(setVersion);const unsubscribe = window.electronAPI.onUpdateAvailable(() => {alert("Update available!");});return unsubscribe;}, []);const handleOpen = async () => {const fileContent = await window.electronAPI.openFile();if (fileContent) {setContent(fileContent);}};const handleSave = async () => {await window.electronAPI.saveFile(content);};return (<div className="app"><header className="titlebar"><span>My App v{version}</span><div className="window-controls"><button onClick={window.electronAPI.minimize}>−</button><button onClick={window.electronAPI.maximize}>□</button><button onClick={window.electronAPI.close}>×</button></div></header><main><button onClick={handleOpen}>Open File</button><textareavalue={content}onChange={(e) => setContent(e.target.value)}/><button onClick={handleSave}>Save File</button></main></div>);}
Custom Title Bar CSS
css
.titlebar {-webkit-app-region: drag; /* Make draggable */height: 32px;display: flex;justify-content: space-between;align-items: center;padding: 0 16px;background: #1e1e1e;color: white;}.window-controls {-webkit-app-region: no-drag; /* Buttons clickable */display: flex;gap: 8px;}.window-controls button {width: 32px;height: 32px;border: none;background: transparent;color: white;cursor: pointer;}.window-controls button:hover {background: rgba(255, 255, 255, 0.1);}
Native Features
System Tray
typescript
import { Tray, Menu, nativeImage } from "electron";let tray: Tray | null = null;function createTray() {const icon = nativeImage.createFromPath("resources/tray-icon.png");tray = new Tray(icon.resize({ width: 16, height: 16 }));const contextMenu = Menu.buildFromTemplate([{ label: "Show App", click: () => mainWindow?.show() },{ type: "separator" },{ label: "Quit", click: () => app.quit() },]);tray.setToolTip("My App");tray.setContextMenu(contextMenu);tray.on("click", () => {mainWindow?.show();});}
Native Menus
typescript
import { Menu } from "electron";const template: Electron.MenuItemConstructorOptions[] = [{label: "File",submenu: [{label: "Open",accelerator: "CmdOrCtrl+O",click: () => {/* handle */},},{label: "Save",accelerator: "CmdOrCtrl+S",click: () => {/* handle */},},{ type: "separator" },{ role: "quit" },],},{label: "Edit",submenu: [{ role: "undo" },{ role: "redo" },{ type: "separator" },{ role: "cut" },{ role: "copy" },{ role: "paste" },],},];Menu.setApplicationMenu(Menu.buildFromTemplate(template));
Notifications
typescript
import { Notification } from "electron";new Notification({title: "Update Available",body: "A new version is ready to install.",icon: "resources/icon.png",}).show();
Auto Updates
typescript
import { autoUpdater } from "electron-updater";autoUpdater.checkForUpdatesAndNotify();autoUpdater.on("update-available", () => {mainWindow?.webContents.send("update:available");});autoUpdater.on("update-downloaded", () => {autoUpdater.quitAndInstall();});
Building & Distribution
electron-builder Configuration
yaml
# electron-builder.ymlappId: com.mycompany.myappproductName: My Appcopyright: Copyright © 2025directories:output: distbuildResources: resourcesfiles:- dist/**/*- package.jsonmac:category: public.app-category.developer-toolstarget:- target: dmgarch: [x64, arm64]- target: ziparch: [x64, arm64]hardenedRuntime: truegatekeeperAssess: falseentitlements: build/entitlements.mac.plistnotarize: truewin:target:- target: nsisarch: [x64]sign: truelinux:target:- target: AppImage- target: deb- target: rpmcategory: Developmentpublish:provider: githubreleaseType: release
Build Commands
bash
# Developmentnpm run dev# Build for current platformnpm run build# Build for all platformsnpm run build:all# Build for specific platformnpm run build:macnpm run build:winnpm run build:linux
Security Best Practices
DO:
- Always use
contextIsolation: true - Use preload scripts for IPC
- Validate all IPC inputs
- Enable
sandbox: true - Sign and notarize for distribution
DON'T:
- Enable
nodeIntegration - Use
remotemodule - Load untrusted content
- Expose full Node.js APIs
- Skip code signing
Alternatives to Consider
| Framework | Best For | |
|---|---|---|
| Tauri | Smaller bundle, Rust backend | |
| Neutralino | Lightweight, system webview | |
| Electron | Full Node.js, mature ecosystem |