Skip to content
Merged
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
17 changes: 17 additions & 0 deletions .github/workflows/release.yml
Original file line number Diff line number Diff line change
Expand Up @@ -103,6 +103,7 @@ jobs:
eslint.config.js \
build.cjs \
README.md \
AMO-SOURCE-README.md \
LICENSE \
.gitignore \
.prettierrc \
Expand Down Expand Up @@ -158,6 +159,22 @@ jobs:
with:
addon-id: ${{ secrets.FIREFOX_ADDON_ID }}
addon-path: light-session-${{ steps.version.outputs.VERSION }}-firefox.zip
source-path: light-session-${{ steps.version.outputs.VERSION }}-source.zip
approval-note: |
Source archive for this exact version is attached.

Build environment:
- Node.js 24.x (see .node-version)
- npm ci (uses package-lock.json)

Build steps:
1. npm ci
2. npm run build:prod:firefox

The submitted zip was created with:
cd extension && zip -r ../light-session-VERSION-firefox.zip manifest.json dist/ popup/ icons/ -x "*.map"

See AMO-SOURCE-README.md in the source archive for details.
auth-api-issuer: ${{ secrets.FIREFOX_API_ISSUER }}
auth-api-secret: ${{ secrets.FIREFOX_API_SECRET }}
release-note: "See release notes at https://github.com/11me/light-session/releases/tag/v${{ steps.version.outputs.VERSION }}"
Expand Down
2 changes: 2 additions & 0 deletions .gitignore
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,7 @@ node_modules/

# AI and speckit related
.claude/
.agents/
.specify/
specs/

Expand Down Expand Up @@ -50,3 +51,4 @@ extension/.dev
*.temp
.serena/
extension/manifest.json
.signum/
39 changes: 39 additions & 0 deletions AMO-SOURCE-README.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,39 @@
# Source Code — LightSession Pro for ChatGPT

This archive contains the complete source code for the submitted extension version.

## Build Environment

- **Node.js** 24.10.0 (see `.node-version`)
- **npm** (lockfile: `package-lock.json`)

## Reproduce the build

```bash
npm ci
npm run build:prod:firefox
```

This runs `NODE_ENV=production node build.cjs --target=firefox` which uses esbuild to bundle TypeScript source files into single JS files with minification and no sourcemaps.

## Create the Firefox zip

```bash
cd extension
zip -r ../light-session-firefox.zip manifest.json dist/ popup/ icons/ -x "*.map"
```

## Source layout

```
extension/src/ TypeScript source (page scripts, content scripts, popup, shared)
extension/icons/ Extension icons
extension/manifest.firefox.json Firefox manifest
extension/manifest.chrome.json Chrome manifest
build.cjs esbuild build script
tests/ Unit tests (vitest)
```

## No vendored or private dependencies

All dependencies are public npm packages resolved via `package-lock.json`.
14 changes: 7 additions & 7 deletions extension/src/content/content.ts
Original file line number Diff line number Diff line change
Expand Up @@ -261,20 +261,20 @@ function setupNavigationDetection(): void {
});
};

// Listen for popstate events
window.addEventListener('popstate', () => {
if (location.href !== lastUrl) {
scheduleNavSideEffects('popstate');
}
});

// Patch history methods for SPA navigation detection
// Guard against double patching (e.g. extension reload / unexpected reinjection).
const PATCH_FLAG = '__lightsession_patched_history__';
const patchScope = window as unknown as Record<string, unknown>;
if (patchScope[PATCH_FLAG] === true) return;
patchScope[PATCH_FLAG] = true;

// Listen for popstate events (inside the guard so it's only registered once)
window.addEventListener('popstate', () => {
if (location.href !== lastUrl) {
scheduleNavSideEffects('popstate');
}
});

const originalPushState = history.pushState.bind(history);
const originalReplaceState = history.replaceState.bind(history);

Expand Down
4 changes: 3 additions & 1 deletion extension/src/content/user-collapse.ts
Original file line number Diff line number Diff line change
Expand Up @@ -455,7 +455,9 @@ export function installUserCollapse(): UserCollapseController {
updateButtonUi(btn, !expanded);

requestAnimationFrame(() => {
preserveScrollAfterHeightChange(scroller!, prevScrollTop, prevScrollHeight, wasPinned);
if (scroller) {
preserveScrollAfterHeightChange(scroller, prevScrollTop, prevScrollHeight, wasPinned);
}
});
};
document.addEventListener('click', onDocClick, true);
Expand Down
20 changes: 16 additions & 4 deletions extension/src/page/page-script.ts
Original file line number Diff line number Diff line change
Expand Up @@ -290,10 +290,6 @@ async function interceptedFetch(
}
}

if (!configReceived) {
return nativeFetch(...args);
}

// Skip if disabled
if (!cfg.enabled) {
return nativeFetch(...args);
Expand Down Expand Up @@ -332,6 +328,22 @@ async function interceptedFetch(
const keptAfter = trimmed.visibleKept;
const removed = Math.max(0, totalBefore - keptAfter);

// Guard: no visible nodes were trimmed - return original response untouched.
// Rewriting the tree when nothing is trimmed would destroy hidden/system/tool/thinking
// nodes and alter the tree shape unnecessarily (issue #26).
if (trimmed.visibleKept === trimmed.visibleTotal) {
log(
`No visible trim needed: ${keptAfter}/${totalBefore} nodes (limit: ${cfg.limit})`
);
dispatchStatus({
totalBefore,
keptAfter,
removed: 0,
limit: cfg.limit,
});
return res;
}

log(
`Trimmed: ${keptAfter}/${totalBefore} nodes (limit: ${cfg.limit}), visible: ${trimmed.visibleKept}/${trimmed.visibleTotal}`
);
Expand Down
8 changes: 4 additions & 4 deletions extension/src/shared/logger.ts
Original file line number Diff line number Diff line change
Expand Up @@ -56,27 +56,27 @@ export function isDebugMode(): boolean {
*/
export function logDebug(message: string, ...args: unknown[]): void {
if (debugEnabled) {
safeConsole.log(`${LOG_PREFIX} [DEBUG]`, message, ...args);
safeConsole.log(`[${LOG_PREFIX}DEBUG]`, message, ...args);
}
}

/**
* Log warning (always shown)
*/
export function logWarn(message: string, ...args: unknown[]): void {
safeConsole.warn(`${LOG_PREFIX} [WARN]`, message, ...args);
safeConsole.warn(`[${LOG_PREFIX}WARN]`, message, ...args);
}

/**
* Log error (always shown)
*/
export function logError(message: string, ...args: unknown[]): void {
safeConsole.error(`${LOG_PREFIX} [ERROR]`, message, ...args);
safeConsole.error(`[${LOG_PREFIX}ERROR]`, message, ...args);
}

/**
* Log info (always shown)
*/
export function logInfo(message: string, ...args: unknown[]): void {
safeConsole.log(`${LOG_PREFIX} [INFO]`, message, ...args);
safeConsole.log(`[${LOG_PREFIX}INFO]`, message, ...args);
}
2 changes: 1 addition & 1 deletion extension/src/shared/trimmer.ts
Original file line number Diff line number Diff line change
Expand Up @@ -184,7 +184,7 @@ export function trimMapping(
// Preserve original root node - ChatGPT needs this "(no role)" node as tree anchor
const originalRootId = path[0];
const originalRootNode = originalRootId ? mapping[originalRootId] : null;
const hasOriginalRoot = originalRootId && originalRootNode;
const hasOriginalRoot = originalRootId && originalRootNode && !isVisibleMessage(originalRootNode);

// Build new mapping with kept nodes + original root
const newMapping: ChatMapping = {};
Expand Down
40 changes: 20 additions & 20 deletions tests/unit/logger.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -88,7 +88,7 @@ describe('logger', () => {
logDebug('test message');

expect(consoleLogSpy).toHaveBeenCalledWith(
`${LOG_PREFIX} [DEBUG]`,
`[${LOG_PREFIX}DEBUG]`,
'test message'
);
});
Expand All @@ -99,7 +99,7 @@ describe('logger', () => {
logDebug('test message', 'arg1', 42, { key: 'value' });

expect(consoleLogSpy).toHaveBeenCalledWith(
`${LOG_PREFIX} [DEBUG]`,
`[${LOG_PREFIX}DEBUG]`,
'test message',
'arg1',
42,
Expand All @@ -113,7 +113,7 @@ describe('logger', () => {
logDebug('test message');

expect(consoleLogSpy).toHaveBeenCalledWith(
`${LOG_PREFIX} [DEBUG]`,
`[${LOG_PREFIX}DEBUG]`,
'test message'
);
});
Expand All @@ -126,7 +126,7 @@ describe('logger', () => {
setDebugMode(true);
logDebug('should appear');
expect(consoleLogSpy).toHaveBeenCalledWith(
`${LOG_PREFIX} [DEBUG]`,
`[${LOG_PREFIX}DEBUG]`,
'should appear'
);
});
Expand Down Expand Up @@ -154,7 +154,7 @@ describe('logger', () => {
logInfo('info message');

expect(consoleLogSpy).toHaveBeenCalledWith(
`${LOG_PREFIX} [INFO]`,
`[${LOG_PREFIX}INFO]`,
'info message'
);
});
Expand All @@ -165,7 +165,7 @@ describe('logger', () => {
logInfo('info message');

expect(consoleLogSpy).toHaveBeenCalledWith(
`${LOG_PREFIX} [INFO]`,
`[${LOG_PREFIX}INFO]`,
'info message'
);
});
Expand All @@ -174,7 +174,7 @@ describe('logger', () => {
logInfo('info message', 'arg1', 42, { key: 'value' });

expect(consoleLogSpy).toHaveBeenCalledWith(
`${LOG_PREFIX} [INFO]`,
`[${LOG_PREFIX}INFO]`,
'info message',
'arg1',
42,
Expand Down Expand Up @@ -202,7 +202,7 @@ describe('logger', () => {
logWarn('warning message');

expect(consoleWarnSpy).toHaveBeenCalledWith(
`${LOG_PREFIX} [WARN]`,
`[${LOG_PREFIX}WARN]`,
'warning message'
);
});
Expand All @@ -213,7 +213,7 @@ describe('logger', () => {
logWarn('warning message');

expect(consoleWarnSpy).toHaveBeenCalledWith(
`${LOG_PREFIX} [WARN]`,
`[${LOG_PREFIX}WARN]`,
'warning message'
);
});
Expand All @@ -222,7 +222,7 @@ describe('logger', () => {
logWarn('warning message', 'arg1', 42, { key: 'value' });

expect(consoleWarnSpy).toHaveBeenCalledWith(
`${LOG_PREFIX} [WARN]`,
`[${LOG_PREFIX}WARN]`,
'warning message',
'arg1',
42,
Expand Down Expand Up @@ -250,7 +250,7 @@ describe('logger', () => {
logError('error message');

expect(consoleErrorSpy).toHaveBeenCalledWith(
`${LOG_PREFIX} [ERROR]`,
`[${LOG_PREFIX}ERROR]`,
'error message'
);
});
Expand All @@ -261,7 +261,7 @@ describe('logger', () => {
logError('error message');

expect(consoleErrorSpy).toHaveBeenCalledWith(
`${LOG_PREFIX} [ERROR]`,
`[${LOG_PREFIX}ERROR]`,
'error message'
);
});
Expand All @@ -270,7 +270,7 @@ describe('logger', () => {
logError('error message', 'arg1', 42, { key: 'value' });

expect(consoleErrorSpy).toHaveBeenCalledWith(
`${LOG_PREFIX} [ERROR]`,
`[${LOG_PREFIX}ERROR]`,
'error message',
'arg1',
42,
Expand All @@ -284,7 +284,7 @@ describe('logger', () => {
logError('operation failed', error);

expect(consoleErrorSpy).toHaveBeenCalledWith(
`${LOG_PREFIX} [ERROR]`,
`[${LOG_PREFIX}ERROR]`,
'operation failed',
error
);
Expand Down Expand Up @@ -325,7 +325,7 @@ describe('logger', () => {
logDebug(specialMessage);

expect(consoleLogSpy).toHaveBeenCalledWith(
`${LOG_PREFIX} [DEBUG]`,
`[${LOG_PREFIX}DEBUG]`,
specialMessage
);
});
Expand All @@ -336,7 +336,7 @@ describe('logger', () => {
logDebug('message', null, undefined);

expect(consoleLogSpy).toHaveBeenCalledWith(
`${LOG_PREFIX} [DEBUG]`,
`[${LOG_PREFIX}DEBUG]`,
'message',
null,
undefined
Expand All @@ -351,7 +351,7 @@ describe('logger', () => {
logDebug(longMessage);

expect(consoleLogSpy).toHaveBeenCalledWith(
`${LOG_PREFIX} [DEBUG]`,
`[${LOG_PREFIX}DEBUG]`,
longMessage
);
});
Expand Down Expand Up @@ -406,17 +406,17 @@ describe('logger', () => {
expect(consoleLogSpy).toHaveBeenCalledTimes(3);
expect(consoleLogSpy).toHaveBeenNthCalledWith(
1,
`${LOG_PREFIX} [DEBUG]`,
`[${LOG_PREFIX}DEBUG]`,
'first'
);
expect(consoleLogSpy).toHaveBeenNthCalledWith(
2,
`${LOG_PREFIX} [DEBUG]`,
`[${LOG_PREFIX}DEBUG]`,
'second'
);
expect(consoleLogSpy).toHaveBeenNthCalledWith(
3,
`${LOG_PREFIX} [DEBUG]`,
`[${LOG_PREFIX}DEBUG]`,
'third'
);
});
Expand Down
Loading
Loading