Greasy Fork is available in English.
Reduces ChatGPT UI lag by disabling costly visuals, collapsing old turns, lazy-loading media, and trimming sidebar rendering.
// ==UserScript==
// @name ChatGPT LagFix Lite
// @namespace local.chatgpt.lagfix
// @version 1.0.0
// @license MIT
// @description Reduces ChatGPT UI lag by disabling costly visuals, collapsing old turns, lazy-loading media, and trimming sidebar rendering.
// @author rexxx
// @match https://chatgpt.com/*
// @match https://chat.openai.com/*
// @run-at document-start
// @grant none
// ==/UserScript==
(() => {
"use strict";
const STORAGE_KEY = "chatgpt_lagfix_lite_settings_v1";
const DEFAULTS = {
speedMode: true,
collapseOldTurns: true,
keepLatestTurns: 12,
trimSidebar: true,
keepSidebarItems: 30,
hideSidebar: false,
lazyMedia: true,
deChrome: true,
showPanel: true
};
let settings = loadSettings();
let scanQueued = false;
let observerStarted = false;
function loadSettings() {
try {
return { ...DEFAULTS, ...JSON.parse(localStorage.getItem(STORAGE_KEY) || "{}") };
} catch {
return { ...DEFAULTS };
}
}
function saveSettings() {
localStorage.setItem(STORAGE_KEY, JSON.stringify(settings));
}
function idle(fn) {
if ("requestIdleCallback" in window) {
requestIdleCallback(fn, { timeout: 800 });
} else {
setTimeout(fn, 80);
}
}
function queueScan() {
if (scanQueued) return;
scanQueued = true;
setTimeout(() => {
scanQueued = false;
idle(scan);
}, 120);
}
function injectStyle() {
if (document.getElementById("cglf-style")) return;
const style = document.createElement("style");
style.id = "cglf-style";
style.textContent = `
html.cglf-speed *,
html.cglf-speed *::before,
html.cglf-speed *::after {
animation-duration: 1ms !important;
animation-delay: 0ms !important;
animation-iteration-count: 1 !important;
transition-duration: 1ms !important;
transition-delay: 0ms !important;
scroll-behavior: auto !important;
}
html.cglf-speed [class*="animate-"],
html.cglf-speed [style*="animation"] {
animation: none !important;
}
html.cglf-speed [class*="backdrop"],
html.cglf-speed [style*="backdrop-filter"],
html.cglf-speed [style*="-webkit-backdrop-filter"] {
backdrop-filter: none !important;
-webkit-backdrop-filter: none !important;
}
html.cglf-speed [class*="shadow"],
html.cglf-speed [style*="box-shadow"] {
box-shadow: none !important;
}
html.cglf-speed main article,
html.cglf-speed main [data-message-author-role] {
content-visibility: auto;
contain-intrinsic-size: 1px 180px;
}
html.cglf-collapse [data-cglf-old-turn="1"]:not([data-cglf-expanded="1"]) > :not(.cglf-placeholder) {
display: none !important;
}
html.cglf-collapse [data-cglf-old-turn="1"] {
contain: layout paint style !important;
margin-block: 4px !important;
}
.cglf-placeholder {
width: 100%;
display: block;
text-align: left;
cursor: pointer;
border: 1px solid color-mix(in srgb, currentColor 18%, transparent);
border-radius: 10px;
padding: 7px 10px;
margin: 4px 0;
font: 12px/1.35 system-ui, -apple-system, BlinkMacSystemFont, "Segoe UI", sans-serif;
opacity: 0.72;
background: color-mix(in srgb, Canvas 85%, currentColor 5%);
color: inherit;
}
.cglf-placeholder:hover {
opacity: 1;
}
html.cglf-trim-sidebar [data-cglf-sidebar-old="1"] {
display: none !important;
}
html.cglf-hide-sidebar aside,
html.cglf-hide-sidebar nav[aria-label*="Chat" i],
html.cglf-hide-sidebar [data-testid*="sidebar" i] {
display: none !important;
}
html.cglf-dechrome [aria-label*="Share" i],
html.cglf-dechrome [data-testid*="share" i],
html.cglf-dechrome [aria-label*="Copy link" i],
html.cglf-dechrome [data-testid*="announcement" i],
html.cglf-dechrome [class*="announcement" i],
html.cglf-dechrome [class*="upsell" i],
html.cglf-dechrome [data-testid*="upsell" i] {
display: none !important;
}
#cglf-panel {
position: fixed;
left: 10px;
bottom: 10px;
z-index: 2147483647;
font: 12px/1.35 system-ui, -apple-system, BlinkMacSystemFont, "Segoe UI", sans-serif;
color: CanvasText;
}
#cglf-panel.cglf-hidden {
display: none !important;
}
#cglf-panel button {
font: inherit;
}
#cglf-panel-toggle {
border: 1px solid color-mix(in srgb, CanvasText 20%, transparent);
border-radius: 999px;
padding: 7px 10px;
cursor: pointer;
background: Canvas;
color: CanvasText;
opacity: 0.82;
}
#cglf-panel-toggle:hover {
opacity: 1;
}
#cglf-panel-body {
display: none;
min-width: 210px;
margin-top: 6px;
padding: 8px;
border: 1px solid color-mix(in srgb, CanvasText 20%, transparent);
border-radius: 12px;
background: Canvas;
box-shadow: 0 8px 30px rgba(0,0,0,.18);
}
#cglf-panel.cglf-open #cglf-panel-body {
display: block;
}
#cglf-panel-body button {
width: 100%;
display: block;
margin: 5px 0;
padding: 6px 8px;
border-radius: 8px;
border: 1px solid color-mix(in srgb, CanvasText 15%, transparent);
background: color-mix(in srgb, Canvas 90%, CanvasText 5%);
color: CanvasText;
cursor: pointer;
text-align: left;
}
#cglf-panel-body button:hover {
background: color-mix(in srgb, Canvas 82%, CanvasText 9%);
}
#cglf-panel-body .cglf-note {
opacity: .66;
margin: 6px 2px 2px;
}
`;
const root = document.head || document.documentElement;
if (root) root.appendChild(style);
}
function applyRootClasses() {
const root = document.documentElement;
if (!root) return;
root.classList.toggle("cglf-speed", !!settings.speedMode);
root.classList.toggle("cglf-collapse", !!settings.collapseOldTurns);
root.classList.toggle("cglf-trim-sidebar", !!settings.trimSidebar);
root.classList.toggle("cglf-hide-sidebar", !!settings.hideSidebar);
root.classList.toggle("cglf-dechrome", !!settings.deChrome);
}
function getConversationTurns() {
const articleTurns = [
...document.querySelectorAll(
'main article[data-testid*="conversation-turn" i], main article'
)
].filter(el => el.querySelector('[data-message-author-role]'));
if (articleTurns.length) return uniqueElements(articleTurns);
const roleNodes = [
...document.querySelectorAll('main [data-message-author-role]')
];
return uniqueElements(
roleNodes.map(node =>
node.closest('article, [data-testid*="conversation-turn" i], .group, .relative') || node
)
);
}
function uniqueElements(elements) {
const seen = new Set();
const out = [];
for (const el of elements) {
if (!el || seen.has(el)) continue;
seen.add(el);
out.push(el);
}
return out;
}
function summarizeTurn(turn) {
const roleNode = turn.querySelector("[data-message-author-role]");
const role = roleNode?.getAttribute("data-message-author-role") || "turn";
let text = roleNode?.textContent || turn.textContent || "";
text = text
.replace(/\s+/g, " ")
.replace(/^LagFix: hidden older .*?click to expand\.?\s*/i, "")
.trim();
if (text.length > 120) text = text.slice(0, 120).trim() + "…";
if (!text) text = "content hidden";
return { role, text };
}
function ensurePlaceholder(turn, index, total) {
let placeholder = turn.querySelector(":scope > .cglf-placeholder");
if (!placeholder) {
placeholder = document.createElement("button");
placeholder.type = "button";
placeholder.className = "cglf-placeholder";
placeholder.addEventListener("click", event => {
event.preventDefault();
event.stopPropagation();
turn.dataset.cglfExpanded =
turn.dataset.cglfExpanded === "1" ? "0" : "1";
});
turn.insertBefore(placeholder, turn.firstChild);
}
if (!placeholder.dataset.cglfReady) {
const { role, text } = summarizeTurn(turn);
placeholder.textContent =
`LagFix: hidden older ${role} message ${index + 1}/${total} — click to expand. ${text}`;
placeholder.dataset.cglfReady = "1";
}
}
function clearPlaceholder(turn) {
const placeholder = turn.querySelector(":scope > .cglf-placeholder");
if (placeholder) placeholder.remove();
}
function collapseOldTurns() {
const turns = getConversationTurns();
const keep = Math.max(2, Number(settings.keepLatestTurns) || DEFAULTS.keepLatestTurns);
const cutoff = Math.max(0, turns.length - keep);
turns.forEach((turn, index) => {
const old = settings.collapseOldTurns && index < cutoff;
if (old) {
turn.dataset.cglfOldTurn = "1";
ensurePlaceholder(turn, index, turns.length);
} else {
delete turn.dataset.cglfOldTurn;
delete turn.dataset.cglfExpanded;
clearPlaceholder(turn);
}
});
}
function trimSidebar() {
const links = [
...document.querySelectorAll(
'aside a[href^="/c/"], nav a[href^="/c/"], aside a[href*="/c/"], nav a[href*="/c/"]'
)
];
const rows = uniqueElements(
links.map(link =>
link.closest('li, [role="listitem"], [data-testid*="conversation" i], div') || link
)
);
const keep = Math.max(5, Number(settings.keepSidebarItems) || DEFAULTS.keepSidebarItems);
rows.forEach((row, index) => {
if (settings.trimSidebar && index >= keep) {
row.dataset.cglfSidebarOld = "1";
} else {
delete row.dataset.cglfSidebarOld;
}
});
}
function lazyMedia() {
if (!settings.lazyMedia) return;
document.querySelectorAll("img:not([data-cglf-lazy])").forEach(img => {
img.loading = "lazy";
img.decoding = "async";
img.dataset.cglfLazy = "1";
});
document.querySelectorAll("iframe:not([data-cglf-lazy])").forEach(frame => {
frame.loading = "lazy";
frame.dataset.cglfLazy = "1";
});
document.querySelectorAll("video:not([data-cglf-lazy])").forEach(video => {
video.preload = "none";
video.dataset.cglfLazy = "1";
});
document
.querySelectorAll('[data-cglf-old-turn="1"]:not([data-cglf-expanded="1"]) video')
.forEach(video => {
try {
video.pause();
} catch {}
});
}
function ensurePanel() {
if (!settings.showPanel) return;
if (!document.body || document.getElementById("cglf-panel")) return;
const panel = document.createElement("div");
panel.id = "cglf-panel";
panel.innerHTML = `
<button id="cglf-panel-toggle" type="button" title="ChatGPT LagFix Lite">⚡ LagFix</button>
<div id="cglf-panel-body">
<button type="button" data-cglf-action="speed"></button>
<button type="button" data-cglf-action="collapse"></button>
<button type="button" data-cglf-action="sidebar-trim"></button>
<button type="button" data-cglf-action="sidebar-hide"></button>
<button type="button" data-cglf-action="dechrome"></button>
<button type="button" data-cglf-action="keep"></button>
<button type="button" data-cglf-action="reset">Reset LagFix settings</button>
<div class="cglf-note">Hotkeys: Alt+Shift+L speed, Alt+Shift+O old turns, Alt+Shift+S sidebar.</div>
</div>
`;
document.body.appendChild(panel);
panel.querySelector("#cglf-panel-toggle").addEventListener("click", () => {
panel.classList.toggle("cglf-open");
});
panel.addEventListener("click", event => {
const button = event.target.closest("[data-cglf-action]");
if (!button) return;
const action = button.dataset.cglfAction;
if (action === "speed") settings.speedMode = !settings.speedMode;
if (action === "collapse") settings.collapseOldTurns = !settings.collapseOldTurns;
if (action === "sidebar-trim") settings.trimSidebar = !settings.trimSidebar;
if (action === "sidebar-hide") settings.hideSidebar = !settings.hideSidebar;
if (action === "dechrome") settings.deChrome = !settings.deChrome;
if (action === "keep") {
const next = Number(prompt("Keep how many latest messages fully rendered?", String(settings.keepLatestTurns)));
if (Number.isFinite(next) && next >= 2 && next <= 100) {
settings.keepLatestTurns = Math.floor(next);
}
}
if (action === "reset") {
settings = { ...DEFAULTS };
}
saveSettings();
applyRootClasses();
updatePanel();
queueScan();
});
updatePanel();
}
function updatePanel() {
const panel = document.getElementById("cglf-panel");
if (!panel) return;
const label = (name, on) => `${on ? "✓" : "○"} ${name}`;
const set = (action, text) => {
const btn = panel.querySelector(`[data-cglf-action="${action}"]`);
if (btn) btn.textContent = text;
};
set("speed", label("Speed mode", settings.speedMode));
set("collapse", label("Collapse old messages", settings.collapseOldTurns));
set("sidebar-trim", label("Trim sidebar history", settings.trimSidebar));
set("sidebar-hide", label("Hide sidebar", settings.hideSidebar));
set("dechrome", label("Hide extra chrome", settings.deChrome));
set("keep", `Keep latest messages: ${settings.keepLatestTurns}`);
}
function bindHotkeys() {
if (window.__cglfHotkeysBound) return;
window.__cglfHotkeysBound = true;
document.addEventListener("keydown", event => {
if (!event.altKey || !event.shiftKey) return;
const code = event.code;
if (code === "KeyL") {
settings.speedMode = !settings.speedMode;
} else if (code === "KeyO") {
settings.collapseOldTurns = !settings.collapseOldTurns;
} else if (code === "KeyS") {
settings.hideSidebar = !settings.hideSidebar;
} else {
return;
}
event.preventDefault();
saveSettings();
applyRootClasses();
updatePanel();
queueScan();
});
}
function scan() {
injectStyle();
applyRootClasses();
ensurePanel();
bindHotkeys();
collapseOldTurns();
trimSidebar();
lazyMedia();
}
function startObserver() {
if (observerStarted || !document.documentElement) return;
observerStarted = true;
const observer = new MutationObserver(queueScan);
observer.observe(document.documentElement, {
childList: true,
subtree: true
});
queueScan();
}
injectStyle();
applyRootClasses();
startObserver();
if (document.readyState === "loading") {
document.addEventListener("DOMContentLoaded", () => {
scan();
startObserver();
}, { once: true });
} else {
scan();
startObserver();
}
})();