Waveterm electron-api
Guide for adding new Electron APIs to Wave Terminal. Use when implementing new frontend-to-electron communications via preload/IPC.
install
source · Clone the upstream repo
git clone https://github.com/wavetermdev/waveterm
Claude Code · Install into ~/.claude/skills/
T=$(mktemp -d) && git clone --depth=1 https://github.com/wavetermdev/waveterm "$T" && mkdir -p ~/.claude/skills && cp -r "$T/.kilocode/skills/electron-api" ~/.claude/skills/wavetermdev-waveterm-electron-api && rm -rf "$T"
manifest:
.kilocode/skills/electron-api/SKILL.mdsafety · automated scan (low risk)
This is a pattern-based risk scan, not a security review. Our crawler flagged:
- references .env files
Always read a skill's source content before installing. Patterns alone don't mean the skill is malicious — but they warrant attention.
source content
Adding Electron APIs
Electron APIs allow the frontend to call Electron main process functionality directly via IPC.
Four Files to Edit
- TypeScriptfrontend/types/custom.d.ts
typeElectronApi
- Expose method viaemain/preload.tscontextBridge
- Implement IPC handleremain/emain-ipc.ts
- Add a no-op stub to keep thefrontend/preview/preview-electron-api.ts
object in sync with thepreviewElectronApi
typeElectronApi
Three Communication Patterns
- Sync -
+ipcRenderer.sendSync()
+ipcMain.on()event.returnValue = ... - Async -
+ipcRenderer.invoke()ipcMain.handle() - Fire-and-forget -
+ipcRenderer.send()ipcMain.on()
Example: Async Method
1. Define TypeScript Interface
In
:frontend/types/custom.d.ts
type ElectronApi = { captureScreenshot: (rect: Electron.Rectangle) => Promise<string>; // capture-screenshot };
2. Expose in Preload
In
:emain/preload.ts
contextBridge.exposeInMainWorld("api", { captureScreenshot: (rect: Rectangle) => ipcRenderer.invoke("capture-screenshot", rect), });
3. Implement Handler
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
captureScreenshot: (_rect: Electron.Rectangle) => Promise.resolve(""),
5. Call from Frontend
import { getApi } from "@/store/global"; const dataUrl = await getApi().captureScreenshot({ x: 0, y: 0, width: 800, height: 600 });
Example: Sync Method
1. Define
type ElectronApi = { getUserName: () => string; // get-user-name };
2. Preload
getUserName: () => ipcRenderer.sendSync("get-user-name"),
3. Handler (⚠️ MUST set event.returnValue or browser hangs)
electron.ipcMain.on("get-user-name", (event) => { event.returnValue = process.env.USER || "unknown"; });
4. Call
import { getApi } from "@/store/global"; const userName = getApi().getUserName(); // blocks until returns
Example: Fire-and-Forget
1. Define
type ElectronApi = { openExternal: (url: string) => void; // open-external };
2. Preload
openExternal: (url) => ipcRenderer.send("open-external", url),
3. Handler
electron.ipcMain.on("open-external", (event, url) => { electron.shell.openExternal(url); });
Example: Event Listener
1. Define
type ElectronApi = { onZoomFactorChange: (callback: (zoomFactor: number) => void) => void; // zoom-factor-change };
2. Preload
onZoomFactorChange: (callback) => ipcRenderer.on("zoom-factor-change", (_event, zoomFactor) => callback(zoomFactor)),
3. Send from Main
webContents.send("zoom-factor-change", newZoomFactor);
Quick Reference
Use Sync when:
- Getting config/env vars
- Quick lookups, no I/O
- ⚠️ CRITICAL: Always set
or browser hangsevent.returnValue
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
inElectronApicustom.d.ts - Include IPC channel name in comment
- Expose in
preload.ts - Implement in
emain-ipc.ts - Add no-op stub to
preview-electron-api.ts - IPC channel names match exactly
- For sync: Set
(or browser hangs!)event.returnValue - Test end-to-end