browsa = browser side panel AI. Side-panel AI chat for any webpage โ talk to your LLM agent about what you're reading.
browsa is a Chrome / Edge extension (Manifest V3) that opens a chat panel next to whatever tab you're on, attaches the page content, and streams replies from any OpenAI-compatible API.
[Web page] โ [browsa side panel] โ [your LLM / agent] โ streaming reply
- Clone or download this repo.
- Open
chrome://extensions(oredge://extensions). - Enable Developer mode.
- Click Load unpacked โ select the
browsa/directory. - Press
Ctrl+Shift+H(or click the toolbar icon) to open the side panel. - Click โ Settings to configure a provider.
npm install # first time only
npm test # run 460+ unit tests
npm run package # โ browsa-v<version>.zipTo bump the version before packaging:
npm version patch # bug fix: 0.24.0 โ 0.24.1
npm version minor # new feature: 0.24.0 โ 0.25.0
npm run packagenpm version automatically syncs the version to both package.json and manifest.json.
browsa splits providers into two categories:
- Agent Providers โ full agent backend with tool execution (bash, file ops, web search, etc.). The AI can actually do things on the server side.
- LLM Providers โ plain language model endpoint for conversation only. Model ID is required.
Use the Ping button in Settings to verify connectivity and auto-detect capabilities. The provider status (reachable / unreachable) is shown in the sidebar dropdown.
Hermes is a self-hosted AI agent with built-in tools (web search, terminal, file ops, memory, skills). browsa uses its /v1/runs API โ richer than plain chat completions (tool progress, approval/clarification prompts for dangerous actions) โ with a stable X-Hermes-Session-Id per conversation so Hermes can maintain session continuity server-side. Falls back to plain /v1/chat/completions automatically if a Hermes deployment doesn't advertise /v1/runs support.
1. Install Hermes
pip install hermes-agent # or follow the official install guide2. Enable the API server โ add to ~/.hermes/.env:
API_SERVER_ENABLED=true
API_SERVER_KEY=your-secret-key3. Start Hermes
hermes gateway
# โ [API Server] API server listening on http://127.0.0.1:86424. Configure browsa โ open โ Settings, select the hermes provider:
| Field | Value |
|---|---|
| Base URL | http://<server-ip>:8642 |
| API Key | value of API_SERVER_KEY |
5. Ping to verify. /v1/runs support is auto-detected and enabled automatically.
This setup runs the real Claude Code CLI on your server โ with its full tool suite (bash, file read/write, etc.) โ and exposes it as a standard OpenAI-compatible HTTP API.
Prerequisites: claude CLI installed and authenticated on the server (claude auth login or ANTHROPIC_API_KEY set).
1. Install uv (if not already installed)
curl -LsSf https://astral.sh/uv/install.sh | sh2. Clone and install the wrapper
git clone https://github.com/RichardAtCT/claude-code-openai-wrapper
cd claude-code-openai-wrapper
uv python install 3.11
uv venv --python 3.11
source .venv/bin/activate
uv pip install -e .3. Configure โ create claude-code-openai-wrapper/.env:
# Directory where Claude Code will operate (your project root)
CLAUDE_CWD=/path/to/your/project
# A password you make up โ browsa uses this as the API Key
API_KEYS=make-up-any-password-here
# Auth method โ pick one:
CLAUDE_AUTH_METHOD=cli # use existing `claude auth login` session
# ANTHROPIC_API_KEY=sk-ant-... # or direct API key
IS_SANDBOX=1 # required if running as root
MAX_TURNS=50 # max tool-call turns per request (default 10)4. Start the wrapper
source .venv/bin/activate
uvicorn src.main:app --host 0.0.0.0 --port 8000
# To keep running in background:
tmux new -s claude-wrapper
# ... start as above, then Ctrl+B D to detach5. Configure browsa โ open โ Settings, select the claude-code provider:
| Field | Value |
|---|---|
| Base URL | http://<server-ip>:8000 |
| API Key | value of API_KEYS |
Claude Code is an agent provider, so there's no Model ID field to configure โ the wrapper picks the model server-side.
Note: Claude Code operates in
CLAUDE_CWD. Set it to your project root so Claude can read and write your actual files.
Any endpoint that speaks /v1/chat/completions โ OpenAI, Anthropic, Ollama, Groq, LiteLLM, etc.
Configure browsa โ open โ Settings, configure the OpenAI-compatible provider:
| Field | Value |
|---|---|
| Base URL | e.g. https://api.openai.com |
| API Key | your API key |
| Model ID | required โ e.g. gpt-4o, claude-sonnet-4-6 |
Ping validates connectivity and verifies the model ID is accepted by the server.
Click ๐ in the composer to attach the current page. browsa supports two attachment modes selectable in the composer footer:
| Mode | What gets sent |
|---|---|
| Auto | Tries Mozilla Readability first (clean article text, ~5โ30 KB), falls back to DOM tree, then full body.innerText |
| ๐ท Screenshot | PNG of the visible tab โ for multimodal models or visual content |
Attaching a PDF (or a page that turns out to be one) is automatic โ no separate mode to pick. browsa tries pdf-inspector-wasm first (full layout reconstruction โ tables, headings, columns โ running client-side, nothing uploaded), falls back to plain pdf.js text extraction if that's unavailable or the PDF turns out to be a scanned/image-only page with no text layer, and as a last resort attaches just the PDF's URL so your agent can fetch and read it with its own tools.
For text selection, highlight text on the page and use the floating toolbar or right-click context menu (Ask / Explain / Translate / Summarize). The selection is sent automatically without needing to click ๐.
For sites where Readability produces poor results, browsa intercepts the browser's own API calls and extracts structured content โ no signing, no re-auth, just observing what the page already fetched. Open the page and let it fully load before sending your first message.
| Site | Content extracted |
|---|---|
| YouTube | Title, transcript, chapters, description, author, view/like counts |
| Bilibili | Title, AI summary, subtitle/transcript, audio URL, video stats |
| ๅฐ็บขไนฆ | Note title, description, tags, images, top comments, stats |
| ๆ้ | Full article Markdown source |
| ็ฅไน | ไธๆ article or top 3 answers to a question |
| Twitter / X | Tweet text, author, engagement stats |
| ้ช็ | Stock quote or post content |
| ๅฐๅฎๅฎ | Podcast episode title, description, show notes |
| ๅพๅฐ | Article text |
| ๆๅฎขๆถ้ด | Article text |
- Streaming replies โ tokens appear as they arrive; click โ or press
Escto stop - Think blocks โ
<think>/<thinking>content shown in a collapsible block, auto-collapsed after streaming - Markdown rendering โ full GFM: tables, code blocks, lists, inline formatting
- Syntax highlighting โ 40+ languages via highlight.js
- LaTeX โ inline
$...$and display$$...$$via KaTeX, offloaded to a Web Worker for formula-heavy messages so the panel doesn't jank - Mermaid diagrams โ rendered inline with zoom / pan / copy source / export SVG toolbar; sequence diagrams with a semicolon in dialogue text (e.g. embedded SQL) render correctly via an automatic escape-and-retry
- ECharts charts โ
```echartscode blocks rendered inline with a resize-aware toolbar - Markmap mind maps โ
```markmapcode blocks (a plain Markdown heading/list outline) rendered inline as an interactive, zoomable mind map with the same zoom/reset/copy/export toolbar as Mermaid/ECharts; no dedicated button needed โ just ask for a mind map/outline and the model knows the format - Diff highlighting โ
diffcode blocks color+green and-red - Detail thread ("็ป่") โ select any text inside a reply to open a scoped side conversation about just that excerpt, without touching the main history. Fully resizable, closes and discards everything on โ
- Edit & resend โ click โ on any user message to edit and re-send
- Copy response โ click โ to copy the full raw Markdown
- Timestamps โ hover any message to see send time
- Session history โ save the current conversation as a named session, browse and restore past sessions from the ๐ drawer
- Export โ export any session as a Markdown file
- In-conversation search โ
Ctrl+Fto search across all messages with prev/next navigation - Multi-select โ select multiple messages for batch deletion
- Undo clear โ clearing history shows an undo option for 5 seconds
- Image attachment โ drag-and-drop or paste images directly into the composer (for multimodal models)
- Slash commands โ type
/to see completions; see list below - Quick actions โ one-click Summarize / Key Points / Explain / โ ไธญๆ / Outline buttons above the composer
- Floating selection toolbar โ appears when you highlight text on any page: Ask ยท Explain ยท โ ไธญๆ ยท Summarize
- Right-click context menu โ browsa โบ Ask / Explain / Translate / Summarize on selected text
- Domain rules โ per-URL-pattern extra system prompt (e.g. always respond in English on
github.com) - Mask rules โ regex-based content redaction before sending to the LLM (e.g. strip phone numbers)
- Reply language โ force replies in a specific language regardless of page language
- Max text chars โ cap how much page content is sent per turn
- Auto-summarize long attachments โ when an attached page or video transcript exceeds the threshold (default: 40,000 chars), browsa chunks it, summarizes each chunk in parallel using the configured provider, and merges the result once in the background โ the attachment response returns immediately with no latency, and subsequent turns use the compressed version instead of re-sending the full text every time. Timestamp markers in video transcripts (
[mm:ss]) are explicitly preserved so clickable seek links keep working. Fails open: any error silently keeps the original text. - llms.txt โ optionally fetch
<origin>/llms.txtbefore each chat for site-specific LLM instructions
Type / in the composer to see autocomplete. All commands can be followed by additional instructions:
/summarize focus on the methodology
/translate keep technical terms in English
| Command | Prompt sent to LLM |
|---|---|
/summarize |
3โ5 bullet summary |
/translate |
Translate to Chinese |
/rewrite |
More concise rewrite, keeping all facts |
/explain |
Explain for a beginner in simple language |
/outline |
Nested outline of headings only |
/keypoints |
Top 5 takeaways |
/prompt |
Show the current active system prompt (not sent to LLM) |
| Shortcut | Action |
|---|---|
Ctrl+Shift+H |
Open / close side panel |
Enter |
Send message (configurable in Settings) |
Shift+Enter |
New line |
Ctrl+K |
Clear history (with undo) |
Ctrl+/ |
Cycle context mode (Auto โ Screenshot) |
Ctrl+F |
Open in-conversation search |
Esc |
Cancel stream / close search / close drawer |
background.jsโ MV3 service worker. Singlehandle()message router for all extension messages โ still the single dispatcher, but the two biggest cases (CHAT,SUBCHAT) delegate tolib/handlers/. Manages site XHR caches (keyed bytabId), streaming via per-turnbrowsa-chat/browsa-subchatports, mid-stream tab switching viastreamState(lib/state.js), and fires off a fire-and-forget attachment auto-summarize pass (lib/handlers/attach-summarizer.js) when a page/video attachment exceeds the configured length threshold.sidepanel.jsโ Chat UI orchestrator: init/send/history/approval-clarify cards/screenshot crop. The Markdown/Mermaid/Markmap/KaTeX/ECharts rendering pipeline, sessions drawer, in-conversation search, multi-select, and detail-thread ("็ป่") side conversations are each their own module underlib/sidepanel/.lib/sidepanel/render.jsโ marked + DOMPurify + KaTeX + Mermaid + ECharts + Markmap + highlight.js pipeline. Streaming deltas are smoothed viareveal-pacer.js(a thin wrapper around the vendoredmarkstream-corepackage); KaTeX rendering for the final per-message render offloads formula-heavy messages to a Web Worker (katex-worker-client.js/katex.worker.js), falling back to synchronous rendering below a small-batch threshold or on worker failure; Mermaid's SVG output is sanitized (sanitizeMermaidSvg, from the vendoredstream-markdown-parserpackage) and sequence-diagram parse failures auto-retry with problem semicolons escaped (mermaid-utils.js). All three diagram vendor bundles (Mermaid/ECharts/Markmap) are speculatively preloaded (preloadChartVendors()) the moment a turn starts, so the first diagram in a session doesn't pay a multi-MB cold-load penalty right when it needs to render.lib/page-extractor.jsโ Injects Readability + Turndown into the page MAIN world for reader mode. For SPA sites, uses the XHR cache from the matching content script.lib/sidepanel/pdf-extractor.jsโ PDF attachment pipeline: triespdf-inspector-wasm(full layout/table/heading reconstruction) in a dedicated Worker first, falls back to plain-textpdf.jsextraction, and quality-gates both (empty/scanned results don't get treated as success) before the caller falls back further to a plain URL attachment.lib/openai-client.jsโ Fetch-based SSE streaming client. Supports/v1/chat/completions(all providers) and/v1/runs(Hermes โ approval/clarification/tool-progress events, auto-detected).lib/storage.jsโchrome.storage.localwrapper. Global flat conversation history (not per-tab), session management, mask rules.- Content scripts (
lib/content-scripts/) โ Run atdocument_startin MAIN world. Wrapwindow.fetchandXMLHttpRequest.prototypeto observe SPA API calls and forward structured data to the background.
browsa/
โโโ manifest.json
โโโ background.js # service worker + message router (dispatcher only)
โโโ sidepanel.{html,css,js} # chat UI orchestrator
โโโ options.{html,css,js} # settings page
โโโ lib/
โ โโโ constants.js # shared constants (PAGE_CONTEXT_PREFIX, โฆ)
โ โโโ state.js # shared stream/approval state Maps (used by background.js)
โ โโโ openai-client.js # SSE streaming client
โ โโโ page-extractor.js # content extraction + site synthesizers
โ โโโ storage.js # chrome.storage wrapper + session mgmt
โ โโโ handlers/
โ โ โโโ chat-handler.js # CHAT case body
โ โ โโโ subchat-handler.js # SUBCHAT / SUBCHAT_ABORT case bodies
โ โ โโโ attach-summarizer.js # auto-compress long page/video attachments
โ โโโ markdown-chunker.js # structure-aware truncation + chunking (fences/tables never split)
โ โโโ sidepanel/ # sidepanel.js's feature modules
โ โ โโโ render.js # marked+DOMPurify+KaTeX+Mermaid+ECharts pipeline
โ โ โโโ reveal-pacer.js # smooth-reveal wrapper around vendored markstream-core
โ โ โโโ katex-threshold.js # "worth offloading to a worker?" heuristic
โ โ โโโ katex-worker-client.js # batches formulas to katex.worker.js, sync fallback
โ โ โโโ katex.worker.js # dedicated KaTeX rendering Worker
โ โ โโโ mermaid-utils.js # sequence-diagram semicolon fix + preview-height estimate
โ โ โโโ pdf-extractor.js # PDF attach: wasm-primary/pdf.js-fallback orchestration
โ โ โโโ pdf-inspector-worker-client.js # pdf-inspector-wasm Worker client (sticky-failure, timeoutโnull)
โ โ โโโ pdf-inspector.worker.js # dedicated Worker running pdf-inspector-wasm's processPdf()
โ โ โโโ sessions-ui.js # sessions drawer
โ โ โโโ multiselect.js # bulk-delete mode
โ โ โโโ msg-search.js # Ctrl+F in-conversation search
โ โ โโโ detail-thread.js # "select text โ ็ป่" side conversation
โ โ โโโ icons.js # ICONS SVG map
โ โ โโโ ui-utils.js # $, escM, sendMessage, toast/confirm, card helpers
โ โโโ content-scripts/ # MAIN-world XHR interceptors + ISOLATED-world toolbar
โ โ โโโ selection-toolbar.js # floating toolbar (Shadow DOM)
โ โ โโโ xhs-content-script.js # ๅฐ็บขไนฆ XHR interceptor
โ โ โโโ youtube-content-script.js # YouTube player API interceptor
โ โ โโโ bilibili-content-script.js # Bilibili video API interceptor
โ โ โโโ juejin-content-script.js # ๆ้ article interceptor
โ โ โโโ zhihu-content-script.js # ็ฅไน article / Q&A interceptor
โ โ โโโ twitter-content-script.js # Twitter/X GraphQL interceptor
โ โ โโโ xueqiu-content-script.js # ้ช็ stock/post interceptor
โ โ โโโ xiaoyuzhou-content-script.js # ๅฐๅฎๅฎ podcast interceptor
โ โ โโโ dedao-content-script.js # ๅพๅฐ interceptor
โ โ โโโ geektime-content-script.js # ๆๅฎขๆถ้ด interceptor
โ โโโ vendor/ # bundled third-party libs
โ โโโ Readability.iife.js
โ โโโ Turndown.iife.js
โ โโโ marked.bundle.js
โ โโโ purify.bundle.js
โ โโโ katex.bundle.js
โ โโโ highlight.bundle.js
โ โโโ mermaid.bundle.js
โ โโโ echarts.bundle.js
โ โโโ markmap-lib.bundle.js # Markdown โ mind map tree transformer
โ โโโ markmap-view.bundle.js # d3-zoom mind map SVG renderer
โ โโโ markstream-core.bundle.js # streaming-reveal pacing controller
โ โโโ stream-markdown-parser.bundle.js # Mermaid SVG sanitizer
โ โโโ pdf.bundle.js # pdf.js โ fallback PDF text extraction
โ โโโ pdf.worker.bundle.js # pdf.js's own Worker
โ โโโ pdf_inspector_wasm.js # pdf-inspector-wasm glue (wasm-bindgen)
โ โโโ pdf_inspector_wasm_bg.wasm # pdf-inspector-wasm binary โ primary PDF extraction
โโโ _locales/{en,zh_CN}/
โโโ icons/
โโโ build/
โ โโโ build.mjs # esbuild vendor bundler
โ โโโ package.mjs # distribution zip builder
โโโ test/ # node:test unit tests (576 tests)
โโโ check-compat.sh # MV3 / static compatibility check
| Browser | Status | Notes |
|---|---|---|
| Chrome 114+ | โ Supported | Primary target |
| Edge 114+ | โ Supported | Identical install |
| Brave 1.56+ | โ Should work | Same Chromium surface |
| Firefox | โ Not supported | No side_panel API |
- API keys are stored in
chrome.storage.localon your machine only โ never sent anywhere except your configuredbaseUrl. - PDF attachments are parsed entirely client-side (WebAssembly + pdf.js, both running in the side panel) โ the file's bytes are never uploaded anywhere, only the extracted text is sent to your configured provider.
- LLM replies are sanitized with DOMPurify before rendering, including a hook that blocks
data:image/svg+xmlsources (which can carry their own<script>/event handlers) while still allowing normal bitmapdata:images. - Mermaid diagram SVG output is sanitized before insertion (strips
<script>, event-handler attributes, and dangerous URLs) โ needed because Mermaid'ssecurityLevel:'loose'mode, required for KaTeX math inside diagram labels, otherwise permits arbitrary HTML through. - Content scripts only observe network requests; they never modify or block them.
MIT