Skip to content

fix(chrome-extension): add start/stop controls for bridge mode#2177

Merged
quanru merged 15 commits intomainfrom
fix/chrome-extension-bridge-start-stop
Mar 20, 2026
Merged

fix(chrome-extension): add start/stop controls for bridge mode#2177
quanru merged 15 commits intomainfrom
fix/chrome-extension-bridge-start-stop

Conversation

@quanru
Copy link
Collaborator

@quanru quanru commented Mar 18, 2026

Summary

  • Add Start/Stop toggle button to the bridge mode bottom status bar, allowing users to control the bridge connection lifecycle
  • Fix status display: show "Stopped" when bridge is closed instead of misleading "Listening for connection"
  • Server URL input is now editable when bridge is stopped (status closed), enabling users to change the remote server URL

Closes #2119

Changes

  • apps/chrome-extension/src/extension/bridge/index.tsx: Added handleToggleBridge function that sends BRIDGE_START/BRIDGE_STOP messages to the service worker, and a toggle button in the status bar
  • apps/chrome-extension/src/extension/bridge/index.less: Added .bridge-toggle-btn styles
  • apps/chrome-extension/tests/bridge-start-stop.test.ts: Added unit tests for bridge connector state machine, worker message handling, and UI state logic

Validation

  • pnpm run lint
  • npx nx build chrome-extension
  • vitest run apps/chrome-extension/tests/bridge-start-stop.test.ts ✅ (15 tests passed)

Test plan

  • Load the extension in Chrome, switch to Bridge Mode
  • Verify the status bar shows a Start/Stop button
  • Click Stop → status shows "Stopped", server URL input becomes editable
  • Change the server URL to a custom endpoint
  • Click Start → bridge connects using the new server URL
  • Verify the toggle works multiple times (stop → change URL → start → stop)

🤖 Generated with Claude Code

quanru and others added 2 commits March 18, 2026 18:42
The bridge mode UI was missing start/stop controls, making it impossible
for users to stop listening or change the remote server URL. The bridge
auto-started on extension load and the URL input was always disabled.

Closes #2119

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
@cloudflare-workers-and-pages
Copy link

cloudflare-workers-and-pages bot commented Mar 18, 2026

Deploying midscene with  Cloudflare Pages  Cloudflare Pages

Latest commit: 14fe38c
Status: ✅  Deploy successful!
Preview URL: https://726c7ec8.midscene.pages.dev
Branch Preview URL: https://fix-chrome-extension-bridge.midscene.pages.dev

View logs

Copy link

@chatgpt-codex-connector chatgpt-codex-connector bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

💡 Codex Review

Here are some automated review suggestions for this pull request.

Reviewed commit: a11b50a525

ℹ️ About Codex in GitHub

Your team has set up Codex to review pull requests in this repo. Reviews are triggered when you

  • Open a pull request for review
  • Mark a draft as ready
  • Comment "@codex review".

If Codex has suggestions, it will comment; otherwise it will react with 👍.

Codex can also answer questions or update the PR. Try commenting "@codex address that feedback".

Comment on lines +228 to +229
const isBridgeActive =
bridgeStatus === 'listening' || bridgeStatus === 'connected';

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

P2 Badge Keep the toggle in Stop mode while reconnecting

When a bridge session drops, BridgeConnector moves to disconnected and keeps retrying inside connect() instead of stopping (apps/chrome-extension/src/utils/bridgeConnector.ts:54-102). Because isBridgeActive only treats listening and connected as active, the new control flips to Start during that reconnect window even though the bridge is still running and the URL field stays disabled. In that state the user cannot actually stop the retry loop without first restarting the bridge, so the new start/stop control is incorrect whenever a connection is lost.

Useful? React with 👍 / 👎.

Comment on lines +234 to +235
chrome.runtime.sendMessage(
{ type: workerMessageTypes.BRIDGE_STOP },

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

P1 Badge Persist the stopped state before advertising a Stop control

This handler makes Stop look permanent, but BRIDGE_STOP only tears down the current worker instance. The extension is declared as an MV3 background.service_worker (apps/chrome-extension/static/manifest.json:5-17), and worker.ts still unconditionally runs initBackgroundBridge() plus safeSetupKeepalive({ shouldEnable: true }) at module load (apps/chrome-extension/src/scripts/worker.ts:352-379). After any normal service-worker restart, the bridge starts listening again, so users who stopped bridge mode can be silently re-exposed to incoming connections.

Useful? React with 👍 / 👎.

quanru and others added 13 commits March 19, 2026 13:03
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
The previous run failed because `npx nx test chrome-extension` ran all
tests including an unrelated flaky timeout in event-memory-stress.test.ts.

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
--testPathPattern is a Jest option, not vitest. Pass the file path
directly to vitest run instead.

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
Add junit reporter for bridge unit tests and upload as separate artifact.
Also check both unit and e2e test results in the final step.

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
Add e2e test case that verifies:
- Bridge mode shows Stop button when listening
- Clicking Stop changes status to "Stopped" and button to "Start"
- Server URL input becomes editable after stopping
- Typing a new server URL works
- Clicking Start restarts bridge with "Listening" status

Remove standalone unit test step from CI, keep e2e only.

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
…ve prompts

The previous run failed because the dropdown menu click didn't register,
leaving the panel in Playground mode. Added retry logic (same pattern as
existing test case 5) and improved AI prompts for more reliable element
targeting.

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
Reduce the bridge mode e2e test to just stop/start lifecycle verification.
The previous version had too many AI steps (URL change, expand config, etc.)
causing timeout at 360s. Focus on core stop→start flow only.

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
Move bridge mode start/stop e2e test out of chrome-extension.test.ts
into its own file chrome-extension-bridge.test.ts with a dedicated
CI job (chrome-extension-bridge) and separate report artifact.

The test covers:
- Open side panel and switch to Bridge Mode
- Stop bridge listening → verify "Stopped" + "Start" button
- Edit server URL when stopped
- Restart bridge → verify "Listening" + "Stop" button

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
Extend bridge e2e test to verify actual WebSocket connections:
- Start a real bridge server → extension connects → UI shows "Connected"
- Click Stop → disconnects → UI shows "Stopped"
- Start another server while stopped → no connection received
- Click Start → extension reconnects → UI shows "Connected" again

Uses a child process running bridge-test-server.mjs (Socket.IO server)
to simulate the CLI-side bridge server.

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
The bridge e2e test failed because the extension shows a connection
confirm dialog when a server connects. Add injectBridgePermission()
helper that sets chrome.storage.local alwaysAllow via CDP, bypassing
the confirm popup in automated tests.

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
The previous approach injected permission on the page target but the
service worker had already started connecting before the permission was
set. Now targets the service worker directly and restarts the bridge
after setting alwaysAllow, ensuring the permission takes effect.

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
When a connection drops, BridgeConnector moves to 'disconnected' but
keeps retrying in connectLoop(). The toggle button incorrectly showed
"Start" during reconnection because isBridgeActive did not include
'disconnected'. Now the Stop button stays visible while reconnecting,
allowing users to actually stop the retry loop.

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
The service worker unconditionally called initBackgroundBridge() on
startup, silently re-enabling bridge listening after the user had
explicitly stopped it. Now the stopped intent is persisted to
chrome.storage.local and checked before auto-starting.

- stopBackgroundBridge() sets BRIDGE_STOPPED_KEY
- startBackgroundBridge() clears BRIDGE_STOPPED_KEY
- initBackgroundBridge() skips auto-start if flag is present

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
@quanru quanru merged commit 6b2a40b into main Mar 20, 2026
11 checks passed
@quanru quanru deleted the fix/chrome-extension-bridge-start-stop branch March 20, 2026 06:16
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Projects

None yet

Development

Successfully merging this pull request may close these issues.

[Bug]: chrome extension cannot change remote server url

2 participants