Skip to content

feat(webview): add dev-only browser bridge for standalone Chrome UI - #1593

Draft
hnbdr wants to merge 1 commit into
Zoo-Code-Org:mainfrom
hnbdr:feature/browser-bridge
Draft

feat(webview): add dev-only browser bridge for standalone Chrome UI#1593
hnbdr wants to merge 1 commit into
Zoo-Code-Org:mainfrom
hnbdr:feature/browser-bridge

Conversation

@hnbdr

@hnbdr hnbdr commented Sep 10, 2026

Copy link
Copy Markdown

Related GitHub Issue

N/A — developer-only tooling; per repo triage, internal DX tooling does not require a pre-existing issue.

Description

Adds an opt-in browser bridge: the Zoo Code UI can run as the main page of a regular Chrome tab instead of a nested VSCode webview iframe.

Why: inside the webview iframe the UI shares one DevTools panel with the rest of the VSCode window and cannot load DevTools extensions, which makes inspection awkward. As a top-level document the UI gets its own DevTools with installable extensions (React DevTools etc.), and the extension IPC becomes plain socket.io traffic that can be monitored live in the Network tab as websocket frames.

How:

  • src/core/webview/browserBridge.tsBrowserBridgeServer: a loopback-only (127.0.0.1) socket.io server, owned one-per-ClineProvider. Port 0 by default so the OS assigns a unique free port per bridge (override with ROO_BROWSER_BRIDGE_PORT); the port is passed to the tab as ?bridgePort=.... A virtual webview adapter keeps the existing WebviewMessage / ExtensionMessage protocol unchanged — no protocol forks anywhere.
  • src/activate/registerCommands.ts — registers the zoo-code.openInBrowser command only when ROO_BROWSER_BRIDGE=1 is set in the extension host env (added to the .vscode/launch.json dev config) and extensionMode === Development. It is intentionally not contributed in package.json, so end users never see a command-palette entry or toolbar button.
  • src/core/webview/ClineProvider.tsenableBrowserBridge() irreversibly switches a provider to its bridge (re-clicking the command reuses the existing port instead of leaking a second server). The real iframe then renders a static placeholder with a clickable link back to the browser tab, so the two environments never run React simultaneously. The bridge is disposed together with the provider.
  • webview-ui/src/utils/vscode.ts — when acquireVsCodeApi is absent but ?bridgePort= is present, VSCodeAPIWrapper routes messages over socket.io; inbound extensionMessage events are re-dispatched through window.postMessage so existing message consumers work unchanged.
  • webview-ui/src/index.css — dark-theme fallback values for the --vscode-* custom properties that VS Code normally injects, so the standalone tab renders sanely.
  • New deps: socket.io (extension) / socket.io-client (webview).

Reviewer notes:

  • Security boundary: server binds to 127.0.0.1 only, CORS restricted to local origins, and the whole path is gated behind Development mode + env var.
  • One provider → one bridge → one port; multiple sidebar panels / browser tabs can run simultaneously, each on its own port.

Test Procedure

  • Unit tests (new): cd src && npx vitest run core/webview/__tests__/browserBridge.spec.ts activate/__tests__/registerCommands.spec.ts core/webview/__tests__/ClineProvider.spec.ts — covers port allocation, virtual webview message routing, listener single-registration, placeholder rendering, dev-only command registration, bridge reuse/dispose.
  • pnpm lint and pnpm check-types pass (also enforced by pre-commit / pre-push hooks).
  • Manual: pnpm install → run webview dev server (pnpm --dir webview-ui dev) → F5 "Run and Debug → Run Extension" → in the extension host's command palette run Zoo Code: Open in Browser → Chrome opens http://localhost:5173/?bridgePort=<port> with the full UI as the main page; the sidebar iframe shows a placeholder link; DevTools → Network → WS shows every webview↔extension message pair live.

Pre-Submission Checklist

  • Issue Linked: N/A for dev-only tooling (see above).
  • Scope: Single focused feature — the browser bridge; no drive-by refactors.
  • Self-Review: I have performed a thorough self-review of my code.
  • Testing: New and/or updated tests have been added to cover my changes.
  • Visual Snapshot (UI changes only): N/A — no user-visible rendered state changes (placeholder and theme fallbacks appear only in the dev-only browser mode).
  • Documentation Impact: Considered — no user-facing docs needed (development-only tooling).
  • Contribution Guidelines: I have read and agree to the Contributor Guidelines.

Documentation Updates

  • No documentation updates are required (developer-only tooling; usage is documented in code comments).

Additional Notes

Local run of the changed-code mutation gate (scripts/stryker-diff.mjs) currently fails to spawn on Windows because spawnSync cannot execute the .cmd pnpm shims for vitest/stryker (ENOENT) — this is an infrastructure limitation of the gate script on Windows, not a code issue; CI (Linux) should run it normally. Happy to add a Stryker disable directive or focused tests in follow-up commits if the gate flags specific lines.

Get in Touch

GitHub: @hnbdr (Discord on request via GitHub/issue thread)

Runs the Zoo Code interface as the main page of a regular Chrome tab
instead of a nested VSCode webview iframe. Inside the iframe the UI
shares one DevTools panel with the rest of the window and cannot load
DevTools extensions, which makes inspection awkward. As a top-level
document the UI gets its own DevTools with installable extensions
(React DevTools, etc.), and the extension IPC becomes plain socket.io
traffic that can be monitored live in the DevTools network panel as
websocket frames.

- Add BrowserBridgeServer: a loopback-only socket.io server owned by
  each ClineProvider, with a virtual webview that keeps the existing
  WebviewMessage/ExtensionMessage protocol unchanged.
- Register a dev-only `zoo-code.openInBrowser` command (activated only
  with ROO_BROWSER_BRIDGE=1 in Development mode; intentionally not
  contributed in package.json so end users never see it) that starts
  the bridge, swaps the real webview for a placeholder with a link, and
  opens the browser tab.
- webview-ui: BrowserBridgeClient in VSCodeAPIWrapper activates when
  the tab is opened with ?bridgePort=..., plus a dark-theme fallback
  for --vscode-* CSS variables in standalone browser mode.
@coderabbitai

coderabbitai Bot commented Sep 10, 2026

Copy link
Copy Markdown
Contributor

Important

Review skipped

Auto reviews are limited based on label configuration.

🏷️ Required labels (at least one) (1)
  • coderabbit-review-active

Please check the settings in the CodeRabbit UI or the .coderabbit.yaml file in this repository. To trigger a single review, invoke the @coderabbitai review command.

⚙️ Run configuration

Configuration used: Path: .coderabbit.yaml

Review profile: ASSERTIVE

Plan: Advanced

Run ID: 7b9ce564-ccae-4f4b-9c6a-b81643e0367e

You can disable this status message by setting the reviews.review_status to false in the CodeRabbit configuration file.

Use the checkbox below for a quick retry:

  • 🔍 Trigger review

Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out.

❤️ Share

Comment @coderabbitai help to get the list of available commands.

@github-actions

github-actions Bot commented Sep 10, 2026

Copy link
Copy Markdown
Contributor

Review status

Thanks for contributing. This comment tracks the review sequence and the next action.

Current step: Mark the PR ready. Required CI must pass before CodeRabbit starts.

Review-state labels are managed by this workflow; do not edit them manually.

@codecov

codecov Bot commented Sep 10, 2026

Copy link
Copy Markdown

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant