Objective
Who is this for, specifically
"Anyone who wants AI on webpages" isn't a product decision, it's the absence of one. This course's reference product, Page Intelligence, is scoped for a specific person: someone who reads a lot of dense pages (articles, docs, product listings) and wants 4 concrete things without leaving the page — summarize it, ask it a question, pull out structured facts, or fact-check a claim. You'll rename and reshape this for your own angle, but pick a specific enough user that you can say no to feature ideas that don't serve them.
Ship the product shell before the AI works -- this is the whole point of Type D courses
The temptation is to get one AI feature working really well first, then "add the UI later." Don't. Lesson 2 builds a complete, navigable, professional-feeling UI with all 4 buttons visible and NONE of them wired to AI yet — because a rough AI feature inside a polished shell feels like a real product in progress; a working AI feature with no shell around it feels like a demo, even when the underlying capability is identical.
The 4 contexts, and why they can't just call each other
A Manifest V3 extension is 4 separate JavaScript execution contexts running at the same time, each with different permissions and none of them sharing memory directly:
┌─────────────┐ message ┌──────────────────┐
│ Popup │ ───────────────▶ │ Service Worker │
│ (React UI) │ ◀─────────────── │ (background logic,│
└─────────────┘ │ calls the AI API) │
└────────┬───────────┘
┌─────────────┐ message │ message
│ Side Panel │ ◀─────────────────────────┘
│ (React UI) │
└──────┬──────┘
│ message
▼
┌─────────────────┐
│ Content Script │ (runs INSIDE the actual webpage,
│ (reads page text) │ reads its DOM)
└─────────────────┘
This diagram is the single most important thing in this lesson
"My popup can't see my content script's data" is the #1 confusion point in this entire course, and it's not a bug -- it's the architecture working as designed. None of these 4 contexts share variables or can call each other's functions directly. Every piece of data that crosses from one box to another in this diagram travels through Chrome's message-passing APIs (chrome.runtime.sendMessage, chrome.tabs.sendMessage), explicitly, every time. If you find yourself trying to import a variable from the content script into the popup, that's the moment to stop and reach for message passing instead.
npx wxt@latest init page-intelligence
cd page-intelligence
npm install
npm run dev
WXT (the framework this course uses instead of raw Webpack/Manifest hand-editing) scaffolds a project with an entrypoints/ directory -- entrypoints/popup/, entrypoints/background.ts, entrypoints/content.ts map directly onto 3 of the 4 boxes in the diagram above (the side panel is its own entrypoint too, added in Lesson 2). Load the .output/chrome-mv3-dev/ folder as an unpacked extension in chrome://extensions (with Developer mode on) to see it running for real.
Your content script successfully extracts the page's text into a JavaScript variable. Your popup's "Summarize" button needs that text to send to the AI. What's the correct way to get it there?
Content scripts run inside the actual webpage's JavaScript context, completely isolated from the popup's context (which is its own separate page) -- there is no shared window object, no shared module scope, nothing to import. Message passing is not a workaround for a limitation; it IS how these two isolated contexts are designed to communicate, and it's the same mechanism every arrow in this lesson's diagram represents.
What you're building
wxt_project_setup.md: your own specific product scoping (who's it for, what does "Page Intelligence" become when you rename it, which of the 4 features matter most to you) plus a running WXT project loaded as an unpacked extension in Chrome, showing a basic "Hello from [your extension name]" popup.