Culls messages from ChatGPT conversations via an overlay. Helpful for those who have low-end hardwares
当前为
// ==UserScript==
// @name ChatGPT.com Advanced Message Culler
// @namespace https://chatgpt.com/
// @version 1.3.6
// @description Culls messages from ChatGPT conversations via an overlay. Helpful for those who have low-end hardwares
// @author sytesn, ChatGPT
// @match https://chatgpt.com/*
// @grant none
// @license MIT
// ==/UserScript==
(function () {
'use strict';
let MESSAGE_LIMIT = 50;
let cullingEnabled = false;
let mode = "hide";
const revealBatch = 20;
// Load settings from localStorage
const storedEnabled = localStorage.getItem('culler-enabled');
const storedLimit = localStorage.getItem('culler-limit');
const storedMode = localStorage.getItem('culler-mode');
cullingEnabled = storedEnabled === "true";
MESSAGE_LIMIT = parseInt(storedLimit, 10) || MESSAGE_LIMIT;
mode = storedMode || mode;
const style = document.createElement('style');
style.textContent = `
#culler-panel {
position: fixed;
top: 10px;
left: 10px;
z-index: 9999;
background-color: #111;
padding: 10px;
border-radius: 8px;
font-family: Arial, sans-serif;
font-size: 13px;
color: #fff;
}
#culler-panel input, #culler-panel select {
background-color: #222;
color: #fff;
border: 1px solid #444;
border-radius: 4px;
padding: 2px 4px;
}
#culler-panel button {
margin-top:6px;
padding:4px 8px;
border: none;
border-radius: 4px;
background-color: #444;
color: #fff;
cursor: pointer;
}
#showMoreOverlay {
position: sticky;
top: 0;
background: rgba(50,50,50,0.95);
color: #fff;
padding: 10px;
text-align: center;
z-index: 10000;
border-radius: 0 0 8px 8px;
box-shadow: 0 4px 10px rgba(0,0,0,0.4);
user-select: none;
}
`;
document.head.appendChild(style);
const panel = document.createElement('div');
panel.id = 'culler-panel';
panel.innerHTML = `
<div style="margin-bottom:6px;font-weight:bold;">Message Culler</div>
<label style="display:block; margin-bottom:4px;">
<input type="checkbox" id="culler-enable">
Enable
</label>
<label style="display:block;margin-top:6px;">
Keep last <input type="number" id="culler-limit" value="${MESSAGE_LIMIT}" style="width: 50px;">
</label>
<label style="display:block;margin-top:6px;">
Mode:
<select id="culler-mode">
<option value="hide">Hide</option>
<option value="remove">Remove</option>
</select>
</label>
<button id="culler-refresh">Refresh Now</button>
`;
document.body.appendChild(panel);
(function makeDraggable(el) {
// Load previous position
const savedLeft = localStorage.getItem('culler-pos-left');
const savedTop = localStorage.getItem('culler-pos-top');
if (savedLeft && savedTop) {
el.style.left = savedLeft;
el.style.top = savedTop;
}
let isDragging = false, offsetX, offsetY;
el.addEventListener('mousedown', e => {
if (["INPUT", "SELECT", "BUTTON"].includes(e.target.tagName)) return;
isDragging = true;
offsetX = e.clientX - el.offsetLeft;
offsetY = e.clientY - el.offsetTop;
});
document.addEventListener('mousemove', e => {
if (isDragging) {
const left = (e.clientX - offsetX);
const top = (e.clientY - offsetY);
el.style.left = `${left}px`;
el.style.top = `${top}px`;
localStorage.setItem('culler-pos-left', `${left}px`);
localStorage.setItem('culler-pos-top', `${top}px`);
}
});
document.addEventListener('mouseup', () => isDragging = false);
})(panel);
const enableCheckbox = document.getElementById('culler-enable');
const limitInput = document.getElementById('culler-limit');
const modeSelect = document.getElementById('culler-mode');
const refreshBtn = document.getElementById('culler-refresh');
// Apply stored values to inputs
enableCheckbox.checked = cullingEnabled;
limitInput.value = MESSAGE_LIMIT;
modeSelect.value = mode;
enableCheckbox.addEventListener('change', () => {
cullingEnabled = enableCheckbox.checked;
localStorage.setItem('culler-enabled', cullingEnabled);
applyCulling();
});
limitInput.addEventListener('change', () => {
MESSAGE_LIMIT = parseInt(limitInput.value, 10) || MESSAGE_LIMIT;
localStorage.setItem('culler-limit', MESSAGE_LIMIT);
applyCulling();
});
modeSelect.addEventListener('change', () => {
mode = modeSelect.value;
localStorage.setItem('culler-mode', mode);
applyCulling();
});
refreshBtn.addEventListener('click', () => {
applyCulling();
});
function getMessageElements() {
return Array.from(document.querySelectorAll('article[data-testid^="conversation-turn-"]'));
}
function applyCulling() {
const messages = getMessageElements();
const total = messages.length;
if (!cullingEnabled) {
messages.forEach(msg => {
msg.style.display = '';
msg.classList.remove('culled');
});
return;
}
messages.forEach((msg, i) => {
if (i < total - MESSAGE_LIMIT) {
if (mode === "remove") {
if (!msg.dataset.removed) {
msg.dataset.removed = "true";
msg.remove();
}
} else if (mode === "hide") {
msg.style.display = "none";
msg.classList.add("culled");
}
} else {
msg.style.display = "";
msg.classList.remove("culled");
}
});
}
let showMoreOverlay = null;
function createShowMoreOverlay() {
if (showMoreOverlay || mode !== "hide") return;
const convoContainer = document.querySelector("div.mt-1\\.5.flex.flex-col.text-sm.md\\:pb-9");
if (!convoContainer) return;
showMoreOverlay = document.createElement("div");
showMoreOverlay.id = "showMoreOverlay";
updateShowMoreText();
showMoreOverlay.addEventListener("click", () => {
doRevealBatch();
if (document.querySelectorAll('article.culled').length === 0) {
removeShowMoreOverlay();
}
});
convoContainer.prepend(showMoreOverlay);
}
function updateShowMoreText() {
if (showMoreOverlay) {
const hiddenCount = document.querySelectorAll('article.culled').length;
showMoreOverlay.textContent = `Show ${revealBatch} older messages (${hiddenCount} hidden)`;
}
}
function removeShowMoreOverlay() {
if (showMoreOverlay) {
showMoreOverlay.remove();
showMoreOverlay = null;
}
}
function doRevealBatch() {
const culled = Array.from(document.querySelectorAll('article.culled'));
let count = 0;
culled.slice(0, revealBatch).forEach(msg => {
msg.style.display = "";
msg.classList.remove("culled");
count++;
});
updateShowMoreText();
}
const scrollContainer = document.querySelector('main') || document.body;
scrollContainer.addEventListener("scroll", () => {
if (scrollContainer.scrollTop === 0 && mode === "hide" && document.querySelectorAll('article.culled').length > 0) {
createShowMoreOverlay();
} else {
removeShowMoreOverlay();
}
});
window.addEventListener("resize", () => {
if (scrollContainer.scrollTop === 0 && mode === "hide" && document.querySelectorAll('article.culled').length > 0) {
createShowMoreOverlay();
} else {
removeShowMoreOverlay();
}
});
setInterval(() => {
if (scrollContainer.scrollTop === 0 && mode === "hide" && document.querySelectorAll('article.culled').length > 0) {
createShowMoreOverlay();
updateShowMoreText();
} else {
removeShowMoreOverlay();
}
}, 1500);
const observer = new MutationObserver(() => {
if (cullingEnabled) {
applyCulling();
}
});
observer.observe(document.body, { childList: true, subtree: true });
setTimeout(() => {
applyCulling();
}, 1500);
})();