This document explains the unified ideBridge used by IDE hosts (JetBrains, VSCode) and the web UI (packages/opencode/webgui). It standardizes bidirectional communication, supports request/response (RPC), centralizes delivery, and handles lifecycle queueing.
window.ideBridgeWe intentionally defer: strict origin checks, schemas, and runtime validation for now.
type: string – message kindpayload?: any – arbitrary data per typeid?: string – present for requestsreplyTo?: string – present for responsesok?: boolean and error?: string – optional result flags in responsestimestamp?: numberExample request:
{ "id": "abc123", "type": "openFile", "payload": { "path": "/p/file.ts", "line": 42 }, "timestamp": 1731390000000 }
Example response:
{ "replyTo": "abc123", "ok": true }
paviko.opencode.ui.IdeBridge
JBCefJSQuery to receive UI→host JSON strings__ideBridgeSend(string) – UI calls this; routes into the JSQuery__ideBridgeDeliver(any) – Host uses this to deliver messages to UIIdeBridge.install(browser, project) – set up bridge and queuesIdeBridge.send(type, payload) – host→UI messageopenFile { path, line } -> opens in IDE, replies { ok: true }Converted usages:
executeJavaScript(window.postMessage(...)) replaced by IdeBridge.send(type, payload)PathInserter → IdeBridge.send("insertPaths"|"pastePath", ...)IdeOpenFilesUpdater → IdeBridge.send("updateOpenedFiles", ...)FontSizeSynchronizer (kept for compatibility) → IdeBridge.send("setFontSize", { size })OpenInIdeHandler is superseded by IdeBridge request handler.src/lib/ideBridge.ts
ideBridge.init() – binds dispatching to window.postMessage and host shimsideBridge.send(msg) – UI→host fire-and-forgetideBridge.request(type, payload) – returns Promise; resolves on { replyTo }ideBridge.on(handler) – subscribe to host→UI messages__ideBridgeSend exists, then flush()src/main.tsx before React mounttypeMinimal UI usage:
import { ideBridge } from "./lib/ideBridge"
ideBridge.init()
ideBridge.on((msg) => {
switch (msg.type) {
case "updateOpenedFiles":
/* update state */ break
case "insertPaths":
/* route into UI */ break
}
})
// Request/response example
await ideBridge.request("openFile", { path: "/p/file.ts", line: 10 })
__ideBridgeSend(string) – UI→extension delivery__ideBridgeOnMessage(any) – extension→UI delivery entry{ replyTo, ok, ... }webgui beyond ideBridge.init()type and payload contractideBridge.send({ type, payload }) or ideBridge.request(type, payload)ideBridge.on(msg => { if (msg.type === 'newType') ... })IdeBridge.handleInbound, add case for typereplyOk(id) or replyError(id, message)IdeBridge.send(type, payload)WebViewLoadHandler, WebViewScripts, and OpenInIdeHandler in new code pathsideBridge.init() runs before app logic__ideBridgeSend, __ideBridgeDeliver) – JetBrains is handled by IdeBridge.installreplyTo equal to request id{ paths: string[] }{ path: string }{ openedFiles: string[], currentFile: string | null }{ path: string, line?: number } → opens file in IDE, responds with { replyTo, ok } or { replyTo, ok: false, error }{ url: string } → opens URL in default browser, responds with { replyTo, ok }{ replyTo, ok, error? }__ideBridgeSend to send and __ideBridgeOnMessage/host __ideBridgeDeliver to receive