Proofread and rewrite text anywhere in Chrome — entirely on your own machine.
Powered by Gemini Nano. No API key, no server, no account, and no network request carrying your text.
Select text on any page, right-click → WriteWell, pick an action. The result streams into a floating card with a Copy button.
This is the central design decision, and everything else follows from it. The extension reads your selection and shows you a result — it never writes back into the page. You copy and paste yourself.
That sounds like a limitation. It buys a lot:
- Nothing can be destroyed. No fighting React controlled inputs that revert changes, no corrupting rich-text structure in Notion or Google Docs, no breaking the page's undo stack, no firing input events that trip the page's own listeners. That is where extensions in this category accumulate most of their bugs, and WriteWell doesn't have the category.
- It works on text you can't edit. Polish a sentence in a document you don't own, or on a page you're only reading.
- The permission set stays tiny. See Privacy.
| Action | What it does | API |
|---|---|---|
| Check grammar | Fixes spelling, grammar and punctuation, preserving your wording. Shows the original with each correction highlighted, and the corrected text below. | Proofreader |
| Polish | Improves clarity and flow while keeping your meaning and voice. | Prompt |
| Friendly | Warmer, more casual tone. | Rewriter |
| Formalize | More formal register. | Rewriter |
| Shorten | More concise, without losing essential meaning. | Rewriter |
Check grammar is the only action that renders a diff, because the Proofreader is the only API that reports which ranges it changed. The rest return prose:
Gemini Nano's hardware bar is high, and a meaningful share of machines will not clear it:
- Chrome 138+ on Windows 10/11, macOS 13+, Linux, or a Chromebook Plus
- ~22 GB free on the drive holding your Chrome profile
- More than 4 GB of GPU VRAM. Integrated GPUs can qualify — Chrome appears to count shared system memory — so don't rule yours out based on the dedicated-VRAM figure alone.
- 16 GB RAM and 4+ CPU cores
- An unmetered connection for the one-time model download, which is several GB
Check before installing. Open any page, press F12, and run:
await LanguageModel.availability()available or downloadable means you are good to go. unavailable means this
device cannot run the model — WriteWell will say so plainly rather than spinning
forever. English only for now.
Not on the Chrome Web Store yet. To run it:
git clone https://github.com/zeroth/WriteWell.git
cd WriteWell
npm installThen:
- Open
chrome://extensionsand turn on Developer mode (top right) - Click Load unpacked and select this directory
- Open WriteWell's Details → Extension options and click the download
button to fetch the model. This has to happen on that page:
create()requires user activation while a model is missing, and a service worker never has any. - Select text on any page → right-click → WriteWell
npm install is only needed for the tests. The extension itself has no build
step and no runtime dependencies — what is on disk is what Chrome runs.
The Proofreader and Rewriter APIs shipped as origin trials, and those trials have lapsed. On a stock Chrome they report as unavailable, and WriteWell falls back to the Prompt API automatically — all five actions keep working with somewhat weaker output, and Check grammar returns a plain rewrite instead of a highlighted diff.
To get the real Proofreader locally, enable chrome://flags/#proofreader-api and
relaunch Chrome. That is currently the only way to exercise the diff.
right-click / keyboard shortcut
│
▼
service worker ──── injects the content script via activeTab
│
▼
content script ──── reads window.getSelection() itself,
│ never info.selectionText (Chrome truncates it)
▼
long-lived Port
│
▼
service worker ──── owns every AI session
│
▼
ai-client ──── picks the API, streams deltas back
│
▼
card ──── shadow root: renders, copies
Two constraints shaped this:
Inference runs in the service worker, reached over a chrome.runtime Port
rather than one-shot messages. Port traffic resets the MV3 idle timer, which
matters because a long generation would otherwise get the worker killed
mid-stream.
Onboarding exists because of user activation. create() demands it while the
model is undownloaded, a service worker never has it, and it cannot be forwarded
between contexts — so the download must be started by a real click on an
extension page. After that first run the model is available browser-wide and the
worker creates sessions freely.
manifest.json MV3; five permissions, no host_permissions
src/background/
actions.js the five actions, as a data table
ai-client.js the ONLY file that names Chrome's AI globals
service-worker.js context menus, keyboard command, port routing
src/content/
content.js the only file that touches the host page
card.js / card.css shadow-DOM result card
selection.js selection reading, viewport clamping
src/onboarding/ setup page; doubles as the options page
src/shared/
protocol.js message types shared by both sides
corrections.js Proofreader ranges → highlight segments
Adding a sixth action is a row in actions.js. Every trial-gated action carries a
Prompt API fallback there, which is why a lapsed origin trial degrades output
instead of breaking the extension.
npm testnpm run test:watchRequires Node 22.22+ or 24.15+ — see engines in package.json for the
authoritative range. Vitest and jsdom are the only dev dependencies.
After changing anything, press reload on WriteWell's card in
chrome://extensions, then refresh any page you want to use it on. A page still
running the old content script shows a [WriteWell] console warning telling you
to reload, rather than silently doing nothing.
Gemini Nano cannot run in CI. No headless runner has a 22 GB profile volume and 4 GB of VRAM, so every automated test here runs against fakes. The suite covers what fakes can honestly cover: correction-range math, action routing and fallback selection, session caching and cleanup, card rendering, viewport clamping, and the port protocol.
Everything a fake cannot reach lives in
docs/manual-checklist.md, a written checklist run by
hand against a real browser. It is not a nicety — it is the only verification that
ever touches a real model, and it has already caught bugs the suite could not,
including a missing output-language declaration and a highlight painted over bare
whitespace.
If you change behaviour only a browser can observe, add a checklist item. Never tick a box you have not personally performed.
WriteWell requests exactly five permissions: contextMenus, activeTab,
scripting, storage, notifications.
There is no host_permissions and no <all_urls> content script. No code
runs on any page until you explicitly invoke WriteWell there, and activeTab
grants access only to the tab you invoked it in. Your text is processed by a model
on your own machine and is never transmitted anywhere. There is no analytics, no
telemetry, and no account.
docs/manual-checklist.md— the browser checks CI cannot dodocs/superpowers/specs/2026-08-17-writewell-design.md— the design, and why it is shaped this waydocs/superpowers/plans/2026-08-17-writewell.md— the implementation plan, including as-built notes where review found the plan's own reference code defective
MIT — do what you like with it, no warranty.


