Created
June 3, 2026 18:07
-
-
Save divyam234/0ab9540475fd65f7f254b3b6d37a263b to your computer and use it in GitHub Desktop.
Chatgpt large chat lag fix
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| // ==UserScript== | |
| // @name ChatGPT Large Chat Performance | |
| // @namespace local.chatgpt.performance | |
| // @version 0.4.0 | |
| // @description Makes very large ChatGPT conversations less sluggish with CSS containment and detachable turbo scrolling. | |
| // @match https://chatgpt.com/* | |
| // @match https://chat.openai.com/* | |
| // @run-at document-idle | |
| // @grant none | |
| // ==/UserScript== | |
| (function () { | |
| 'use strict'; | |
| const STYLE_ID = 'tm-chatgpt-large-chat-performance-style'; | |
| const HEIGHT_VAR = '--tm-cgpt-turn-height'; | |
| const TURN_SELECTOR = '[data-turn-id-container]'; | |
| const SCROLL_IDLE_MS = 180; | |
| let enabled = true; | |
| let root = null; | |
| let ro = null; | |
| let mo = null; | |
| let scanTimer = 0; | |
| let scrollIdleTimer = 0; | |
| let parked = false; | |
| let parkedNodes = []; | |
| let placeholder = null; | |
| const seen = new WeakSet(); | |
| function addStyles() { | |
| if (document.getElementById(STYLE_ID)) return; | |
| const style = document.createElement('style'); | |
| style.id = STYLE_ID; | |
| style.textContent = ` | |
| ${TURN_SELECTOR} { | |
| content-visibility: auto !important; | |
| contain: layout style paint !important; | |
| contain-intrinsic-size: auto var(${HEIGHT_VAR}, 760px) !important; | |
| } | |
| .markdown, | |
| .text-message, | |
| pre, | |
| code { | |
| overflow-wrap: anywhere !important; | |
| } | |
| video, | |
| canvas, | |
| iframe { | |
| content-visibility: auto !important; | |
| contain: layout paint !important; | |
| } | |
| #tm-cgpt-thread-placeholder { | |
| display: block !important; | |
| contain: strict !important; | |
| visibility: hidden !important; | |
| pointer-events: none !important; | |
| } | |
| `; | |
| document.documentElement.appendChild(style); | |
| } | |
| function findThreadScrollRoot() { | |
| const thread = document.querySelector('#thread'); | |
| for (let el = thread?.parentElement; el; el = el.parentElement) { | |
| const style = getComputedStyle(el); | |
| if (/(auto|scroll|overlay)/.test(style.overflowY) && el.scrollHeight > el.clientHeight + 200) { | |
| return el; | |
| } | |
| } | |
| return document.scrollingElement || document.documentElement; | |
| } | |
| function rememberHeight(entry) { | |
| const height = Math.ceil(entry.borderBoxSize?.[0]?.blockSize || entry.contentRect.height); | |
| if (height > 40) entry.target.style.setProperty(HEIGHT_VAR, `${height}px`); | |
| } | |
| function observeTurn(turn) { | |
| if (seen.has(turn)) return; | |
| seen.add(turn); | |
| ro.observe(turn); | |
| } | |
| function scanTurns() { | |
| if (!enabled || !ro) return; | |
| document.querySelectorAll(TURN_SELECTOR).forEach(observeTurn); | |
| } | |
| function scheduleScan() { | |
| clearTimeout(scanTimer); | |
| scanTimer = window.setTimeout(scanTurns, 150); | |
| } | |
| function getThread() { | |
| return document.querySelector('#thread'); | |
| } | |
| function parkThread() { | |
| if (parked) return; | |
| const thread = getThread(); | |
| if (!thread || !thread.childNodes.length) return; | |
| const height = Math.max(thread.scrollHeight, thread.getBoundingClientRect().height, 1000); | |
| placeholder = document.createElement('div'); | |
| placeholder.id = 'tm-cgpt-thread-placeholder'; | |
| placeholder.style.minHeight = `${Math.ceil(height)}px`; | |
| parkedNodes = [...thread.childNodes]; | |
| thread.replaceChildren(placeholder); | |
| thread.style.minHeight = `${Math.ceil(height)}px`; | |
| parked = true; | |
| } | |
| function restoreThread() { | |
| if (!parked) return; | |
| const thread = getThread(); | |
| if (thread) { | |
| thread.replaceChildren(...parkedNodes); | |
| thread.style.minHeight = ''; | |
| } | |
| parkedNodes = []; | |
| placeholder = null; | |
| parked = false; | |
| scheduleScan(); | |
| } | |
| function onScroll() { | |
| parkThread(); | |
| clearTimeout(scrollIdleTimer); | |
| scrollIdleTimer = window.setTimeout(restoreThread, SCROLL_IDLE_MS); | |
| } | |
| function start() { | |
| stop(false); | |
| enabled = true; | |
| addStyles(); | |
| root = findThreadScrollRoot(); | |
| ro = new ResizeObserver((entries) => entries.forEach(rememberHeight)); | |
| scanTurns(); | |
| mo = new MutationObserver(scheduleScan); | |
| mo.observe(document.body, { childList: true, subtree: true }); | |
| root?.addEventListener('scroll', onScroll, { passive: true }); | |
| } | |
| function stop(removeStyles = true) { | |
| root?.removeEventListener('scroll', onScroll); | |
| ro?.disconnect(); | |
| mo?.disconnect(); | |
| root = null; | |
| ro = null; | |
| mo = null; | |
| clearTimeout(scanTimer); | |
| clearTimeout(scrollIdleTimer); | |
| restoreThread(); | |
| if (removeStyles) document.getElementById(STYLE_ID)?.remove(); | |
| } | |
| function toggle() { | |
| enabled = !enabled; | |
| if (enabled) start(); | |
| else stop(); | |
| console.info(`[ChatGPT Large Chat Performance] ${enabled ? 'enabled' : 'disabled'}`); | |
| } | |
| window.addEventListener('keydown', (event) => { | |
| if (event.altKey && event.code === 'KeyV') toggle(); | |
| }); | |
| start(); | |
| })(); |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment