A Chrome extension (Manifest V3) that reads the article in your current tab and summarises it with the Gemini API — streaming output, three lengths, side panel, and one-click PDF / Markdown export.
- Screenshots
- Features
- Quick Start
- Usage
- Project Structure
- Architecture
- Configuration
- Permissions & Privacy
- Scripts
- Troubleshooting
- Contributing
| Popup — streaming summary | Side panel — finished summary | Settings — key, model, length, theme |
|---|---|---|
![]() |
![]() |
![]() |
- Three summary lengths —
Brief(2–3 sentences),Short(~120–180 words),Bullets(5–8 key points). - Streaming output — tokens render as the model produces them, with Stop/Regenerate.
- Popup + side panel — quick popup for fast reads, persistent side panel for long articles.
- Summarise a selection — highlight any passage, right-click → Summarise selection with Lucid.
- Exports — styled A4 PDF, Markdown download, and Copy to clipboard.
- Resilient Gemini client — SSE streaming with thought-part filtering, plain-
generateContentfallback, and clear per-error messages. - Survives popup closes — in-flight state is mirrored to
chrome.storage.session; reopen anywhere to resume. - Light / dark / system themes — applied across popup, side panel, and settings.
- Safe rendering — model output goes through
react-markdownwith HTML skipped, so summaries can't inject markup.
| Requirement | Version |
|---|---|
| Node.js | ≥ 18 |
| Google Chrome | ≥ 116 (side-panel API) |
| Gemini API key | Free from Google AI Studio |
npm install
npm run buildThis typechecks, builds the extension pages with CRXJS into dist/, then bundles the standalone Readability extractor (dist/content-extract.js).
- Open
chrome://extensions. - Enable Developer mode (top right).
- Click Load unpacked and select the
dist/folder. - Pin Lucid to the toolbar.
- Click the Lucid icon → gear icon (Settings).
- Paste your Gemini key → Verify & save (it is validated against the API before saving).
- Pick a model (
Gemini 3.5 Flash-Liteis the safest default for free-tier quota), a default length, and a theme.
Your key is stored in
chrome.storage.local(this browser profile only — never synced) and is only ever sent togenerativelanguage.googleapis.comin anx-goog-api-keyheader. See Permissions & Privacy.
- Open any article (news, blog, docs, Wikipedia…).
- Click the Lucid toolbar icon.
- Choose Brief / Short / Bullets and hit Summarise.
- While streaming you can Stop; after finishing you can Regenerate, Export PDF, Copy, or save Markdown.
- For long reads, click the panel icon in the popup header to move into the side panel.
- To summarise just a passage: select text → right-click → Summarise selection with Lucid.
src/lib/*— framework-free modules (no React, no Chrome UI); unit-testable in Node.src/ui/*— shared presentational code used by all three surfaces.src/{popup,sidepanel,options}/*— thin per-surface entries (index.html+main.tsx).- Path alias
@/*maps tosrc/*(seetsconfig.json+vite.config.ts).
flowchart LR
A[Popup / Side panel] -- Port: START/CANCEL --> B[Service worker]
B -- chrome.scripting.executeScript --> C[Readability extractor]
C -- article text --> B
B -- SSE stream --> D[Gemini API]
D -- text deltas --> B
B -- CHUNK messages --> A
A --> E[PDF / Markdown / Clipboard]
- The service worker owns the API key and all network calls; UI surfaces only ever receive text deltas.
- Article extraction uses Mozilla Readability on a cloned document, with a
main/articlefallback; text is capped at 100,000 chars and the UI flags truncation. - The extractor is a separate IIFE bundle (
vite.content.config.ts→dist/content-extract.js) becausechrome.scripting.executeScript({ files })loads classic scripts. It parks its result on__LUCID_EXTRACT__for a follow-up call to collect. - The Gemini client (
src/lib/gemini.ts) streams via:streamGenerateContent?alt=sse, skips 3.xthoughtparts, tolerates non-SSE JSON-array bodies, and retries once via plain:generateContentif the stream is empty. - In-flight jobs live in
chrome.storage.session, so closing the popup mid-stream loses nothing.
| Model | Label | Best for |
|---|---|---|
gemini-3.8-flash |
Gemini 3.8 Flash | Most intelligent Flash model |
gemini-3.5-flash |
Gemini 3.5 Flash | Balanced speed and quality |
gemini-3.5-flash-lite (default) |
Gemini 3.5 Flash-Lite | Fastest, lowest cost / quota |
Note (Sept 2026 Google change): fresh API keys are blocked from
gemini-2.5-*(404 … no longer available to new users) even thoughmodels.liststill advertises them. This project targets the 3.x models above. Stored2.5values from older installs auto-migrate to the new default vianormaliseSettings().
| Mode | Output contract |
|---|---|
| Brief | Exactly 2–3 sentences, one plain paragraph |
| Short | One ~120–180-word paragraph, argument → facts → conclusion |
| Bullets | 5–8 one-sentence bullets (- prefix, ≤ 25 words each) |
Prompts force source-language matching, plain-Markdown-only output, and treat page content as data (prompt-injection resistant).
| Permission | Why it is needed |
|---|---|
storage |
Save key/preferences (local); hold in-flight summaries (session) |
activeTab + scripting |
Read article text — only from the tab you explicitly act on. No always-on content script, no broad host access |
sidePanel |
Open the persistent summary panel |
contextMenus |
The Summarise selection right-click item |
host_permissions: generativelanguage.googleapis.com |
Call the Gemini API — the only network destination |
- Page text is sent to Google's Gemini API to produce the summary. Nothing is sent anywhere else, nothing is stored off your machine.
- Extension local storage is not encrypted. Anyone with access to this computer profile could read the key — treat it like any local credential and revoke it in AI Studio if exposed.
| Command | What it does |
|---|---|
npm install |
Install dependencies |
npm run dev |
Vite dev server with HMR for extension pages (does not rebuild the extractor) |
npm run build |
tsc --noEmit + main CRXJS build + extractor IIFE build → dist/ |
npm run build:content |
Rebuild only the extractor (run after editing src/content/extract.ts) |
npm run typecheck |
Typecheck only |
npm run preview |
Preview the Vite build |
npm run clean |
Remove dist/ |
node scripts/generate-icons.mjs |
Regenerate public/icons/ PNGs from the vector spec |
| Symptom | Cause / Fix |
|---|---|
Add your Gemini API key in Settings |
No key saved — open Settings (gear icon) and verify & save one. |
That API key was rejected / not authorised |
Wrong, revoked, or API-disabled key. Recreate it in AI Studio. |
That model is not available for your key (404) |
Fresh keys can't use retired 2.5 models. Pick 3.5 Flash-Lite / 3.8 Flash in Settings and reload the extension. |
Gemini returned an empty summary… |
Model answered with no text (thought-only/filtered) — retry Bullets mode or another model. The client already retries once via non-streaming generateContent. |
Rate limit reached |
Free-tier per-minute quota. Wait, or switch to Gemini 3.5 Flash-Lite. |
Could not read this page / does not have access yet |
Tab opened before install, or permission not yet granted. Reload the tab, click the Lucid icon on that page, retry. |
Nothing to read here / Not enough readable text |
chrome://, Web Store, PDFs, login walls, and very short pages are unreadable by design. Try selection mode on a passage. |
Gemini is having trouble right now |
Google-side 5xx. Wait and retry. |
| Summary from another tab shows as empty | By design — a summary belongs to its tab (job.tabId). Switch back to the original tab. |
Changed extract.ts but nothing changed |
npm run dev doesn't rebuild it — run npm run build:content. |
Still stuck? Open the service-worker console (chrome://extensions → Lucid → Inspect views: service worker) and look for [Lucid] empty Gemini stream — it logs { model, mode, state } (event count, candidates seen, block reason), which pinpoints the failure class.
npm install→npm run devfor UI work;npm run build:contentafter extractor changes.npm run typecheckmust pass;npm run buildmust produce a loadabledist/.- Keep secrets out of the repo — the API key lives only in your browser profile, never in code.
- Small, focused commits; don't commit
dist/,node_modules/, or exported*-summary.pdf/mdfiles (all git-ignored).
Private project — all rights reserved. Dependencies retain their own licenses (see package.json).


