I built a small Chrome extension, DevPause, that adds buttons under ChatGPT, Claude and Gemini answers: Clean copy, Shorter, Simpler, As a table, Example, Continue. This post is about the three technical decisions that shaped it, because I think they're useful to anyone building on top of AI chat UIs.
1. Don't read the conversation
The obvious way to build "buttons under answers" is to parse the answer text. I didn't want that — a content script that reads every conversation is a privacy liability even if it never sends anything.
So the rule became: the extension reads answer text only at the moment the user clicks Clean copy, and that text goes to the clipboard and nowhere else.
Everything else works on structure, not content:
- Answer blocks are found with a
MutationObserverat the container level. I only care that a new answer element appeared, not what's inside it. - The buttons for Shorter / Simpler / As a table / Example / Continue don't need the answer at all. They insert a fixed phrase into the composer (
"Make it shorter.","Present this as a table.", …) and submit. Submitting can be turned off in the popup if you prefer to edit the phrase first.
2. "Is the model typing?" = "Is the Stop button there?"
I needed a streaming signal for two features: the "answer ready" notification (when you started a long prompt and switched tabs) and the ad card (more on that below).
Every one of the three sites shows a Stop button while the model is generating, and removes it when done. That's a single boolean I can observe without touching text:
const isStreaming = () => !!document.querySelector(SITE.stopButtonSelector);
Each site gets its own selector set (chatgpt.com, claude.ai, gemini.google.com), and when a site changes its DOM, that's the only thing I have to fix. The observer debounces to avoid firing on every token.
The trade-off: if a site ever removes the Stop button pattern, the signal breaks. So far all three have had it since 2023.
3. Ad-supported, and what that means for the wire
DevPause is free for everyone with no paid tier. It's paid for by one text line (≤60 chars, one link) shown beside the chat while the answer streams. It disappears when streaming ends and never covers the conversation.
Ad-supported extensions have a bad reputation, mostly because of what they send. So the full list of what DevPause sends to its single server origin:
| Field | Why |
|---|---|
| random install id + server signature | count impressions, fraud protection |
| country and language chosen in the popup | ad matching |
| extension version | compatibility |
| ad id, seconds visible, clicked or not, which of the three sites | advertiser report |
| invite code from the install link, if any | referral counting |
No page URLs, no chat titles, no text. Country is guessed once from IP on first contact to prefill the popup, and you can change it. No remote code — the ad is data (text + URL), rendered by the extension's own template.
The ad policy is human-reviewed and stricter than the industry norm because I run it under Islamic rules: no interest-based finance, no gambling, no crypto trading, no adult/dating, no alcohol. If you're curious what that looks like as a document: ad policy.
Permissions
- Host permissions:
chatgpt.com,claude.ai,gemini.google.comonly -
storage,clipboardWrite,notifications - Manifest V3, service worker background, no
tabspermission
The zip ships unminified, so you can read every line. GitHub repo (PolyForm Noncommercial) is coming after launch; the Chrome Web Store listing is in review.
What I'd do differently
- I started with a "pay to remove ads" key system and then removed it. It made the free version feel crippled and it capped revenue. The product is now identical for everyone; there's a voluntary support link that unlocks nothing.
- I should have shipped the site in multiple languages from day one. The users of these three chatbots are everywhere; the site now runs in 10 languages.
Try it: https://devpause.vercel.app. If there's a button you wish existed, tell me in the comments — adding a prompt phrase is a one-line change.
Top comments (0)