/** * Typed access to the Rust backend. * * `bindings.ts` is generated by tauri-specta; never edit it. Use `call` to turn * its `{status, data | error}` results into plain promises that reject with * `IpcError`, and `onIdeEvent` to subscribe to the backend event bus. */ import type { UnlistenFn } from "@tauri-apps/api/event"; import { getCurrentWebviewWindow } from "@tauri-apps/api/webviewWindow"; import { commands, type IdeError, type IdeEvent, type RequestId } from "@/bindings"; export { commands }; export type * from "@/bindings"; export class IpcError extends Error { readonly kind: IdeError["kind"]; constructor(error: IdeError) { super(error.message); this.name = "IpcError"; this.kind = error.kind; } } type Result = { status: "ok"; data: T } | { status: "error"; error: IdeError }; /** Unwraps a fallible command result: `await call(commands.docOpen(path))`. */ export async function call(result: Promise>): Promise { const r = await result; if (r.status === "error") throw new IpcError(r.error); return r.data; } export function isCancelled(e: unknown): boolean { return e instanceof IpcError && e.kind === "cancelled"; } let nextId = 1; /** Allocates an id for a cancellable request (see `commands.cancelRequest`). */ export function nextRequestId(): RequestId { return nextId++ as RequestId; } export const IDE_EVENT = "ide://event"; type EventOf = Extract; type Handler = (event: IdeEvent) => void; const handlers = new Map>(); let unlisten: Promise | null = null; function ensureListening() { if (unlisten || !isTauri()) return; // Window-scoped: a global `listen` also receives events emitted to other windows. unlisten = getCurrentWebviewWindow().listen(IDE_EVENT, ({ payload }) => { handlers.get(payload.type)?.forEach((h) => h(payload)); handlers.get("*")?.forEach((h) => h(payload)); }); } /** Subscribes to one backend event type (or `"*"`); returns an unsubscribe function. */ export function onIdeEvent(type: K | "*", handler: (event: EventOf) => void): () => void { ensureListening(); let set = handlers.get(type); if (!set) handlers.set(type, (set = new Set())); const h = handler as Handler; set.add(h); return () => set.delete(h); } /** Test helper: dispatch an event as if it came from the backend. */ export function emitLocalIdeEvent(event: IdeEvent) { handlers.get(event.type)?.forEach((h) => h(event)); handlers.get("*")?.forEach((h) => h(event)); } export function isTauri(): boolean { return typeof window !== "undefined" && "__TAURI_INTERNALS__" in window; }