This document describes the Summarize feature of the Wayback Machine Client-Side AI extension. It covers how the feature works end-to-end, which files are involved, and how the data flows between the background service worker, content script, and the on-device AI.
The Summarize feature generates a concise 2–3 sentence summary of an archived web page directly in the browser using the on-device Gemini Nano model (the built-in Prompt API). No page content ever leaves the machine. In addition to the summary, the feature extracts structured insights — interesting FAQ questions and notable personalities mentioned on the page — and renders them in an accordion-style UI.
The feature also supports live translation: if the user has selected a non-English target language in the extension popup, the summary and insights are translated on-device using the built-in Translator API.
- Open any archived page (e.g.
https://web.archive.org/web/20240101120000/https://example.com). - Right-click anywhere on the page → Wayback Machine AI Helper → Summarize Page.
- Alternatively, click the extension toolbar icon and press the Summarize action card.
The result overlay appears in the top-right corner of the page with:
- A Summary tab containing the generated summary.
- A translated-language tab (only if a non-English target language is configured).
- A Logs tab showing the streaming raw output and processing steps.
- An Insights section with collapsible FAQ items and famous-personality chips.
The context menu item summarize is registered under the parent wbm-parent:
contextMenus.create({ id: "summarize", parentId: "wbm-parent", title: "Summarize Page", contexts: ["page"] })
contextMenus.onClicked forwards the menuItemId to handleAction(action, tab).
The popup's Summarize card sends a PERFORM_ACTION message with action: "summarize" and the active tabId. The background listener looks up the tab and calls handleAction("summarize", tab).
Both paths converge on handleAction(action, tab) in background.js.
Before doing any AI work, handleAction validates the tab:
| Check | Function | Failure message shown |
|---|---|---|
Page is a playback page (web.archive.org/web/*) |
isPlaybackPage(tab.url) |
"This page is not a valid archive. Please navigate to a specific snapshot." |
| Browser exposes the built-in Prompt API | isBrowserSupported() (typeof LanguageModel !== "undefined") |
"Built-in AI is not supported in this browser." |
| Model is downloaded & ready | checkAIAvailability() (LanguageModel.availability() not unavailable/downloadable) |
"Built-in AI is not supported." |
The target language is read from chrome.storage.sync (targetLanguage, default "en").
Summarize results are cached in chrome.storage.local under:
wbm_summarize_<page-url>_<target-language>
- On a cache hit, the overlay is created and the cached summary (
TRANSLATED_RESULT) plus cached insights (STRUCTURED_INSIGHTS) are served instantly — no AI call is made. - On a miss, the result is stored after generation (only on success), with a
timestampfield. - Insights are cached separately under
wbm_insights_<page-url>_<target-language>. - The
StorageCleaner(ai/storageCleaner.js, instantiated at startup) runs a sweep once per 24h and evicts everywbm_*entry older than 1 day (runSweep(1)).
handleAction sends STREAM_START to the content script:
chrome.tabs.sendMessage(tab.id, { type: "STREAM_START", action, targetLanguage });The content script (content/content.js) calls createStreamingOverlay(action, targetLanguage, showInsights) (content/ui/overlay.js), which builds the tabbed overlay (Summary / translated tab / Logs) and wires up the accordion toggles. showInsights is true for summarize.
handleAction sends REQUEST_CONTENT with action: "summarize". The content script's analyzePage(sendResponse, action) (content/services/pageAnalyzer.js) calls extractPageContent() (content/services/contentExtractor.js), which:
- Clones the DOM and removes the Wayback toolbar (
#wm-ipp-base). - Runs Mozilla Readability.js on the clone.
- If successful and the extracted text is longer than 100 chars, prefixes the article title and returns up to 4000 chars.
- Otherwise falls back to a manual extraction that concatenates
article,main,pelements (ordocument.body.innerText).
If fewer than 100 chars are extracted, a "Not enough content to analyze" result overlay is shown and the request aborts.
The background calls aiSession.analyzePage(...) (ai/utility.js):
- Ensures the shared main session exists (
init()). - Creates a lightweight clone of the session via
session.clone()so the main session is untouched. - Builds the prompt:
"Summarize this archived web page in 2-3 sentences: {pageContent}". - Calls
promptStreaming(promptInput, streamOptions)and, for every chunk, forwards aSTREAM_CHUNKmessage to the tab. The content script'sappendStreamChunk()streams the raw text into the Logs tab (which also drives the process-step list). - If
targetLanguageis set and not"en", the finished summary is translated withtranslateResult()and the result object carries bothsummary(translated) andoriginalSummary(English).
While the summary streams, getStructuredInsights(pageContent) runs in parallel (via Promise.all):
- Uses a dedicated
insightSession(initialized frominit()). - Sends a prompt asking for 3–5 FAQs and 2–4 famous people, constrained with a strict JSON schema (
responseConstraint). - Parses the response and returns
{ faqs: [...], famousPeople: [...] }.
The background sends TRANSLATED_RESULT:
content.jspopulates the Summary accordion withmarked.parse(summary)and marks process steps as complete.- The result is cached under
wbm_summarize_...(summarize only — quality results are intentionally not cached).
If insights were produced, the background sends STRUCTURED_INSIGHTS:
appendInsights()renders FAQ items (click-to-expand) and famous-personality chips with hover tooltips linking to Wikipedia.- If a translation is needed,
translateInsights()is called first and the English originals are kept for the tooltips. - Insights are cached under
wbm_insights_....
- The overlay renders an extra tab for the target language and a
<select>to switch languages on the fly. - Switching tabs triggers a
TRANSLATE_TEXTmessage to the background, which uses the Language Detector + Translator APIs:LanguageDetector.create()detects the source language.Translator.create({ sourceLanguage, targetLanguage })translates line-by-line.
- The background replies with
TRANSLATE_TEXT_RESPONSE, and the content script fills the target-language tab.
| Message | Direction | Purpose |
|---|---|---|
PERFORM_ACTION |
popup → background | Trigger summarize from the popup |
STREAM_START |
background → content | Create the streaming overlay |
REQUEST_CONTENT |
background → content | Ask for extracted page text |
STREAM_CHUNK |
background → content | Stream the raw AI output into Logs |
TRANSLATED_RESULT |
background → content | Deliver the (possibly translated) summary |
STRUCTURED_INSIGHTS |
background → content | Deliver FAQs + famous people |
TRANSLATE_TEXT / TRANSLATE_TEXT_RESPONSE |
content ⇄ background | On-demand language switching |
| File | Responsibility |
|---|---|
background.js |
handleAction("summarize"), caching, orchestration |
content/content.js |
Content-script message router for overlay events |
content/services/contentExtractor.js |
Readability.js + fallback text extraction (4000-char cap) |
content/services/pageAnalyzer.js |
analyzePage() — routes to extraction / timing collection |
content/ui/overlay.js |
createStreamingOverlay(), appendStreamChunk(), appendInsights() |
content/ui/styles.js |
Overlay styling (hostStyle) |
ai/utility.js |
analyzePage(), getStructuredInsights(), translateResult(), translateInsights() |
ai/storageCleaner.js |
Daily eviction of expired wbm_* cache entries |
- Empty page: content under 100 chars → "Not enough content to analyze" overlay.
- AI unavailable: blocked by the pre-flight availability check with a friendly message.
- Screenshot caching: summarize results are cached without any screenshot (screenshots only apply to Quality).
- Cache eviction: all cached summaries/insights expire after 1 day via
StorageCleaner. - Translation failure:
translateResult()andtranslateInsights()degrade gracefully to the original English text. - Session isolation: the summary uses a
clone()of the main session, so repeated analyses never poison each other's context.