Skip to content

feat(extension): MV3 shell, content script, and side panel — completes Phase 0#5

Merged
erichuang1425 merged 2 commits into
mainfrom
claude/elegant-brahmagupta-kp2jmz
Jun 15, 2026
Merged

feat(extension): MV3 shell, content script, and side panel — completes Phase 0#5
erichuang1425 merged 2 commits into
mainfrom
claude/elegant-brahmagupta-kp2jmz

Conversation

@erichuang1425

Copy link
Copy Markdown
Owner

Summary

Completes Phase 0 — Foundation by landing apps/extension, the browser
extension that ties the foundation packages together end-to-end: the thread on
the current page → @forumforge/parser → the
@forumforge/core post model → a clean, read-only side
panel
view. With the three earlier packages (#3, #4) merged, this lands the
remaining Phase 0 roadmap items — extension shell, content script, side panel
UI
— as real, unit-tested, buildable code (no placeholder stubs, per AGENTS.md).

toolbar action ─▶ background (service worker) ─▶ opens the side panel
side panel "Read this thread" ─▶ inject content.js (activeTab) ─▶ extract ─▶ render

What's included

  • src/background.ts — thin MV3 service worker (the shell); the toolbar
    action opens the side panel.
  • src/content.ts — extracts the thread from its page and replies. Injected
    on demand into the active tab, with idempotent re-injection.
  • src/sidepanel.ts + public/sidepanel.html — the panel UI: a button
    that injects the content script, requests extraction, and renders the result.
  • src/extract.ts — the seam where a site-specific adapter will be chosen
    later (Phase 2); today it always uses the generic parser.
  • src/render.ts — builds the view with textContent only, never
    innerHTML.
  • src/messaging.ts — typed request/response protocol, validated with type
    guards because messages cross the untrusted page boundary.
  • Buildbuild.mjs (esbuild) bundles entries to dist/ and copies the
    manifest + panel HTML. Minimal webext.d.ts instead of a heavy @types/chrome.

Privacy & security

  • Narrow permissions: activeTab + scripting + sidePanel only — no host
    permissions
    , no declared content script, no standing access to pages the user
    hasn't invoked ForumForge on. The content script runs only on the tab the user
    clicks on (activeTab granted on that gesture). See docs/PRIVACY.md.
  • Untrusted content stays inert: the side panel renders extracted post content
    as text via textContent and never injects markup, so a malicious contentHtml
    or author string can't run (see SECURITY.md). A unit test asserts
    this. Rich, sanitized contentHtml rendering is Phase 1 clean reading mode.

Design notes

  • Why a real build now (not source-only): packages/* are source-only with no
    build step, but a browser can't resolve workspace TS imports, so the extension
    has a genuine esbuild bundle. All entries are classic IIFE scripts because a
    content script injected via scripting.executeScript({ files }) runs as a
    classic script.
  • esbuild is the one new dev dependency (standard, minimal, justified by the
    bundling need).

Roadmap (Phase 0 — now complete)

  • Core post model — packages/core
  • Basic generic extractor — packages/parser
  • Local storage layer — packages/storage
  • Extension shell — apps/extension (background service worker + MV3 manifest)
  • Content script — apps/extension (content.ts, on-demand injection)
  • Side panel UI — apps/extension (sidepanel.ts + renderThread)

Docs synced in the same change: README.md, ROADMAP.md, the canonical
Initial Plan.md checklist, a package README.md, and a .memory/ lesson. CI
now also builds the extension (pnpm build).

Verification

  • pnpm -r typecheck — passes (core + parser + storage + extension)
  • pnpm test55 tests pass (8 files; 8 new: messaging guards, extraction
    wiring, rendering incl. the XSS-safety guarantee)
  • pnpm build — bundles apps/extension to dist/
  • pnpm install --frozen-lockfile — passes (lockfile updated for esbuild)

Intentionally not done

  • Loading the unpacked extension in a real browser is a manual step (needs a
    browser); the unit tests and the bundle build are automated, and the manual
    load/click-through steps are documented in the app README. No browser-in-CI
    added — that's a heavier follow-up.
  • Rich contentHtml rendering with sanitization is deliberately Phase 1
    (clean reading mode), not Phase 0.

https://claude.ai/code/session_01PDzDar5vUbJ5Ez1Kc1JYiy


Generated by Claude Code

Completes Phase 0 with apps/extension — the browser extension that turns the
thread on the current page into a clean, readable side-panel view, wiring the
live DOM through @forumforge/parser and the @forumforge/core post model.

- background.ts: thin MV3 service worker; toolbar action opens the side panel.
- content.ts: extracts the thread; injected on demand via activeTab + scripting
  (no host permissions, no standing page access), idempotent re-injection.
- sidepanel.ts + sidepanel.html: 'Read this thread' injects the content script,
  requests extraction, and renders the result.
- render.ts: read-only view written with textContent only — untrusted page
  content (incl. contentHtml) can never inject markup. Rich sanitized rendering
  is Phase 1.
- extract.ts: the seam where a site-specific adapter is chosen later (Phase 2),
  generic parser as fallback.
- messaging.ts: typed protocol validated with guards across the page boundary.
- esbuild build (build.mjs) bundles to dist/; minimal webext.d.ts instead of a
  heavy @types/chrome dep.

Tooling: esbuild dev dependency; root 'build' script and CI build step; vitest
includes apps/**/test. Docs synced (README, ROADMAP, Initial Plan checklist,
app README, .memory lesson).

Verification: pnpm -r typecheck passes; pnpm test — 55 tests pass (8 new);
pnpm build bundles the extension; pnpm install --frozen-lockfile passes.

@chatgpt-codex-connector chatgpt-codex-connector Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

💡 Codex Review

Here are some automated review suggestions for this pull request.

Reviewed commit: 128268ebe5

ℹ️ About Codex in GitHub

Your team has set up Codex to review pull requests in this repo. Reviews are triggered when you

  • Open a pull request for review
  • Mark a draft as ready
  • Comment "@codex review".

If Codex has suggestions, it will comment; otherwise it will react with 👍.

Codex can also answer questions or update the PR. Try commenting "@codex address that feedback".

Comment thread apps/extension/src/sidepanel.ts
…icked

setPanelBehavior({ openPanelOnActionClick: true }) opens the side panel but
does not confer the activeTab grant the panel needs to read the page
(crbug.com/40916430). With no host permissions, the panel's executeScript then
rejected on ordinary forum pages, sending "Read this thread" into the error
handler. Handle the toolbar click via chrome.action.onClicked and open the
panel programmatically so the click's activeTab grant covers the tab and
on-demand content-script injection succeeds.

Update the minimal webext typings: add chrome.action.onClicked and
sidePanel.open, drop the now-unused setPanelBehavior.

https://claude.ai/code/session_01WRRAzKehcHPUhGNibdiGp7
@erichuang1425 erichuang1425 merged commit 54f50a4 into main Jun 15, 2026
1 check passed

@chatgpt-codex-connector chatgpt-codex-connector Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

💡 Codex Review

Here are some automated review suggestions for this pull request.

Reviewed commit: 522fec7c88

ℹ️ About Codex in GitHub

Your team has set up Codex to review pull requests in this repo. Reviews are triggered when you

  • Open a pull request for review
  • Mark a draft as ready
  • Comment "@codex review".

If Codex has suggestions, it will comment; otherwise it will react with 👍.

Codex can also answer questions or update the PR. Try commenting "@codex address that feedback".

Comment thread apps/extension/build.mjs
// as a classic script, so keep every entry consistent.
format: "iife",
platform: "browser",
target: ["chrome114"],

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

P2 Badge Raise the Chrome compatibility floor to 116

On Chrome 114 or 115, which this build explicitly targets and the manifest does not exclude, clicking the toolbar action calls chrome.sidePanel.open() and fails before the panel can open because Chrome's API documentation states that open() was introduced in Chrome 116. Target Chrome 116 and add the corresponding minimum_chrome_version, or use an API available in Chrome 114.

Useful? React with 👍 / 👎.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants