legal-compliance
legal compliance
electron-desktop
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.
Full skill instructions
Build cross-platform desktop applications using web technologies.
| Platform | Architecture | Notes |
|---|---|---|
| Windows | x64, arm64, ia32 | Windows 10+ |
| macOS | x64, arm64 (Apple Silicon) | macOS 10.15+ |
| Linux | x64, arm64, armv7l | Most distributions |
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
// src/main/main.ts
import { 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", // macOS
frame: process.platform === "darwin", // Windows/Linux custom frame
show: false, // Show when ready
});
// Load the app
if (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 flash
mainWindow.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();
}
});
// src/main/preload.ts
import { contextBridge, ipcRenderer } from "electron";
// Expose safe APIs to renderer
contextBridge.exposeInMainWorld("electronAPI", {
// File operations
openFile: () => ipcRenderer.invoke("dialog:openFile"),
saveFile: (content: string) => ipcRenderer.invoke("dialog:saveFile", content),
// App info
getVersion: () => ipcRenderer.invoke("app:getVersion"),
// Window controls
minimize: () => ipcRenderer.send("window:minimize"),
maximize: () => ipcRenderer.send("window:maximize"),
close: () => ipcRenderer.send("window:close"),
// Two-way communication
onUpdateAvailable: (callback: () => void) => {
ipcRenderer.on("update:available", callback);
return () => ipcRenderer.removeListener("update:available", callback);
},
});
// Type definitions for renderer
declare 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;
};
}
}
// src/main/ipc.ts
import { ipcMain, dialog, BrowserWindow } from "electron";
import fs from "fs/promises";
export function setupIPC() {
// File dialogs
ipcMain.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 controls
ipcMain.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();
});
}
// src/renderer/App.tsx
import { 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>
<textarea
value={content}
onChange={(e) => setContent(e.target.value)}
/>
<button onClick={handleSave}>Save File</button>
</main>
</div>
);
}
.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);
}
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();
});
}
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));
import { Notification } from "electron";
new Notification({
title: "Update Available",
body: "A new version is ready to install.",
icon: "resources/icon.png",
}).show();
import { autoUpdater } from "electron-updater";
autoUpdater.checkForUpdatesAndNotify();
autoUpdater.on("update-available", () => {
mainWindow?.webContents.send("update:available");
});
autoUpdater.on("update-downloaded", () => {
autoUpdater.quitAndInstall();
});
# electron-builder.yml
appId: com.mycompany.myapp
productName: My App
copyright: Copyright © 2025
directories:
output: dist
buildResources: resources
files:
- dist/**/*
- package.json
mac:
category: public.app-category.developer-tools
target:
- target: dmg
arch: [x64, arm64]
- target: zip
arch: [x64, arm64]
hardenedRuntime: true
gatekeeperAssess: false
entitlements: build/entitlements.mac.plist
notarize: true
win:
target:
- target: nsis
arch: [x64]
sign: true
linux:
target:
- target: AppImage
- target: deb
- target: rpm
category: Development
publish:
provider: github
releaseType: release
# Development
npm run dev
# Build for current platform
npm run build
# Build for all platforms
npm run build:all
# Build for specific platform
npm run build:mac
npm run build:win
npm run build:linux
contextIsolation: truesandbox: truenodeIntegrationremote module| Framework | Best For |
|---|---|
| Tauri | Smaller bundle, Rust backend |
| Neutralino | Lightweight, system webview |
| Electron | Full Node.js, mature ecosystem |
legal compliance
graphic design
travel planner
course material creator
supply chain optimizer
macos native
real estate analyzer
career path planner
database expert
generic react ux designer
audio production
investment memo generator
Generate, edit, and beat-sync AI video with leading models in one workspace.
The world's fastest calendar for remote work
Transform Your Design with AI Designer by ImgCreator.ai
Revolutionizing Video Production with AI-Powered Creativity
Extend an image past the frame and let AI fill the new aspect ratio.
Discover your celebrity doppelgänger with StarByFace!
ChainClarity explains 700+ crypto whitepapers in plain English, with layered summaries, comparisons, research tools, alerts, and a $4.99 Pro plan.
Opus.ai: Revolutionize Your Web Experience