- Removed "Test connection" item from the browse settings dropdown
(chrome+firefox HTML + JS handler). Available in Advanced Options.
- Model column "*" bounce marker:
- Color now matches the badge per family (sonnet/opus/haiku/default)
instead of always rendering var(--badge-default) at 0.7 opacity.
- Tooltip on the bounced cell now fires when hovering EITHER the
badge or the asterisk: wrapped both in a .model-cell span that
carries the title + cursor: help.
- Popup header (#header-title) is now populated from manifest.name in
popup.js — testing branch's "Claude Exporter Beta" surfaces in the
popup automatically without a separate HTML edit per branch.
- src/CLAUDE.md extended the existing manifest-name rule to note that
popup.js reads manifest.name into #header-title. (Workspace-root
CLAUDE.md was mirrored locally but isn't tracked in git.)
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
7.4 KiB
Claude Exporter - Development Guide
Communication Style
- Narrate what you're doing at each step — brief status updates help the user follow along and make the chat searchable
- Be patient with tangents and context-switching (ADHD-friendly pacing)
- Keep explanations concise but don't skip them
- Scope guard: Gently remind the user when a tangent is pulling away from the current task. User tends to spiral into feature ideas mid-implementation — help stay focused on finishing the current thing before starting the next. A quick "want to add that to TODO and finish X first?" goes a long way.
Self-Maintenance
This file is the shared project memory. Update it proactively when:
- A new critical rule or recurring bug pattern is discovered
- Project structure changes (new files, renamed files, new architecture patterns)
- A decision is made about how something should always work (e.g., "exports > 1 file must ZIP")
Keep it concise. Don't duplicate what's already here — update existing sections instead.
This file exists in two places: the workspace root (read by Claude Code) and src/CLAUDE.md (tracked in git). When updating, update both copies.
Project Structure
- Extension source and git repo lives in
src/ - Parallel Chrome (
src/chrome/) and Firefox (src/firefox/) versions — nearly identical copies - Chrome uses Manifest V3, Firefox uses Manifest V2
- Releases go in
releases/vX.Y.Z/
Key Files (under src/chrome/ and src/firefox/)
content.js— Content script injected on claude.ai pages (handles API calls, popup export actions)utils.js— Shared utilities (convertToMarkdown, convertToText, downloadFile, extractArtifactFiles, etc.)browse.js— Browse page logic (filtering, sorting, has its own export functions, always ZIPs)background.js— Re-injects content scripts on install/updatejszip.min.js— ZIP librarypopup.html/popup.js— Extension popup UI and logicbrowse.html— Browse/search conversations page
Git & Commits
Auto-commit after every completed change. Don't wait for the user to ask. After finishing a task (bug fix, feature, refactor), commit immediately with a clear message.
- Git repo is in
src/— alwayscdthere for git commands - Write concise, descriptive commit messages:
Fix bulk export to always ZIP instead of individual downloads - Not:
wip,fix stuff,update,final FINAL (1) - Group related changes into one commit (e.g., Chrome + Firefox changes for the same fix = one commit)
- Don't push unless asked
- Branching: Do all development on
testingbranch. Merge tomainonly when creating a release
Documentation Upkeep
After each commit, update these files:
src/docs/TODO.md— Move completed items to the Completed section, update the current version number, clean up any stale entriessrc/docs/CHANGELOG.md— Append a short entry under the current version. Create the file if it doesn't exist. Format:## [X.Y.Z]header, then bullet points describing changes. Keep entries concise — one line per change is fine- The CHANGELOG doubles as store update notes. All changes between the current version and the last
_Published_marker are what goes into the store listing update
Release Process
Only create releases when explicitly asked. Never auto-release.
When the user asks to create a release for version X.Y.Z:
- Verify version — Confirm both
chrome/manifest.jsonandfirefox/manifest.jsonshow the correct version - Create release directory —
mkdir -p releases/vX.Y.Z - ZIP Chrome extension —
cd src/chrome && zip -r ../../releases/vX.Y.Z/claude-exporter-chrome.zip ./* - ZIP Firefox extension —
cd src/firefox && zip -r ../../releases/vX.Y.Z/claude-exporter-firefox.zip ./*(unsigned; user handles .xpi signing via AMO) - Git tag —
cd src && git tag vX.Y.Z -m "Release vX.Y.Z" - Push tag —
git push origin vX.Y.Z - Create GitHub release —
gh release create vX.Y.Z ../releases/vX.Y.Z/* --title "vX.Y.Z" --notes "$(changelog excerpt from docs/CHANGELOG.md)"— use all changes since last_Published_marker as the notes - Mark as published — Add
_Published_line after the released version's entries in docs/CHANGELOG.md, commit
Critical Rules
Always apply changes to BOTH browsers
Every code change to chrome/ must also be applied to firefox/. The files are nearly identical — differences are only in manifest format and API calls (chrome.scripting.executeScript vs chrome.tabs.executeScript).
Always bump version on every change
Update "version" in BOTH chrome/manifest.json AND firefox/manifest.json.
background.js must inject ALL content scripts
When re-injecting into already-open tabs (on install/update), background.js must inject all three files: jszip.min.js, utils.js, AND content.js. Injecting only content.js causes "JSZip is not defined" / "downloadFile is not defined" / "extractArtifactFiles is not defined" errors on already-open tabs.
Multi-file exports must always be ZIPped
Any export producing more than one file should always create a ZIP — never trigger individual browser downloads.
Manifest name differs by branch
"name" in BOTH manifests must be:
"Claude Exporter"on themainbranch (released version)"Claude Exporter Beta"on thetestingbranch (so the user can tell at a glance which build is loaded)
The popup header title is populated from manifest.name in popup.js (#header-title), so the popup automatically reads "Claude Exporter Beta" on the testing branch — no separate HTML edit needed.
When merging testing → main for a release, flip both manifest names to drop "Beta" as part of the merge.
Testing
- Vitest test harness (
package.json+node_modules/) lives insrc/tests/. Run tests withnpm test(one-shot) ornpm run test:watch(watch mode) fromsrc/tests/. - Test files live in
src/tests/and import fromsrc/chrome/utils.js(the canonical copy). firefox/utils.jsis a mirror — if it drifts fromchrome/utils.js, the tests won't catch it. Keep them in sync per the existing rule.utils.jshas a conditionalmodule.exportsblock at the bottom that fires only whenmoduleis defined (Node/vitest). Browser extensions ignore it because the global is undefined.node_modules/andpackage-lock.jsonare gitignored (package.jsonis tracked undersrc/tests/). None are part of the release ZIPs.
Architecture Notes
Content Script Injection
- On fresh page loads: manifest
content_scriptshandles injection of all three JS files - On extension install/update:
background.jsre-injects into already-open claude.ai tabs content.jshas a double-injection guard (window.claudeExporterContentScriptLoaded) to prevent duplicate message listeners
Export Flow
- Popup "Export Current" → sends message to content script on the active claude.ai tab
- Popup "Export All" → sends message to content script, which fetches all conversations and ZIPs them
- Browse page → loads conversation list via content script relay (
sendMessageToClaudeTab), then exports directly viafetch()to claude.ai API
Chrome vs Firefox API Differences
- Chrome MV3:
chrome.scripting.executeScript({ target, files })— accepts file array - Firefox MV2:
chrome.tabs.executeScript(tabId, { file })— one file at a time, must loop