Browser extension architecture
Introduction#
WXT + React extension architecture: side panel, background, content scripts, messaging, WebSocket to backend, auth, packaging.
Project structure#
Entrypoints cover background, content, and side panel, plus shared utilities and hooks. WXT manages build, manifest generation, and browser-specific targets.
graph TB subgraph "Extension Root" CFG["wxt.config.ts"] PKG["package.json"] TSC["tsconfig.json"] end subgraph "Entry Points" BG["background.ts"] CT["content.ts"] SP_IDX["sidepanel/index.tsx"] SP_MAIN["sidepanel/main.tsx"] APP["sidepanel/App.tsx"] AE["sidepanel/AgentExecutor.tsx"] end subgraph "Utilities" WS_CLIENT["utils/websocket-client.ts"] EXEC_AGENT["utils/executeAgent.ts"] EXEC_ACTIONS["utils/executeActions.ts"] PARSE_CMD["utils/parseAgentCommand.ts"] end subgraph "Hooks" AUTH["hooks/useAuth.ts"] WS_HOOK["hooks/useWebSocket.ts"] TABS["hooks/useTabManagement.ts"] end CFG --> BG CFG --> CT CFG --> SP_IDX SP_IDX --> APP APP --> AE APP --> AUTH APP --> WS_HOOK APP --> TABS AE --> WS_CLIENT AE --> EXEC_AGENT AE --> EXEC_ACTIONS AE --> PARSE_CMD AUTH --> WS_CLIENT
Core components#
- Side Panel (React): Hosted inside a shadow root UI, renders the main app, manages authentication, WebSocket connectivity, tab management, and the agent executor.
- Background Script: Handles cross-tab messaging, executes agent tools, performs browser-level actions, and manages Gemini requests.
- Content Script: Injects UI overlays and performs page-level actions when instructed by the background script.
- Messaging System: Uses browser runtime messaging for background ↔ side panel and background ↔ content script communication.
- WebSocket Client: Provides a minimal client for real-time agent execution updates and statistics retrieval.
- Utilities: Command parsing, agent execution, and browser action execution.
Architecture overview#
Extension pieces:
- UI Layer: Side panel React app with hooks for auth, WebSocket, and tab management.
- Control Layer: Side panel orchestrates agent execution and displays progress.
- Communication Layer: WebSocket client for real-time updates; browser messaging for background ↔ side panel and background ↔ content script.
- Execution Layer: Background script handles browser APIs and dispatches actions; content script executes DOM-level actions.
graph TB SP["Side Panel (React)"] BG["Background Script"] CS["Content Script"] WS["WebSocket Server"] BA["Browser APIs"] SP --> BG BG --> BA SP --> WS SP --> BA BG --> CS CS --> BA
Detailed component analysis#
Side panel React application#
The side panel is a React app mounted inside a shadow root UI. It initializes the app, sets up authentication, WebSocket connectivity, and tab management. It renders the agent executor and unified settings menu.
classDiagram
class App {
+user
+authLoading
+tokenStatus
+browserInfo
+handleLogin()
+handleGitHubLogin()
+handleLogout()
+getTokenAge()
+getTokenExpiry()
+handleManualRefresh()
+loadApiKey()
+saveApiKey()
+loadConversationStats()
}
class AgentExecutor {
+goal
+isExecuting
+progress
+result
+error
+sessions
+activeSessionId
+openTabs
+handleExecute()
+handleStop()
+handleNewChat()
+handleDeleteSession()
}
class useAuth {
+user
+authLoading
+tokenStatus
+shouldRedirectToSettings
+handleLogin()
+handleGitHubLogin()
+handleLogout()
+getTokenAge()
+getTokenExpiry()
+handleManualRefresh()
}
class useWebSocket {
+wsConnected
+useWebSocket
+autoConnectEnabled
+setupWebSocket()
}
class useTabManagement {
+tabs
+activeTab
+loadTabs()
}
App --> AgentExecutor : "renders"
App --> useAuth : "uses"
App --> useWebSocket : "uses"
App --> useTabManagement : "uses"Authentication flow#
The authentication hook integrates with browser identity and a backend service to exchange OAuth codes for tokens, persist user data, and manage token refresh. It supports both Google OAuth and a demo GitHub flow.
sequenceDiagram
participant UI as "Side Panel UI"
participant Hook as "useAuth"
participant Identity as "browser.identity"
participant Backend as "Backend Service"
UI->>Hook : "handleLogin()"
Hook->>Identity : "launchWebAuthFlow(authUrl)"
Identity-->>Hook : "redirectResponse(code)"
Hook->>Backend : "POST /exchange-code {code, redirect_uri}"
Backend-->>Hook : "{access_token, refresh_token, expires_in}"
Hook->>Backend : "GET /userinfo?access_token"
Backend-->>Hook : "user info"
Hook->>Hook : "persist googleUser in storage"
Hook-->>UI : "set user state"WebSocket integration and real-time updates#
The WebSocket client encapsulates connection management, event emission, and agent execution. The side panel hook subscribes to connection status and progress updates, displaying real-time feedback.
sequenceDiagram
participant SP as "Side Panel"
participant Hook as "useWebSocket"
participant WSClient as "wsClient"
participant Server as "WebSocket Server"
SP->>Hook : "setupWebSocket()"
Hook->>WSClient : "subscribe to connection_status"
WSClient->>Server : "connect()"
Server-->>WSClient : "connect"
WSClient-->>Hook : "emit {connected : true}"
Hook-->>SP : "set wsConnected=true"
SP->>WSClient : "executeAgent(command, onProgress)"
WSClient->>Server : "emit execute_agent {command}"
Server-->>WSClient : "generation_progress"
WSClient-->>SP : "onProgress(message)"
Server-->>WSClient : "agent_result"
WSClient-->>SP : "resolve result"Agent executor pattern and action execution#
The agent executor parses slash commands, executes agents either via WebSocket or HTTP, and triggers browser actions. It maintains chat sessions and displays formatted responses.
flowchart TD
Start(["User submits command"]) --> Parse["Parse command (/agent-action ...)"]
Parse --> Stage{"Stage complete?"}
Stage --> |No| Suggestions["Show suggestions or partial matches"]
Suggestions --> WaitInput["Wait for user refinement"]
WaitInput --> Parse
Stage --> |Yes| Prepare["Prepare payload with context<br/>tabs, chat history, attachments"]
Prepare --> Mode{"WebSocket connected?"}
Mode --> |Yes| WSExec["wsClient.executeAgent()"]
Mode --> |No| HTTPExec["fetch() to backend endpoint"]
WSExec --> Actions{"Has action plan?"}
HTTPExec --> Actions
Actions --> |Yes| ExecActions["executeBrowserActions()"]
Actions --> |No| Display["Format and display response"]
ExecActions --> Display
Display --> Persist["Persist sessions to storage"]
Persist --> End(["Done"])Background script functionality and messaging#
The background script listens for messages from the side panel and content script, executes agent tools, manages tabs, and performs browser-level actions. It injects content scripts and coordinates cross-tab communication.
sequenceDiagram
participant SP as "Side Panel"
participant BG as "Background Script"
participant CS as "Content Script"
participant Tabs as "Tabs API"
SP->>BG : "runtime.sendMessage {type : EXECUTE_AGENT_TOOL, payload}"
BG->>BG : "handleExecuteAgentTool()"
BG-->>SP : "sendResponse(result)"
SP->>BG : "runtime.sendMessage {type : EXECUTE_ACTION, payload}"
BG->>Tabs : "scripting.executeScript(files : [...])"
BG->>Tabs : "tabs.sendMessage(tabId, {type : PERFORM_ACTION, action})"
BG-->>SP : "sendResponse(response)"
SP->>BG : "runtime.sendMessage {type : GET_ACTIVE_TAB}"
BG->>Tabs : "tabs.query({active : true,currentWindow : true})"
BG-->>SP : "sendResponse({success, tab})"
SP->>BG : "runtime.sendMessage {type : GET_ALL_TABS}"
BG->>Tabs : "tabs.query({})"
BG-->>SP : "sendResponse({success, tabs})"Content script integration#
The content script runs on all URLs and can be extended to perform page-level actions. It listens for messages from the background script and executes DOM operations.
Dependency analysis#
The extension relies on WXT for build and manifest generation, React for UI, and Socket.IO for real-time communication. Dependencies are declared in package.json and TypeScript configuration extends WXT’s generated tsconfig.
graph TB WXT["WXT Build System"] React["React + ReactDOM"] SocketIO["Socket.IO Client"] Tailwind["Tailwind Merge / Animate CSS"] Radix["Radix UI Select"] Markdown["React Markdown + KaTeX"] WXT --> AppTSX["App.tsx"] React --> AppTSX SocketIO --> WSClient["websocket-client.ts"] Tailwind --> AppTSX Radix --> AppTSX Markdown --> AppTSX
Performance considerations#
- Debounce or throttle frequent UI updates (e.g., tab list refresh) to reduce re-renders.
- Batch browser API calls (tabs.query, scripting.executeScript) and cache results where appropriate.
- Use lazy loading for heavy components and defer non-critical computations.
- Limit DOM extraction sizes (e.g., HTML capture) and apply timeouts to prevent long-running operations.
- Prefer polling fallbacks for stats retrieval when WebSocket is unavailable.
Troubleshooting guide#
Common issues and resolutions:
- WebSocket not connecting: Verify VITE_API_URL and server availability; check connection events and fallback to HTTP stats.
- Authentication failures: Ensure backend is running and identity API is available; confirm OAuth redirect URI and scopes.
- Content script injection errors: Confirm permissions and that the content script path is correct; verify target tab exists.
- Tab management inconsistencies: Ensure listeners are registered/unregistered on mount/unmount; use query results before acting.
- Cross-origin limitations: Use host_permissions and appropriate permissions; avoid unsafe inline styles in injected UI.
Conclusion#
Host permissions are broad by design for automation. OAuth uses browser.identity. Point the client at the API base URL through env, not hardcoded strings.
Appendices#
WXT configuration and permissions#
- Manifest permissions include activeTab, tabs, storage, scripting, identity, sidePanel, webNavigation, webRequest, cookies, bookmarks, history, clipboard, notifications, contextMenus, downloads.
- Host permissions grant access to all URLs.
- Scripts support development and build targets for Chrome and Firefox.
Browser compatibility and packaging#
- Use WXT scripts to build and package for Chrome and Firefox.
- Shadow DOM UI ensures isolation and compatibility across pages.
- Feature detection for browser APIs (e.g., speech recognition) prevents runtime errors.
Cross-Origin communication#
- Use host_permissions for broad access.
- For OAuth, rely on browser.identity and secure redirects.
- For backend communication, configure CORS and environment variables for API base URL.