🎞️ The Video Demonstration
demo.mp4
Production-ready MV3 extension that intercepts ChatGPT conversation data before React ingests it, clamps to the last N messages to keep long chats fast, and provides a clean UI to reveal older messages on demand.
ChatGPT loads the entire conversation into React state, which slows down long chats. This extension:
- Intercepts conversation data before React ingests it (fetch, ReadableStream, EventSource, bootstrap JSON)
- Clamps to the last N messages (default tail is configurable via the popup or per-conversation)
- Stores the full “flat” conversation in localStorage (off-React) with metadata to safely rehydrate
- Provides a toolbar to load older messages on demand (+10 previous, Show all, reset to default)
- Renders older messages outside React (plain DOM) so React’s state stays small
- Adds a minimalist status bar showing total messages and load time (in seconds)
- Open Chrome and navigate to
chrome://extensions/ - Enable "Developer mode" (toggle in top-right)
- Click "Load unpacked"
- Select the
extention/folder
extention/
├── manifest.json # MV3 manifest with content_scripts (MAIN & ISOLATED worlds)
├── popup.html # Settings popup UI
├── popup.js # Popup logic (default tail, enable/disable, verbose logs)
├── util/
│ ├── settings-loader.js # ISOLATED: chrome.storage.sync → localStorage bridge + events
│ ├── logger.js # Buffered logger on window; console gated by cl:log:verbose
│ └── shared.js # Central utilities (IDs, traversal, tool filtering)
├── core/
│ ├── optimizer.js # Core config, storage helpers (localStorage), fetch hook
│ └── stream-hooks.js # ReadableStream, EventSource, bootstrap JSON, SPA watcher
├── lib/
│ ├── marked.min.js # Markdown parser
│ ├── purify.min.js # HTML sanitizer
│ └── highlight.min.js # Code syntax highlighting
├── theme/
│ ├── clx-icons.js # SVG icons
│ └── clx-theme.js # CSS, Markdown rendering, article creation
└── ui/
├── toolbar.js # Toolbar (+10 / Show all / reset) and status bar
└── loader.js # Loading overlay + load time capture (ms)
-
Fetch API (
core/optimizer.js)- Intercepts
/backend-api/conversation/:id - Trims mapping to last N renderable messages
- Returns rewritten JSON to React
- Intercepts
-
ReadableStream (
core/stream-hooks.js)- Patches
ReadableStream.prototype.getReader().read() - Transforms streaming flight frames before React consumes them
- Patches
-
EventSource (
core/stream-hooks.js)- Wraps EventSource message listeners
- Transforms SSE frames containing conversation data
-
Bootstrap JSON (
core/stream-hooks.js)- Rewrites inline
<script type="application/json">when mapping detected - Catches conversation data embedded in the page
- Rewrites inline
-
React Router streaming path (
core/stream-hooks.js)- Mapping replacement + SPA watcher during navigation
Initial load → Fetch/Stream/Bootstrap interceptors → Trim to N messages → React (small state)
↓
Store full conversation
in localStorage
↓
Toolbar injects older
messages as plain DOM
- Default messages to show (tail)
- Enable optimizer (on/off)
- Verbose logs (developer)
- Enable optimizer for this page (appears when a chat tab is active)
Settings are persisted in chrome.storage.sync and bridged to localStorage for runtime access in the MAIN world.
Per-page switch: use the popup’s “Enable optimizer for this page” (visible when a chat tab is active) to override only the current conversation.
Open DevTools console and check for logs:
[ChatGPT-Opt] core:ready- Core optimizer loaded[ChatGPT-Opt] stream:init:complete- Stream hooks installed[ChatGPT-Opt] fetch:trimmed- Conversation trimmed (shows total/kept counts)[ChatGPT-Opt] stream:readablestream:patched- ReadableStream hook active[ChatGPT-Opt] stream:eventsource:patched- EventSource hook active[ChatGPT-Opt] stream:bootstrap:patched- Bootstrap JSON interceptor active
Tip: The logger is buffered and quiet by default. Enable console output via popup “Verbose logs (developer)” or in console: window.TailLog.setVerbose(true).
cl:global-settings—{ defaultTail: number, optimizerEnabled: boolean }cl:tail:<conversation-id>— Number of messages to keep (default 10, min 10, max 800)cl:meta:<conversation-id>—{ renderableTotal, keptRenderableByReact }cl:flat:<conversation-id>— Flat array[{id, role, text, time}, ...]cl:inj:<conversation-id>— Count of injected older messagescl:last-load-ms:<conversation-id>— Last measured load time in mscl:log:verbose—'1'enables console output for buffered loggercl:loader—'1'or'0'to enable/disable loading overlay
✅ Works on /c/<uuid> conversation pages
✅ Handles SPA navigation (no page reload needed)
✅ Toolbar with Show all / +10 previous / reset controls (respects global default)
✅ Scroll anchoring after +N insert (viewport stays at boundary)
✅ Markdown rendering with code highlighting and copy buttons
✅ Loading overlay while thread mounts; auto-hides error after ~2s
✅ Status bar with total messages + seconds (live timer)
✅ Preserves conversation structure (parent/children/current_node)
✅ Filters tool noise and reasoning tokens
✅ Quiet buffered logger with optional verbose console
✅ Settings popup (default tail, enable optimizer, verbose logs)
✅ Per-page ON/OFF toggle in popup (overrides global just for current conversation)
- Check
chrome://extensions/for errors - Verify all files are present (see structure above)
- Check browser console for script errors
- Open DevTools console
- Look for
[ChatGPT-Opt] fetch:trimmedorstream:shrinklogs - If missing, the fetch/stream hooks aren't running
- Verify you're on
chatgpt.comorchat.openai.com
- Toolbar waits for first
<article>in the thread - Check console for errors in
ui/toolbar.js - Verify
theme/clx-theme.jsloaded (provides makeArticle)
- Recommended: open the extension popup and set “Default messages to show”
- Advanced per-conversation override:
// In console (advanced):
localStorage.setItem('cl:tail:<conversation-id>', '20');// In console:
localStorage.setItem('cl:loader', '0');