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.md
safety · 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

  1. frontend/types/custom.d.ts
    - TypeScript
    ElectronApi
    type
  2. emain/preload.ts
    - Expose method via
    contextBridge
  3. emain/emain-ipc.ts
    - Implement IPC handler
  4. 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
:

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

In

emain/emain-ipc.ts
:

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
    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