AI-powered page summarizer for Chrome.
Get the gist of any article in seconds.
Manifest V3 · React 18 · TypeScript · Tailwind v4 · Vite
- Three summary styles — TL;DR, bullet points, or a detailed multi-paragraph breakdown
- Local history — up to 50 past summaries stored via
chrome.storage.local - One-click copy — with "Copied!" feedback
- Privacy-first — your API key never leaves your browser; calls go directly to OpenAI, Anthropic, or Google
- Free tier available — Google Gemini's free tier (15 req/min, no credit card) is a zero-cost starting point
- Minimum-privilege manifest —
activeTabonly, plus scoped host permissions for the three API endpoints
(Describe what the store screenshots would show — replace with real PNGs before submission.)
- Main view — header with the Quick Intel bolt mark, summary-style dropdown, primary "Summarize this page" button, and a clean card with the generated summary.
- History tab — list of past summaries, each showing title, source URL, style badge, and relative timestamp. Hover reveals a delete button.
- Settings — provider toggle (OpenAI / Anthropic / Gemini), model input, and a password-style API key field with "Show" / "Hide" toggle and a "Key saved" indicator.
- Error state — rose-colored banner with a clear title, specific message, and a hint on how to recover.
cd quick-intel
npm installnpm run buildThis will:
- Generate icon PNGs (16/32/48/128) from the SVG source
- Type-check with
tsc - Bundle with Vite + CRXJS into
dist/
For watch-mode development:
npm run dev- Open
chrome://extensions - Toggle Developer mode (top right)
- Click Load unpacked
- Select the
dist/folder - Pin the Quick Intel icon to your toolbar for quick access
- Click the Quick Intel icon
- Go to the Settings tab
- Pick a provider (OpenAI, Anthropic, or Gemini)
- Paste your API key → Save settings
Where to get a key:
- Google Gemini (free) → https://aistudio.google.com/apikey — free tier, 15 req/min, no credit card required
- OpenAI → https://platform.openai.com/api-keys
- Anthropic → https://console.anthropic.com/settings/keys
Navigate to any article → click the Quick Intel icon → pick a style → Summarize this page. The result is copyable and auto-saved to your history.
For developer convenience only. Copy .env.example to .env.local:
VITE_DEFAULT_PROVIDER=openai
VITE_DEFAULT_MODEL=gpt-4o-miniNever bake a real API key into the build. These defaults only control the Settings panel's initial values; the real key is always user-entered and stored in chrome.storage.local.
- The popup never sees the API key. It only queries
GET_PUBLIC_SETTINGS, which returns a redacted{ hasApiKey: boolean, model, provider }. - All LLM calls happen in the service worker. The key is read from
chrome.storage.local, used in afetch, and never returned to the popup. - The manifest's
host_permissionsis scoped to the three API endpoints only. Page content is accessed viaactiveTab+chrome.scripting.executeScript— no broad<all_urls>grant. - A strict Content Security Policy restricts
connect-srcto the three API hosts.
Service workers are event-driven. They spin up on an event (message, install), run the handler, and are terminated when idle. Quick Intel's worker never relies on in-memory state — every handler reads/writes through chrome.storage.local. The top-level chrome.runtime.onMessage.addListener re-registers on every wake-up, which is the intended pattern.
All popup ↔ background communication uses a discriminated union (RuntimeMessage) in src/types/index.ts. The worker's onMessage handler returns true to keep the channel open for async sendResponse.
AppError with codes (NO_API_KEY, RESTRICTED_PAGE, EMPTY_CONTENT, API_ERROR, INVALID_RESPONSE, ...) is thrown in the worker, serialized into { code, message }, and rendered by <ErrorBanner/> using a friendly message map.
Not a persistent content script. extractPageContent() is injected via chrome.scripting.executeScript({ func: ... }) only when the user clicks Summarize. Lower footprint, less privacy surface, faster page loads for the user.
| Command | What it does |
|---|---|
npm run dev |
Watch-mode build for local development |
npm run build |
Type-check + production build into dist/ |
npm run build:icons |
Regenerate icons from SVG source |
npm run package |
Build + zip dist/ into quick-intel.zip for Chrome Web Store upload |
npm run clean |
Remove dist/ |
npm run package→ producesquick-intel.zip- Go to the Chrome Web Store Developer Dashboard
- Upload the zip, fill in store listing, privacy disclosures (this extension uses
storage,activeTab,scripting— be explicit about the LLM API calls), and screenshots - Submit for review
src/
├── manifest.json MV3 manifest (CSP, minimum-privilege perms)
├── background/service-worker.ts Message router, summarization orchestrator
├── content/extractor.ts Injected via chrome.scripting (self-contained)
├── popup/
│ ├── App.tsx Tab container, state root
│ ├── components/ Header, SummaryCard, HistoryList, ...
│ └── index.css Tailwind v4 + theme tokens
├── utils/
│ ├── llm.ts Provider-agnostic summarize() + validators (worker-only)
│ ├── storage.ts chrome.storage.local wrapper + redacted public view
│ ├── clipboard.ts Copy with fallback
│ ├── errors.ts AppError + code taxonomy
│ ├── logger.ts Dev-only gated logger
│ └── url-guard.ts Restricted-URL detection
└── types/index.ts Shared types + message protocol
| Symptom | Fix |
|---|---|
| "This page can't be summarized" | Browser-internal URLs (chrome://, Web Store) are blocked by Chrome, not us. Use a regular site. |
| "Not enough content" | Page may still be loading, or it's mostly rendered client-side. Wait a moment and retry. |
| OpenAI 401 / Anthropic 401 / Gemini 400 | Key is invalid or missing. Check Settings. OpenAI keys start with sk-; Anthropic keys start with sk-ant-; Gemini keys start with AIza. |
| Popup shows blank after code change | Reload the extension from chrome://extensions. |
| Service worker not updating | In chrome://extensions, click the refresh icon on the Quick Intel card. |
MIT