"ChromeEmbed's popup.js is six lines. Lesson 4 is why that's the right number — and what happens when you reach for 'just one more popup feature.'"
The Six Lines
document.getElementById('open-panel')?.addEventListener('click', async () => {
const windowInfo = await chrome.windows.getCurrent().catch(() => null);
if (windowInfo?.id !== undefined) {
await chrome.sidePanel.open({ windowId: windowInfo.id }).catch(() => {});
}
window.close();
});
One handler. Click 'Open Panel' button → fetch current window → open side panel in that window → close the popup. The popup HTML is comparably tiny — a styled button labeled 'Open Panel.'
Why This Is Right
- Single responsibility — the popup is a launcher. Anything more would compete with the panel as the actual experience surface.
- Predictable lifecycle — popup opens, popup closes. No state to manage, no rerender to handle, no race conditions to debug.
- Fast paint — the popup HTML loads, JS runs, click handler attaches. Probably under 50 ms from open to ready.
- No competing UI — Pippa lives in the panel iframe; the popup pointing the user toward it (rather than hosting parallel chat UI) keeps the mental model singular.
The Alternative That Doesn't Work
What if you wanted the popup to be useful? You'd add quick-chat input, a most-recent-message preview, a brain selector. Each of those:
- Duplicates UI that already exists in the panel.
- Needs to message the cwkPippa backend, which the panel iframe already does — so you'd either re-implement that messaging in the popup or route through the SW → iframe (which doesn't have a clean API today).
- Has lifecycle problems: the popup dies the moment the user does anything outside it, so any operation in flight when they click elsewhere is lost.
The 'useful popup' design always ends up paying duplication costs that the doorway-popup avoids entirely. ChromeEmbed's choice: don't pay the cost.
The Window vs Tab Choice
The popup passes { windowId } to chrome.sidePanel.open. Two consequences:
- The panel opens for the entire window. Tab switching inside the window keeps the panel open with content updating per the active tab (via the background bus + Lesson 5's bridge).
- The user can still close the panel via Chrome's standard side-panel chrome.
Passing { tabId } instead would scope the panel to that one tab; the moment the user switches tabs, the panel closes. That's the wrong UX for a household extension that wants to be ambient across the entire window's browsing session.
The Optional Chaining
document.getElementById('open-panel')?.addEventListener(...) — the optional chaining is paranoia. If the popup HTML is changed and the button id mis-spelled, the popup degrades gracefully (no JS error, just a non-functional button). For a 6-line script that's overkill; for an extension you might update from a hundred git revisions later, it's a courtesy. Keep it.