// ==UserScript== // @name ChatGPT DeBloat // @namespace gpt.debloat // @version 1.1.0 // @description Keep last N messages visible; unload media in hidden ones; Show Older/All works without breaking the page. // @match https://chat.openai.com/* // @match https://chatgpt.com/* // @run-at document-idle // @grant none // ==/UserScript== (() => { 'use strict'; // ---------- Config (saved across sessions) ---------- const CFG = { keepLast: loadNum('cgpt_keep_last_safe2', 120), chunk: loadNum('cgpt_chunk_safe2', 40), auto: loadBool('cgpt_auto_safe2', true), unloadHiddenMedia: loadBool('cgpt_unload_media2', true), }; // ---------- Utilities ---------- function save(k,v){ try{ localStorage.setItem(k, JSON.stringify(v)); }catch{} } function loadNum(k,d){ try{ const v = JSON.parse(localStorage.getItem(k)); return Number.isFinite(v)?v:d; }catch{ return d; } } function loadBool(k,d){ try{ const v = JSON.parse(localStorage.getItem(k)); return typeof v==='boolean'?v:d; }catch{ return d; } } const BLANK_IMG = 'data:image/gif;base64,R0lGODlhAQABAIAAAAAAAP///ywAAAAAAQABAAACAUwAOw=='; function getContainer(){ return document.querySelector('[data-testid="conversation-turns"]') || document.querySelector('main') || document.body; } // NOTE: include hidden nodes (no offsetParent filtering!) function getMessageNodes(){ const root = getContainer(); if (!root) return []; const list = root.querySelectorAll('[data-testid^="conversation-turn-"], div[data-message-id], article'); // de-dup while preserving order const seen = new Set(); const out = []; list.forEach(n => { if (!seen.has(n)) { seen.add(n); out.push(n); } }); return out; } function unloadMediaIn(node){ if(!CFG.unloadHiddenMedia) return; node.querySelectorAll('img').forEach(img=>{ if(img.dataset._cgptUnloaded) return; if(img.src && img.src !== BLANK_IMG){ img.dataset._cgptSrc = img.src; img.src = BLANK_IMG; } if(img.srcset){ img.dataset._cgptSrcset = img.srcset; img.srcset = ''; } img.loading = 'lazy'; img.decoding='async'; img.dataset._cgptUnloaded = '1'; }); node.querySelectorAll('video').forEach(v=>{ if(v.dataset._cgptUnloaded) return; try{ v.pause(); }catch{} const s = v.getAttribute('src'); if(s){ v.dataset._cgptSrc = s; v.removeAttribute('src'); } v.querySelectorAll('source').forEach(src=>{ const s2 = src.getAttribute('src'); if(s2){ src.dataset._cgptSrc = s2; src.removeAttribute('src'); } }); v.load?.(); v.dataset._cgptUnloaded = '1'; }); } function restoreMediaIn(node){ node.querySelectorAll('img').forEach(img=>{ if(img.dataset._cgptSrc){ img.src = img.dataset._cgptSrc; delete img.dataset._cgptSrc; } if(img.dataset._cgptSrcset){ img.srcset = img.dataset._cgptSrcset; delete img.dataset._cgptSrcset; } if(img.dataset._cgptUnloaded){ delete img.dataset._cgptUnloaded; } }); node.querySelectorAll('video').forEach(v=>{ if(v.dataset._cgptSrc){ v.setAttribute('src', v.dataset._cgptSrc); delete v.dataset._cgptSrc; } v.querySelectorAll('source').forEach(src=>{ if(src.dataset._cgptSrc){ src.setAttribute('src', src.dataset._cgptSrc); delete src.dataset._cgptSrc; } }); if(v.dataset._cgptUnloaded){ delete v.dataset._cgptUnloaded; } try{ v.load?.(); }catch{} }); } // ---------- Trim & Restore ---------- function applyTrim(){ const nodes = getMessageNodes(); const total = nodes.length; const toKeep = Math.min(CFG.keepLast, total); const cut = total - toKeep; for(let i=0;i{ n.classList.remove('cgpt-safe-hidden'); restoreMediaIn(n); }); updateStats(nodes.length, 0); refreshUI(); } // ---------- UI ---------- const STYLE = ` .cgpt-safe-hidden{display:none!important} #cgpt-ui{position:fixed;right:14px;bottom:14px;z-index:2147483647;background:rgba(22,22,28,.95); color:#eee;border:1px solid #3b3b48;border-radius:12px;padding:10px 12px; font:12px/1.4 ui-sans-serif,system-ui,-apple-system,Segoe UI,Roboto;box-shadow:0 8px 24px rgba(0,0,0,.35);backdrop-filter:blur(4px);user-select:none} #cgpt-ui .row{display:flex;gap:8px;align-items:center;margin:6px 0;flex-wrap:wrap} #cgpt-ui button,#cgpt-ui input{background:#15151b;color:#eee;border:1px solid #40404f;border-radius:8px;padding:6px 8px} #cgpt-ui .muted{color:#b7b7c7} #cgpt-ui .drag{cursor:move} `; function ensureStyle(){ if(document.getElementById('cgpt-style')) return; const s=document.createElement('style'); s.id='cgpt-style'; s.textContent=STYLE; document.head.appendChild(s); } function buildUI(){ if(document.getElementById('cgpt-ui')) return; const box=document.createElement('div'); box.id='cgpt-ui'; box.innerHTML = `
GPT DeBloat
Keep last
`; document.body.appendChild(box); // wire const keep = document.getElementById('cgpt-keep'); keep.value = CFG.keepLast === Number.MAX_SAFE_INTEGER ? 3000 : CFG.keepLast; document.getElementById('cgpt-auto').checked = CFG.auto; document.getElementById('cgpt-unload').checked = CFG.unloadHiddenMedia; document.getElementById('cgpt-trim').onclick = applyTrim; document.getElementById('cgpt-more').onclick = showOlderChunk; document.getElementById('cgpt-all').onclick = showAll; document.getElementById('cgpt-reset').onclick = resetAll; document.getElementById('cgpt-close').onclick = ()=>{ box.style.display='none'; }; keep.onchange = e=>{ const v = Math.max(20, Math.min(3000, Number(e.target.value)||120)); CFG.keepLast = v; save('cgpt_keep_last_safe2', v); applyTrim(); }; document.getElementById('cgpt-auto').onchange = e=>{ CFG.auto = !!e.target.checked; save('cgpt_auto_safe2', CFG.auto); CFG.auto ? startObserver() : stopObserver(); }; document.getElementById('cgpt-unload').onchange = e=>{ CFG.unloadHiddenMedia = !!e.target.checked; save('cgpt_unload_media2', CFG.unloadHiddenMedia); applyTrim(); }; makeDraggable(box); } function updateStats(total, hidden){ const el=document.getElementById('cgpt-stats'); if(el) el.textContent = ` · ${total-hidden}/${total} showing`; } function refreshUI(){ const keepEl=document.getElementById('cgpt-keep'); if(keepEl) keepEl.value = (CFG.keepLast===Number.MAX_SAFE_INTEGER)?3000:CFG.keepLast; const autoEl=document.getElementById('cgpt-auto'); if(autoEl) autoEl.checked = CFG.auto; const unEl=document.getElementById('cgpt-unload'); if(unEl) unEl.checked = CFG.unloadHiddenMedia; } function makeDraggable(el){ let sx=0, sy=0, ox=0, oy=0, drag=false; const h = el.querySelector('.drag') || el; h.addEventListener('mousedown', e=>{ if(e.button!==0) return; drag=true; sx=e.clientX; sy=e.clientY; const r=el.getBoundingClientRect(); ox=r.left; oy=r.top; e.preventDefault(); }); window.addEventListener('mousemove', e=>{ if(!drag) return; const nx = Math.max(6, ox + (e.clientX - sx)); const ny = Math.max(6, oy + (e.clientY - sy)); el.style.right='auto'; el.style.bottom='auto'; el.style.left = nx+'px'; el.style.top = ny+'px'; }); window.addEventListener('mouseup', ()=>drag=false); } // ---------- Observer ---------- let MO=null, deb=null; function startObserver(){ const root=getContainer(); if(!root) return; stopObserver(); MO = new MutationObserver(()=>{ if(!CFG.auto) return; clearTimeout(deb); deb = setTimeout(applyTrim, 200); }); MO.observe(root, { childList:true, subtree:true }); } function stopObserver(){ try{ MO?.disconnect(); }catch{} } // ---------- Boot ---------- function boot(){ ensureStyle(); buildUI(); setTimeout(applyTrim, 600); if(CFG.auto) startObserver(); // handle SPA route changes let prev = location.href; setInterval(()=>{ if(location.href!==prev){ prev=location.href; setTimeout(()=>{ applyTrim(); if(CFG.auto){ startObserver(); } }, 600);} }, 500); } if(document.readyState==='complete' || document.readyState==='interactive') boot(); else document.addEventListener('DOMContentLoaded', boot, { once:true }); })();