Skip to content
Back to skills

Electron Api

ASecurity

Guide for adding new Electron APIs to Wave Terminal. Use when implementing new frontend-to-electron communications via preload/IPC.

  • 42 stars
  • 0 votes
  • 0 copies
  • 2 views
  • Added May 26, 2026
developmenttypescriptshellapidatabasefrontendbackend

Works with

  • terminal
  • api

Security analysis

A100/100

Scanned May 27, 2026

npx -y skills add mits-pl/wove --skill electron-api --agent claude-code

Installs into .claude/skills of the current project.

Are you the author of Electron Api?

Add the live security badge to your README. It updates with every re-scan.

Security grade badge for Electron Api
[![Security: A — Skills Directory](https://www.skillsdirectory.com/api/skills/mits-pl-electron-api/badge)](https://www.skillsdirectory.com/skills/mits-pl-electron-api)

More formats (shields.io, HTML) on the badges page. Keep it an A: scan every change in CI with Pro.

Download with Pro
SKILL.md
---
name: electron-api
description: Guide for adding new Electron APIs to Wave Terminal. Use when implementing new frontend-to-electron communications via preload/IPC.
---

# Adding Electron APIs

Electron APIs allow the frontend to call Electron main process functionality directly via IPC.

## Four Files to Edit

1. [`frontend/types/custom.d.ts`](frontend/types/custom.d.ts) - TypeScript [`ElectronApi`](frontend/types/custom.d.ts:82) type
2. [`emain/preload.ts`](emain/preload.ts) - Expose method via `contextBridge`
3. [`emain/emain-ipc.ts`](emain/emain-ipc.ts) - Implement IPC handler
4. [`frontend/preview/preview-electron-api.ts`](frontend/preview/preview-electron-api.ts) - Add a no-op stub to keep the `previewElectronApi` object in sync with the `ElectronApi` type

## Three Communication Patterns

1. **Sync** - `ipcRenderer.sendSync()` + `ipcMain.on()` + `event.returnValue = ...`
2. **Async** - `ipcRenderer.invoke()` + `ipcMain.handle()`
3. **Fire-and-forget** - `ipcRenderer.send()` + `ipcMain.on()`

## Example: Async Method

### 1. Define TypeScript Interface

In [`frontend/types/custom.d.ts`](frontend/types/custom.d.ts):

```typescript
type ElectronApi = {
    captureScreenshot: (rect: Electron.Rectangle) => Promise<string>; // capture-screenshot
};
```

### 2. Expose in Preload

In [`emain/preload.ts`](emain/preload.ts):

```typescript
contextBridge.exposeInMainWorld("api", {
    captureScreenshot: (rect: Rectangle) => ipcRenderer.invoke("capture-screenshot", rect),
});
```

### 3. Implement Handler

In [`emain/emain-ipc.ts`](emain/emain-ipc.ts):

```typescript
electron.ipcMain.handle("capture-screenshot", async (event, rect) => {
    const tabView = getWaveTabViewByWebContentsId(event.sender.id);
    if (!tabView) throw new Error("No tab view found");
    const image = await tabView.webContents.capturePage(rect);
    return `data:image/png;base64,${image.toPNG().toString("base64")}`;
});
```

### 4. Add Preview Stub

In [`frontend/preview/preview-electron-api.ts`](frontend/preview/preview-electron-api.ts):

```typescript
captureScreenshot: (_rect: Electron.Rectangle) => Promise.resolve(""),
```

### 5. Call from Frontend

```typescript
import { getApi } from "@/store/global";

const dataUrl = await getApi().captureScreenshot({ x: 0, y: 0, width: 800, height: 600 });
```

## Example: Sync Method

### 1. Define

```typescript
type ElectronApi = {
    getUserName: () => string; // get-user-name
};
```

### 2. Preload

```typescript
getUserName: () => ipcRenderer.sendSync("get-user-name"),
```

### 3. Handler (⚠️ MUST set event.returnValue or browser hangs)

```typescript
electron.ipcMain.on("get-user-name", (event) => {
    event.returnValue = process.env.USER || "unknown";
});
```

### 4. Call

```typescript
import { getApi } from "@/store/global";

const userName = getApi().getUserName(); // blocks until returns
```

## Example: Fire-and-Forget

### 1. Define

```typescript
type ElectronApi = {
    openExternal: (url: string) => void; // open-external
};
```

### 2. Preload

```typescript
openExternal: (url) => ipcRenderer.send("open-external", url),
```

### 3. Handler

```typescript
electron.ipcMain.on("open-external", (event, url) => {
    electron.shell.openExternal(url);
});
```

## Example: Event Listener

### 1. Define

```typescript
type ElectronApi = {
    onZoomFactorChange: (callback: (zoomFactor: number) => void) => void; // zoom-factor-change
};
```

### 2. Preload

```typescript
onZoomFactorChange: (callback) => 
    ipcRenderer.on("zoom-factor-change", (_event, zoomFactor) => callback(zoomFactor)),
```

### 3. Send from Main

```typescript
webContents.send("zoom-factor-change", newZoomFactor);
```

## Quick Reference

**Use Sync when:**
- Getting config/env vars
- Quick lookups, no I/O
- ⚠️ **CRITICAL**: Always set `event.returnValue` or browser hangs

**Use Async when:**
- File operations
- Network requests
- Can fail or take time

**Use Fire-and-forget when:**
- No return value needed
- Triggering actions

**Electron API vs RPC:**
- Electron API: Native OS features, window management, Electron APIs
- RPC: Database, backend logic, remote servers

## Checklist

- [ ] Add to [`ElectronApi`](frontend/types/custom.d.ts:82) in [`custom.d.ts`](frontend/types/custom.d.ts)
- [ ] Include IPC channel name in comment
- [ ] Expose in [`preload.ts`](emain/preload.ts)
- [ ] Implement in [`emain-ipc.ts`](emain/emain-ipc.ts)
- [ ] Add no-op stub to [`preview-electron-api.ts`](frontend/preview/preview-electron-api.ts)
- [ ] IPC channel names match exactly
- [ ] **For sync**: Set `event.returnValue` (or browser hangs!)
- [ ] Test end-to-end

Attribution

Is this your skill, or is something wrong with this listing? Request removal or report an issue. Author removals are honored within 72 hours.

Comments

Loading comments…

Newsletter

Get the weekly list of safe new skills, and the ones we pulled.