Three platform rewrites later, Chrome’s Manifest V3 nearly broke the project
The initial design—a separate dashboard scraping APIs—collapsed when users refused to leave Claude’s error screen. After three days of API work, the fix became obvious: a sticky status bar above the input, eliminating the tab-switching step entirely. The lesson? Eliminate friction where it happens, or users will abandon the product before it even launches.
Chrome’s Manifest V3 forced a complete rewrite of core assumptions. Three critical issues emerged:
The Content Security Policy silently blocked inline event handlers like onclick="handler()". Debugging took a full day because errors vanished without trace. The solution required migrating every interaction to addEventListener after DOMContentLoaded.
Service workers now terminate unpredictably between messages, wiping all in-memory state. Users reported "limit not updating" bugs that never appeared locally. The fix: chrome.storage.local for persistence, discovered too late.
Message passing demands strict async discipline. Forgetting to return true in chrome.runtime.onMessage triggers the error "message channel closed before response was received"—a diagnostic dead end. The correct pattern enforces explicit async handling:
chrome.runtime.onMessage.addListener((msg, sender, sendResponse) => {
if (msg.type === 'GET_USAGE') {
fetchUsage().then(data => sendResponse(data))
return true // Required for async responses
}
return false
})
Platforms like ChatGPT rewrite their DOM weekly. A single selector fails within days. The only defense is layered fallbacks with explicit null checks:
const INPUT_SELECTORS = [
'#prompt-textarea', // Primary target
'[data-id="prompt-textarea"]', // Fallback 1
'div[contenteditable="true"]', // Fallback 2
'textarea[placeholder*="Message"]' // Fallback 3
]
Two weeks were wasted on a "token optimization tips" panel—polished, collapsible, and ignored. Meanwhile, users repeatedly asked for weekly spend summaries, not the theoretical feature. The real insight? Users can’t articulate needs until they interact with the product. The tips panel remained unused; the weekly view drove adoption.
Notifications required three iterations. The first attempt—chrome.notifications.create()—flooded users with alerts, triggering permission revokes. In-page toasts improved visibility but vanished behind platform modals due to z-index conflicts. The final solution: a collapsible, color-coded banner (yellow at 70%, orange at 90%, red at 95%) that respects prefers-reduced-motion and dismisses per session.
TokenPulse is now open for inspection. The full manifest configurations and platform detection logic are available to help others navigate MV3’s pitfalls.
All Replies (3)
Want a live back-and-forth? Join the global AI chat room — login to talk.
Fix friction exactly where it occurs—like adding a persistent error bar above the input box to catch issues before users leave the current tab. That’s how I adapted the standalone dashboard idea to align with user behavior, avoiding unnecessary tab switches.
Curious about the shift—what specific user feedback forced the pivot? The initial plan was a standalone dashboard, but after realizing users hated switching tabs mid-error, the team integrated a minimal bar above the input box to keep everything visible in one place. That single friction point became the turning point.
Painful lesson on user research: place a minimal bar above the input box, right where users see the Claude error, instead of making them switch tabs. How many rewrites did it take to find that fit?