Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
9 changes: 6 additions & 3 deletions CLAUDE.md
Original file line number Diff line number Diff line change
Expand Up @@ -9,18 +9,20 @@ npm install # Install dependencies
npm run build # Build for Firefox (default)
npm run build:firefox # Build for Firefox
npm run build:chrome # Build for Chrome
npm run build:safari # Build for Safari
npm run dev # Run in Firefox Developer Edition
npm run watch:chrome # Watch mode for Chrome development
npm run test # Run unit tests (vitest)
npm run lint # ESLint check
npm run build:types # TypeScript type check
npm run package # Package for Firefox (web-ext-artifacts/)
npm run package:chrome # Package for Chrome (ZIP)
npm run package:safari # Build for Safari + run safari-web-extension-converter (outputs to web-ext-artifacts/safari/)
```

## Architecture

**Cross-browser extension (Manifest V3)** for Firefox and Chrome that uses Fetch Proxy to trim ChatGPT conversations before React renders.
**Cross-browser extension (Manifest V3)** for Firefox, Chrome, and Safari that uses Fetch Proxy to trim ChatGPT conversations before React renders.

### Core Components

Expand Down Expand Up @@ -60,17 +62,18 @@ LightSession Pro counts **messages** (role changes) instead of nodes:

```
extension/
├── manifest.json # Symlink → manifest.firefox.json (or chrome copy)
├── manifest.json # Symlink → manifest.firefox.json (or chrome/safari copy)
├── manifest.firefox.json # Firefox-specific manifest
├── manifest.chrome.json # Chrome-specific manifest
├── manifest.safari.json # Safari-specific manifest (MV3, no declarativeContent)
└── src/
├── page/ # Page script (Fetch Proxy, runs in page context)
├── content/ # Content scripts (settings, status bar)
├── background/ # Background service worker
├── popup/ # Popup HTML/CSS/TS
└── shared/ # Types, constants, storage, logger
tests/ # Unit tests (vitest + happy-dom)
build.cjs # esbuild build script (supports --target=firefox|chrome)
build.cjs # esbuild build script (supports --target=firefox|chrome|safari)
```

## Conventions
Expand Down
9 changes: 5 additions & 4 deletions build.cjs
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,7 @@
* node build.cjs - Development build for Firefox (default)
* node build.cjs --target=firefox - Build for Firefox
* node build.cjs --target=chrome - Build for Chrome
* node build.cjs --target=safari - Build for Safari
* node build.cjs --watch - Watch mode for development
* NODE_ENV=production node build.cjs - Production build (minified, no sourcemaps)
*/
Expand All @@ -18,10 +19,10 @@ const path = require('path');
const isWatch = process.argv.includes('--watch');
const isProduction = process.env.NODE_ENV === 'production';

// Parse --target=firefox|chrome (default: firefox)
// Parse --target=firefox|chrome|safari (default: firefox)
const targetArg = process.argv.find((arg) => arg.startsWith('--target='));
const target = targetArg ? targetArg.split('=')[1] : 'firefox';
const validTargets = ['firefox', 'chrome'];
const validTargets = ['firefox', 'chrome', 'safari'];
if (!validTargets.includes(target)) {
console.error(`❌ Invalid target: ${target}. Use: ${validTargets.join(', ')}`);
process.exit(1);
Expand All @@ -39,8 +40,8 @@ function copyManifest() {
fs.unlinkSync(manifestDest);
}

if (target === 'chrome') {
// For Chrome, copy manifest.chrome.json
if (target === 'chrome' || target === 'safari') {
// For Chrome/Safari, copy the target-specific manifest
fs.copyFileSync(manifestSrc, manifestDest);
console.log(`✓ Copied manifest.${target}.json → manifest.json`);
} else {
Expand Down
53 changes: 53 additions & 0 deletions extension/manifest.safari.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,53 @@
{
"manifest_version": 3,
"name": "LightSession Pro for ChatGPT",
"version": "1.7.0",
"description": "Keep ChatGPT fast by keeping only the last N messages in the DOM. Local-only.",
"icons": {
"16": "icons/icon-16.png",
"32": "icons/icon-32.png",
"48": "icons/icon-48.png",
"128": "icons/icon-128.png"
},
"action": {
"default_title": "LightSession Pro",
"default_popup": "popup/popup.html"
},
"permissions": ["storage", "tabs"],
"host_permissions": [
"*://chat.openai.com/*",
"*://chatgpt.com/*"
],
"content_scripts": [
{
"matches": [
"*://chat.openai.com/*",
"*://chatgpt.com/*"
],
"js": ["dist/page-inject.js"],
"run_at": "document_start"
},
{
"matches": [
"*://chat.openai.com/*",
"*://chatgpt.com/*"
],
"js": ["dist/content.js"],
"run_at": "document_idle"
}
],
"background": {
"service_worker": "dist/background.js"
},
"web_accessible_resources": [
{
"resources": ["dist/page-script.js", ".dev"],
"matches": ["*://chat.openai.com/*", "*://chatgpt.com/*"]
}
],
"browser_specific_settings": {
"safari": {
"strict_min_version": "15.4"
}
}
}
1 change: 1 addition & 0 deletions extension/src/shared/browser-polyfill.ts
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,7 @@
* Cross-browser compatibility layer for WebExtension APIs.
* - Firefox: uses global `browser` object (Promise-based)
* - Chrome: uses global `chrome` object (callback-based, but MV3 supports Promises)
* - Safari: Web Extensions API (browser/chrome); MV3 from Safari 15.4+
*
* Modern Chrome (MV3) supports Promise-based APIs similar to Firefox,
* so we can use `chrome` directly as a drop-in replacement for `browser`.
Expand Down
8 changes: 0 additions & 8 deletions package-lock.json

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

3 changes: 3 additions & 0 deletions package.json
Original file line number Diff line number Diff line change
Expand Up @@ -10,10 +10,12 @@
"build": "node build.cjs",
"build:firefox": "node build.cjs --target=firefox",
"build:chrome": "node build.cjs --target=chrome",
"build:safari": "node build.cjs --target=safari",
"build:types": "tsc --noEmit",
"build:prod": "rm -f extension/.dev && NODE_ENV=production node build.cjs",
"build:prod:firefox": "rm -f extension/.dev && NODE_ENV=production node build.cjs --target=firefox",
"build:prod:chrome": "rm -f extension/.dev && NODE_ENV=production node build.cjs --target=chrome",
"build:prod:safari": "rm -f extension/.dev && NODE_ENV=production node build.cjs --target=safari",
"watch": "node build.cjs --watch",
"watch:chrome": "node build.cjs --target=chrome --watch",
"lint": "eslint extension/src",
Expand All @@ -27,6 +29,7 @@
"dev:stable": "npm run build:firefox && web-ext run --source-dir=extension --firefox=firefox --start-url='https://chat.openai.com'",
"package": "npm run build:prod:firefox && web-ext build --source-dir=extension --artifacts-dir=web-ext-artifacts",
"package:chrome": "npm run build:prod:chrome && cd extension && zip -r ../web-ext-artifacts/lightsession-chrome.zip . -x '*.ts' -x 'src/*' -x 'manifest.*.json'",
"package:safari": "npm run build:prod:safari && xcrun safari-web-extension-converter extension --project-location web-ext-artifacts/safari --app-name 'LightSession Pro' --bundle-identifier com.lightsession.pro --copy-resources --no-open",
"clean": "rm -rf extension/dist extension/popup/popup.js web-ext-artifacts"
},
"keywords": [
Expand Down