Plan: Replace Chrome Extension with Tampermonkey Userscript
Implement the following plan:
Plan: Replace Chrome Extension with Tampermonkey Userscript
Context
The Chrome extension approach for page capture requires users to load an unpacked extension (developer mode, Chrome nags on every launch) or publish to the Web Store. A Tampermonkey/Greasemonkey userscript is simpler: instructors install Tampermonkey once from the Web Store, then install a personalized .user.js script served from the API. The script uses GM_registerMenuCommand to add a "Capture This Page" item to the Tampermonkey menu — no floating button, no popup UI.
Each instructor gets a personalized script with their capture token baked in, eliminating any configuration step.
Changes
1. New API endpoint: personalized userscript
Add to apps/api/src/routes/admin/capture-tokens.ts:
GET /api/admin/capture-tokens/script.user.js
- Gated with
authMiddleware+requireRole("instructor", "staff") - Looks up the user's capture token (returns 404 if none)
- Returns a
Content-Type: text/javascriptresponse containing a complete Tampermonkey userscript with:@name UAI Page Capture@match *://*/*(runs on all pages)@grant GM_registerMenuCommand,GM_xmlhttpRequest,GM_notificationGM_xmlhttpRequestis needed instead offetchbecause it bypasses CORS restrictions (the userscript runs in the page context but GM_xmlhttpRequest makes requests from the extension context)GM_registerMenuCommand("Capture This Page", ...)— adds menu item to Tampermonkey dropdown- On click: grabs
document.documentElement.innerHTML,document.title,location.hrefand POSTs to the baked-in capture URL - Uses
GM_notificationfor success/error feedback (brief toast)
- The token is embedded directly in the script as a string literal
2. Update profile page UI
Modify apps/web/app/profile/client.tsx:
- Replace "Extension Setup" instructions section with "Userscript Setup":
- Install Tampermonkey from the Chrome Web Store (with link)
- Click "Install Capture Script" (link to
/api/admin/capture-tokens/script.user.js) - Tampermonkey will prompt to install — click "Install"
- On any page, click the Tampermonkey icon → "Capture This Page"
- The "Install Capture Script" link should be a prominent button/link that goes to the script URL
- Note: the link needs to go through the API with auth cookies, so it should use the API URL directly. Since the profile page already has the capture token, we can construct the URL client-side.
Modify apps/web/app/profile/page.tsx:
- Pass
apiUrl(fromprocess.env.API_URL) to the client component so it can construct the script install link
3. Update capture endpoint CORS
Modify apps/api/src/routes/captures.ts:
- The CORS
Access-Control-Allow-Origin: *and OPTIONS handler can be removed.GM_xmlhttpRequestbypasses CORS entirely (requests come from the extension context, not the page). This tightens security — the capture endpoint no longer needs to be open to all origins.
4. Delete Chrome extension
Delete tools/chrome-extension/ — all 7 files (manifest.json, popup.html, popup.js, options.html, options.js, icon16.png, icon48.png, icon128.png)
Files Summary
| Action | File |
|---|---|
| Modify | apps/api/src/routes/admin/capture-tokens.ts (add script.user.js endpoint) |
| Modify | apps/web/app/profile/client.tsx (replace extension instructions with userscript) |
| Modify | apps/web/app/profile/page.tsx (pass apiUrl prop) |
| Modify | apps/api/src/routes/captures.ts (remove CORS handling) |
| Delete | tools/chrome-extension/ (all files) |
Verification
- Generate token: Visit
/profileas instructor, generate capture token - Install script: Click "Install Capture Script" link → Tampermonkey install prompt appears
- Capture: Navigate to any page, click Tampermonkey icon → "Capture This Page" → notification shows success
- Browse captures: Visit
/capture, see the captured page - Type check:
bun run tscpasses - Check:
bun run checkpasses
If you need specific details from before exiting plan mode (like exact code snippets, error messages, or content you generated), read the full transcript at: ~/.claude/projects/-Users-challen-www-usingandunderstanding-ai/e6fc698f-db07-40fd-b506-9f2541ae28ac.jsonl
I'll start by reading all the files I need to modify in parallel.
Now let me implement all the changes. I'll start with the independent modifications in parallel.
Now let me implement the userscript endpoint. I need to add a new route for script.user.js:
Now let me do the remaining changes in parallel — update the profile page (both server and client), remove CORS from captures, and delete the Chrome extension.
Now update the client component — update the destructured props and replace the extension instructions with userscript instructions:
Now remove the CORS header from the POST handler and replace the extension setup instructions:
Now replace the extension instructions in the profile client with userscript instructions:
Now replace the "Capture URL" display and extension setup instructions with the userscript install link:
Now let me remove the unused handleCopyUrl and copied state since we no longer need them:
Let me verify the final state of the modified files and run the type check.
All files look correct and the type check passes cleanly.
All done. Here's a summary of the changes:
-
apps/api/src/routes/admin/capture-tokens.ts— AddedGET /api/admin/capture-tokens/script.user.jsendpoint that serves a personalized Tampermonkey userscript with the user's capture token and API URL baked in. UsesGM_registerMenuCommand,GM_xmlhttpRequest, andGM_notification. -
apps/web/app/profile/page.tsx— Now passesapiUrl(fromprocess.env.API_URL) to the client component. -
apps/web/app/profile/client.tsx— Replaced Chrome extension instructions with Tampermonkey userscript setup: "Install Capture Script" button linking to the.user.jsendpoint, plus 3-step setup instructions (install Tampermonkey, click install, use the menu). Removed unusedcopiedstate andhandleCopyUrl. -
apps/api/src/routes/captures.ts— Removed CORS preflight handler andAccess-Control-Allow-Originheader sinceGM_xmlhttpRequestbypasses CORS. -
tools/chrome-extension/— Deleted entirely.
Your task
First, run git status, git diff, and git log --oneline -10 to understand the current state.
Then add and commit all modified files with a descriptive message. Break the changes into multiple commits if appropriate. If there are untracked files, ask if they should be added or added to the appropriate .gitignore file. The user may also want to take over at this point, so provide an abort option as well. Always check changes for unnecessary debugging statements or logging that might have been accidentally included. Prompt the user if you find any, and offer to remove each one by one. Do not use interactive git staging (git add -p) as it does not work.
There are a lot of changes. Let me get targeted diffs to understand each group:
Let me check for any debug statements and look at more of the diffs:
No debug statements found. Let me check the remaining diffs:
Everything looks clean — all formatting changes (Prettier), no debug statements. Here's my plan for 4 logical commits:
- DB schema: page captures migration + preferred name field
- Page capture system: profile, captures, admin routes, web pages, nav, Tampermonkey userscript (replaces Chrome extension)
- Question board improvements: UI fixes + socket formatting + E2E test updates
- Markov babbler: formatting/lint fixes
Working tree is clean. 4 commits created:
45f814b— DB schema:page_capturestable,capture_tokenstable,preferred_namecolumn, migration 00160147b1d— Page capture system: profile/capture pages, API routes, Tampermonkey userscript (Chrome extension deleted)12f44bb— Question board UI polish + E2E tests for Feb 24 meeting8059d34— Markov babbler unused variable fix + formatting
OK! Now, how do we test these new capabilities?
Let me read the existing plan and explore the current state of the capture system to understand what we need to test.
Good exploration. Now let me design the test plan.
Excellent research. Let me now read the capture client to verify the data-testid placement, and check the nav patterns for how the "more" dropdown works.
Good — I can see the nav pattern. Let me also check how a similar instructor-only page test works (e.g. roster), and check whether students even get the "more" dropdown to confirm test logic.
Now I have everything needed. Let me also note the stale Chrome extension text that needs updating:
Good catch — there's a stale "Chrome extension" reference in the capture client empty state. Now let me write the final plan.