A Chrome extension that lets users transform any webpage using natural language instructions, powered by Google Gemini and gated by Auth0 sign-in.
The user types a prompt in the side panel (e.g. "hide all MrBeast videos" or "make this page easier to read") and the extension applies DOM transformations to the live page in real time — no page reload required.
Chrome Extension (MV3)
├── background.js — opens side panel on icon click
├── content_script.js — injected into all pages; collects DOM snapshot, applies ops
└── panel/ — built React side panel (Vite output)
sidepanel/ (React/Vite source)
└── App.jsx — UI: prompt input, preset buttons, status/log display
backend/ (Python)
├── main.py — FastAPI server, calls Gemini, returns ops JSON
└── requirements.txt
- User types a prompt in the side panel
- Side panel sends
getSnapshotto the content script — returns the top 300 meaningful DOM elements (tag, id, class, aria-label, href, text) - If needed, the side panel asks Auth0 to log the user in through Universal Login
- Side panel POSTs
{ prompt, snapshot }tohttp://localhost:8000/transformwith an Auth0 bearer token - FastAPI validates the Auth0 access token, sends the prompt + snapshot to Gemini (
gemini-flash-latest), and gets back a structured JSON instruction object - Side panel sends
{ type: "applyOps", ops }to the content script - Content script executes ops in order: restyle → inject → hide → remove
- Changed elements flash green (added/restyled) or red (hidden/removed) as visual diff feedback
Gemini always returns exactly this shape:
{
"remove": ["css selectors"],
"hide": ["css selectors"],
"restyle": { "selector": "css string" },
"inject": [{ "tag": "div", "id": "id", "text": "text", "css": "inline css" }]
}hide— setsdisplay: none, preferred over removeremove— callselement.remove()restyle— injects a<style>tag into the page headinject— creates and appends new DOM elements; supports apayloadfield for raw HTML
Four one-click presets that apply hardcoded ops with no API call:
| Preset | Effect | Best on |
|---|---|---|
| Reader | Comfortable reading typography, max-width body | Wikipedia, Medium |
| Cinematic | Full dark mode via CSS color overrides | Wikipedia, news sites |
| Sensory | Warm muted palette, desaturated images | Any article page |
| Focus | Dark navy background, spotlight content area | Wikipedia, The Verge |
- Extension: Manifest V3, vanilla JS content/background scripts
- UI: React 18 + Vite 5, built into
extension/panel/ - Backend: Python FastAPI +
google-genaiSDK - AI:
gemini-flash-latestvia Google Gemini API
cd backend
pip install -r requirements.txt
$env:GEMINI_API_KEY="your_key_here"
$env:AUTH0_DOMAIN="your-tenant.us.auth0.com"
$env:AUTH0_CLIENT_ID="your_auth0_spa_or_native_client_id"
$env:AUTH0_AUDIENCE="https://ibrowse-api"
uvicorn main:app --reload --port 8000You can also copy backend/.env.example to backend/.env and fill the values there.
Use your existing Auth0 tenant, but configure it for this architecture instead of the Flask quickstart:
- Create or reuse an Auth0 application for the Chrome extension login flow.
- In that Auth0 application, add these Allowed Callback URLs:
https://<your-extension-id>.chromiumapp.org/auth0https://<your-extension-id>.chromiumapp.org/auth0-logout
- Add the same logout URL to Allowed Logout URLs.
- Create or reuse an Auth0 API with an identifier such as
https://ibrowse-api. - Set
AUTH0_AUDIENCEin the backend to exactly that API identifier. - Enable the connections you want in Universal Login, such as Google, GitHub, and Username-Password-Authentication.
cd extension/sidepanel
npm install
npm run build
# outputs to extension/panel/- Go to
chrome://extensions - Enable Developer Mode
- Click "Load unpacked" → select the
extension/folder - Click the I Browse icon in the toolbar to open the side panel
- The content script runs in the page context and communicates with the side panel via
chrome.tabs.sendMessage - The side panel is a Chrome side panel (not a popup) — it persists across navigation
- The extension now requires an Auth0 session before presets or prompts are usable
- Presets apply ops directly without hitting the backend
restyleops inject a<style>tag — they stack and are not reversible without a page reload- The snapshot deliberately skips pure container divs to keep token usage low
gemini-flash-latestis used becausegemini-2.0-flashandgemini-2.5-flashare unavailable on the free tier API key in use