Guide for adding new Electron APIs to Wave Terminal. Use when implementing new frontend-to-electron communications via preload/IPC.
Electron APIs allow the frontend to call Electron main process functionality directly via IPC.
frontend/types/custom.d.ts - TypeScript ElectronApi typeemain/preload.ts - Expose method via contextBridgeemain/emain-ipc.ts - Implement IPC handlerfrontend/preview/preview-electron-api.tspreviewElectronApiElectronApiipcRenderer.sendSync() + ipcMain.on() + event.returnValue = ...ipcRenderer.invoke() + ipcMain.handle()ipcRenderer.send() + ipcMain.on()type ElectronApi = {
captureScreenshot: (rect: Electron.Rectangle) => Promise<string>; // capture-screenshot
};
In emain/preload.ts:
contextBridge.exposeInMainWorld("api", {
captureScreenshot: (rect: Rectangle) => ipcRenderer.invoke("capture-screenshot", rect),
});
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")}`;
});
captureScreenshot: (_rect: Electron.Rectangle) => Promise.resolve(""),
import { getApi } from "@/store/global";
const dataUrl = await getApi().captureScreenshot({ x: 0, y: 0, width: 800, height: 600 });
type ElectronApi = {
getUserName: () => string; // get-user-name
};
getUserName: () => ipcRenderer.sendSync("get-user-name"),
electron.ipcMain.on("get-user-name", (event) => {
event.returnValue = process.env.USER || "unknown";
});
import { getApi } from "@/store/global";
const userName = getApi().getUserName(); // blocks until returns
type ElectronApi = {
openExternal: (url: string) => void; // open-external
};
openExternal: (url) => ipcRenderer.send("open-external", url),
electron.ipcMain.on("open-external", (event, url) => {
electron.shell.openExternal(url);
});
type ElectronApi = {
onZoomFactorChange: (callback: (zoomFactor: number) => void) => void; // zoom-factor-change
};
onZoomFactorChange: (callback) =>
ipcRenderer.on("zoom-factor-change", (_event, zoomFactor) => callback(zoomFactor)),
webContents.send("zoom-factor-change", newZoomFactor);
Use Sync when:
event.returnValue or browser hangsUse Async when:
Use Fire-and-forget when:
Electron API vs RPC:
ElectronApi in custom.d.tspreload.tsemain-ipc.tspreview-electron-api.tsevent.returnValue (or browser hangs!)