There's a special kind of pain reserved for 2am debugging sessions. You know the one: the monitor's glow, a half-drunk mug of cold coffee, and a parade of errors that make you question every career choice. That's where DebugClip was born not from a hackathon or grand vision, but out of sheer frustration and the burning need to make things easier for myself.
1. The Night It All Started
1.1 The Breaking Point
It was February. 2am. I was knee-deep in a Next.js client project. The page was toast. DevTools lit up like a Christmas tree: hydration errors, failed API calls, an unhandled promise rejection from a dependency I didn't even write, and a CSP violation for good measure.
1.2 The Copy-Paste Loop
I did what everyone does: copied the first error and pasted it into ChatGPT. The response? "This could be caused by several things…" followed by a list so generic it could've applied to a toaster.
Of course it was generic I'd given it a single line, no stack trace, no response body, no context. So, back I went. Stack trace, Next.js version, TypeScript, failed API response. Copy, paste, repeat. Slightly better answer, but still missing the mark because it didn't know what my API returned.
By the time I'd gathered all the relevant info from DevTools and fed it to ChatGPT, eight minutes were gone. For one error. I had six more. And the kicker: my browser already had everything ChatGPT needed. Why was I doing all this manual copying between tabs?
2. The "What If?" That Changed Everything
2.1 The Spark
What if the browser could capture all the key details stack traces, requests, responses, context and wrap it into a perfect AI prompt with one click? That was the spark. The next morning, I started coding. DebugClip was born.
3. Tech Stack Decisions and Why They Mattered
I'm a solo dev. No team, no budget, just me. Every technology decision came down to: Will this save me time? Will it just work?
3.1 React + TypeScript + Vite: The Core Trio
I tried vanilla JS for my extension. Lasted two hours. The UI I wanted tabs, real-time updates, settings would be a nightmare without components. React was a no-brainer. TypeScript was essential for sanity: complex message passing between popup, background worker, and content scripts meant types were my safety net.
Vite? Most tutorials say webpack, but Vite's dev speed is unreal. Hot reload for extension development shaved days off my timeline. There's a plugin @crxjs/vite-plugin that handled Chrome's Manifest V3 pipeline. No contest.
3.2 Manifest V3: Not a Choice, a Mandate
Google deprecated Manifest V2, so I had to build for V3. That meant no persistent background scripts, no remote code, and stricter CSP. These limits forced me to rethink how everything worked, from storing state to injecting scripts.
3.3 Tailwind in the Popup: Tiny Canvas, Big Decisions
The popup is 460px wide. Every pixel is precious. Tailwind let me iterate fast, without context-switching between files. The dark mode was painless, and I spent way more time on layout and information density than on any other part.
3.4 Cloudflare Workers: The $0 Backend
All my backend needed to do was validate license keys. Cloudflare Workers start instantly and, at my scale, are free. No servers, no Docker, no "but it works on my machine." One command to deploy. API costs? Still zero.
3.5 Next.js for the Marketing Site
I wanted a static site with good SEO. Next.js 15 with static export gave me React's architecture, but shipped pure HTML and CSS from Vercel's edge. No runtime, no server, just push to git and go live.
4. The Architecture That (Mostly) Worked
4.1 Capturing Errors From Any Website
Chrome extensions run content scripts in an "isolated world." They can't see console errors, fetch failures, or unhandled rejections. So I needed to get code into the page's actual JS context.
4.1.1 Approach 1: Script Injection
Using chrome.scripting.executeScript with world: "MAIN", I could patch console.error, wrap fetch, and listen for errors. This worked almost everywhere except sites with strict CSP like Gmail or banking apps.
// The concept: patch console.error in the page's main world
const originalError = console.error;
console.error = function(...args) {
// Store the error for the extension to read later
window.__captured_errors.push({
message: args.map(String).join(' '),
timestamp: Date.now(),
stack: new Error().stack
});
// Always call the original so nothing breaks
originalError.apply(console, args);
};
// Wrapping fetch to catch failures
const originalFetch = window.fetch;
window.fetch = async function(...args) {
const start = Date.now();
try {
const response = await originalFetch(...args);
if (response.status >= 400) {
// Store failed request details
captureNetworkFailure(args[0], response, Date.now() - start);
}
return response;
} catch (err) {
captureNetworkFailure(args[0], null, Date.now() - start);
throw err;
}
};
4.1.2 Approach 2: Chrome DevTools Protocol
Chrome's debugger API gives DevTools-level access, capturing everything regardless of CSP. The tradeoff? It shows a "Debugger attached" banner, so I only enable it when users open the popup.
Both strategies feed into the same error store, with dupe detection. One silent, one opt-in.
4.2 Living With Manifest V3's Service Worker
Manifest V3's service worker is not a background page. It dies when idle, taking all in-memory state with it. No persistent WebSocket, no reliable globals.
My fix: keep transient data in memory, but immediately persist anything important (settings, license, history) to chrome.storage.local.
The subtlest bug: if you don't return true from the async onMessage handler, the service worker can die mid-response. I lost days to a bug where license activation "sometimes worked" because the worker would vanish during the API call.
4.3 Injecting Prompts Into AI Chat
"Open ChatGPT, paste the prompt." Sounded simple. It wasn't.
ChatGPT, Claude, Gemini, DeepSeek, and Copilot are all React SPAs. Open a new tab, and the input field doesn't exist yet. You have to wait for the app to render, find the right element (which changes constantly), and inject text in a way React actually notices.
// The native setter trick for React-controlled inputs
const textarea = document.querySelector('#prompt-textarea');
const nativeSetter = Object.getOwnPropertyDescriptor(
HTMLTextAreaElement.prototype, 'value'
).set;
nativeSetter.call(textarea, promptText);
textarea.dispatchEvent(new Event('input', { bubbles: true }));
ProseMirror editors (like Claude) need you to focus, select all, and use document.execCommand("insertText"). For React, just setting .value does nothing you have to do a native setter trick and dispatch a synthetic input event. Add in timing issues, and none of the usual tricks (MutationObserver, DOMContentLoaded) were reliable.
My answer: try injection at 1.5s, 3.5s, 6s, and 10s after load. Flag once injected, and bail if it's already done. Not elegant, but it works.
5. The Debates in My Head
5.1 One-Time Pricing vs Subscription
DebugClip runs locally. My API costs are basically nil. Monthly pricing would force me to add features just for the sake of it. One-time pricing means users aren't haunted by "Am I using this enough?" and I don't have to worry about churn.
$4 for Pro, $19 for Ultimate. Lifetime. That felt honest for a tool that saves real developer time.
5.2 Supporting 6 AI Providers
Developers have strong opinions about their AI stack. Some love Groq (it's free and fast), some have OpenAI credits, some get Anthropic through work, others want local-first like DeepSeek.
Supporting just one would alienate the rest. So I normalized all the APIs under one function. Adding a new provider? About 20 lines of code.
5.3 11 Templates, Not One
A React hydration error needs different context than a Node.js fetch error. Templates wrap raw errors in the right persona and instruction set, so the AI actually "thinks" in your stack. Quality of answers shot up when I did this.
6. Try DebugClip
If you're tired of the copy-paste dance between DevTools and ChatGPT, DebugClip's live on:
And if you're a solo dev thinking about launching your own tool? Ship it. The first version will be rough. That's fine. One real user's feedback beats three weeks of extra polish every time.
Originally published on Medium


Top comments (0)