Skip to content

feat(desktop): add embedded browser panel with WebContentsView#1671

Merged
benjaminshafii merged 1 commit intodevfrom
feat/embedded-browser-panel
May 6, 2026
Merged

feat(desktop): add embedded browser panel with WebContentsView#1671
benjaminshafii merged 1 commit intodevfrom
feat/embedded-browser-panel

Conversation

@benjaminshafii
Copy link
Copy Markdown
Member

Summary

Adds a built-in browser panel to the Electron desktop app. Users click the Globe icon in the session header to open a browser panel on the right side of the layout.

  • Uses Electron's WebContentsView (the modern replacement for BrowserView) for full process isolation
  • Sandboxed session partition (persist:openwork-browser) -- separate cookies/storage from the main app
  • Real-time navigation state sync (URL, title, loading, can-go-back/forward) pushed to the renderer
  • URL bar with keyboard navigation (Enter to go), back/forward/reload buttons, close button
  • ResizeObserver keeps the WebContentsView bounds in sync with the React panel

Architecture

React (BrowserPanel)
  └── IPC (openwork:browser:*)
      └── Electron Main Process
          └── WebContentsView (sandboxed, persist:openwork-browser partition)
              └── Renders web content

The WebContentsView is a native Chromium view overlaid on top of the BrowserWindow. The React panel provides the toolbar (URL bar, nav buttons), and a transparent <div> placeholder where the native view renders. Bounds are synced via ResizeObserver + window resize listener.

Why this matters

This enables Chrome DevTools MCP to point at the embedded browser (--browserUrl=http://127.0.0.1:<port>) so agents can automate a browser visible inside the app -- no external Chrome window needed. Users can watch what the agent is doing in real time.

Test plan

  • 17 existing tests pass (bun test apps/app/tests/ -- 0 failures)
  • The Globe toggle icon only renders in the Electron runtime (isElectronRuntime() gate)
  • The BrowserPanel component shows a fallback message in non-Electron contexts
  • WebContentsView is destroyed on window close to prevent leaks
bun test apps/app/tests/
# 17 pass, 0 fail

Files changed

File Change
apps/desktop/electron/main.mjs Add WebContentsView browser management + IPC handlers
apps/desktop/electron/preload.mjs Expose browser.* methods via contextBridge
apps/app/src/app/lib/desktop.ts Add TypeScript types for browser bridge
apps/app/src/react-app/domains/session/browser/browser-panel.tsx New browser panel component
apps/app/src/react-app/domains/session/chat/session-page.tsx Integrate browser panel + Globe toggle

Manual verification steps

  1. pnpm dev:electron from apps/desktop/
  2. Click the Globe icon in the session header bar
  3. A browser panel opens on the right (520px wide) showing Google
  4. Type a URL in the address bar, press Enter -- navigates
  5. Back/forward/reload buttons work
  6. Click the X or Globe icon again to close
  7. WebContentsView is removed from the window (no ghost rendering)

Adds a built-in browser panel to the Electron desktop app, activated via
a Globe icon in the session header. The browser uses WebContentsView for
full process isolation with a sandboxed session partition.

Main process (electron/main.mjs):
- Create/show/hide/destroy a WebContentsView with sandbox=true
- IPC handlers for navigate, back, forward, reload, setBounds, getState
- Push navigation state changes to the renderer in real time
- Clean up on window close

Preload (preload.mjs):
- Expose browser.* methods: show, hide, navigate, back, forward,
  reload, setBounds, getState, destroy, onStateChange

Renderer (apps/app/):
- New BrowserPanel component with URL bar, nav buttons, close button
- Integrated into session-page.tsx as a right-side panel (520px)
- Globe toggle icon in the session header (Electron-only)
- ResizeObserver keeps WebContentsView bounds in sync with the panel
- TypeScript types for the browser bridge in desktop.ts
@vercel
Copy link
Copy Markdown
Contributor

vercel Bot commented May 6, 2026

The latest updates on your projects. Learn more about Vercel for GitHub.

Project Deployment Actions Updated (UTC)
openwork-app Ready Ready Preview, Comment May 6, 2026 2:08am
openwork-den Ready Ready Preview, Comment May 6, 2026 2:08am
openwork-den-worker-proxy Ready Ready Preview, Comment May 6, 2026 2:08am
openwork-landing Ready Ready Preview, Comment, Open in v0 May 6, 2026 2:08am
openwork-share Ready Ready Preview, Comment May 6, 2026 2:08am

@benjaminshafii benjaminshafii merged commit 610ae1c into dev May 6, 2026
11 checks passed
benjaminshafii added a commit that referenced this pull request May 6, 2026
…ixed)

Re-implements both features with fixes for the issues found in #1670/#1671:

Fixes from PR1 (chrome-devtools-mcp bundling):
- Use 'node' string literal instead of process.execPath in the IPC
  handler — process.execPath in Electron is the Electron binary, not
  node, so OpenCode couldn't spawn the command
- Fix args filter to only carry --prefixed flags (not -y from npx)
- Fix mcp.add fallback path to use resolved command from mcpEntryConfig
  instead of the original entry.command

Fixes from PR2 (embedded browser panel):
- Guard against zero-dimension bounds on first render — skip
  browser.show() until the panel has non-zero dimensions, let
  ResizeObserver trigger the actual show
- Defensive try/catch in destroyBrowserView for already-destroyed
  webContents

Verified:
- resolveChromeDevtoolsMcpBin returns ['node', '<abs-path>'] (not
  ['Electron', '<path>'])
- All 17 existing tests pass
- Electron app starts and loads successfully
- CDP targets visible at the remote debugging port
benjaminshafii added a commit that referenced this pull request May 6, 2026
…e-implement

* Revert "feat(desktop): add embedded browser panel with WebContentsView (#1671)"

This reverts commit 610ae1c.

* Revert "feat(desktop): bundle chrome-devtools-mcp as dependency, eliminate npx requirement (#1670)"

This reverts commit fe9881b.

* feat(desktop): bundle chrome-devtools-mcp + embedded browser panel (fixed)

Re-implements both features with fixes for the issues found in #1670/#1671:

Fixes from PR1 (chrome-devtools-mcp bundling):
- Use 'node' string literal instead of process.execPath in the IPC
  handler — process.execPath in Electron is the Electron binary, not
  node, so OpenCode couldn't spawn the command
- Fix args filter to only carry --prefixed flags (not -y from npx)
- Fix mcp.add fallback path to use resolved command from mcpEntryConfig
  instead of the original entry.command

Fixes from PR2 (embedded browser panel):
- Guard against zero-dimension bounds on first render — skip
  browser.show() until the panel has non-zero dimensions, let
  ResizeObserver trigger the actual show
- Defensive try/catch in destroyBrowserView for already-destroyed
  webContents

Verified:
- resolveChromeDevtoolsMcpBin returns ['node', '<abs-path>'] (not
  ['Electron', '<path>'])
- All 17 existing tests pass
- Electron app starts and loads successfully
- CDP targets visible at the remote debugging port

* feat(desktop): auto-inject chrome-devtools MCP into opencode.json on engine start

When the OpenCode engine starts in the Electron desktop app,
ensureOpencodeConfig now automatically adds a chrome-devtools MCP entry
to opencode.json if one doesn't already exist. The command uses the
bundled chrome-devtools-mcp package (['node', '<abs-path>']) so no
npm/npx is needed at runtime.

This mirrors what Tauri's ensure_workspace_files() does in files.rs for
the starter preset, making Control Chrome a zero-setup default for all
Electron workspaces.

* fix(desktop): seed chrome-devtools MCP at workspace creation time

ensureOpencodeConfig in runtime.mjs only runs when the OpenCode engine
starts, which happens asynchronously after the workspace is selected.
New workspaces were left without a chrome-devtools entry because the
engine hadn't started yet.

Fix: call seedChromeDevtoolsMcp(folderPath) directly inside the
workspaceCreate IPC handler so the config is written synchronously
during workspace creation. The runtime.mjs ensureOpencodeConfig
remains as a backup for workspaces created before this change.

Verified: created a workspace via IPC, confirmed opencode.jsonc
contains chrome-devtools with ['node', '<bundled-bin-path>'].

* fix(desktop): migrate legacy npx/bare chrome-devtools commands to bundled node path

Existing workspaces created by Tauri or older Electron versions have
chrome-devtools MCP entries with npx-based or bare-binary commands
like ['npx','-y','chrome-devtools-mcp@latest'] or ['chrome-devtools-mcp'].

Both seedChromeDevtoolsMcp (main.mjs, runs at workspace creation) and
ensureOpencodeConfig (runtime.mjs, runs at engine start) now detect
these legacy commands and migrate them to the bundled ['node', path]
command. User-customised commands (absolute paths, etc.) are preserved.
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