-
Notifications
You must be signed in to change notification settings - Fork 0
The Bridge API
An app frontend imports @helve/bridge and nothing from @tauri-apps/api. The bridge is a singleton. It picks its host once, at load. The same app code then runs in an iframe under the shell, or as a top-level window in a standalone Tauri app.
import { invoke, reportPainted, openIn, publish, subscribe } from "@helve/bridge";invoke<T = unknown>(method: string, params?: unknown, timeoutMs?: number): Promise<T>The method name carries the app id:
const notes = await invoke<Note[]>("notes/list");A rejected call throws HelveRpcError, which carries a numeric code from HelveErrorCode. Handle the failure and draw a state for it.
reportPainted(): voidCall it once the first meaningful content reaches the DOM. The bridge ignores every repeat call, which makes React StrictMode double effects safe. Build an App covers the timing.
openIn(appId: string, payload?: unknown): Promise<{ instanceId: string }>openIn names a kind of app, and never one instance. File Explorer asks for a viewer in its own cluster. Which surface answers is a layout fact that only the shell sees.
One rule makes this safe. The shell resolves the asking frame from event.source against its own map of mounted iframes. A frame cannot assert its own identity, which limits an app to its own cluster.
publish(topic: string, value: unknown): void
subscribe(topic: string, cb: (value: unknown, from: string) => void): () => voidA published topic is retained. A frame that mounts later reads the current value instead of waiting for the next change.
The shell routes both messages without reading either one. Two apps agree on a new topic with no edit under src/shell/.
declareCommands(commands: readonly string[]): void
onCommand(cb: (command: string) => void): () => voiddeclareCommands states what this frontend carries out at this moment. The shell greys out the rest and sends nothing you have not declared. Declare again when the state changes. onCommand runs the handler.
host: "helve" | "tauri"
session(): Promise<Session> // { projectPath: string | null }host reports which side answered the handshake. Use it for the few places where the two hosts differ, and keep the rest of the code common.
openIn, publish and subscribe refuse under the tauri host. That host is one window with one frontend: no cluster, no second app, and no delivery target. The bridge refuses the call instead of dropping it, which makes the failure visible.
on(event: string, cb: (payload: unknown) => void): () => voidEach subscription returns its own unsubscribe. Call it on unmount.
docs/tool-protocol.md §3 holds the message contract, §4 covers this package, and §6 separates what is settled from what is still in motion.