Arena.ai | DOM Performance Optimizer

Fixes sluggish scrolling in Arena by virtualizing old messages, flattening 30k+ node Shiki codeblocks, and neutralizing CSS blurs and transitions.

Na nainštalovanie skriptu si budete musieť nainštalovať rozšírenie, ako napríklad Tampermonkey, Greasemonkey alebo Violentmonkey.

Na inštaláciu tohto skriptu je potrebné nainštalovať rozšírenie, ako napríklad Tampermonkey.

Na nainštalovanie skriptu si budete musieť nainštalovať rozšírenie, ako napríklad Tampermonkey, % alebo Violentmonkey.

Na nainštalovanie skriptu si budete musieť nainštalovať rozšírenie, ako napríklad Tampermonkey alebo Userscripts.

Na inštaláciu tohto skriptu je potrebné nainštalovať rozšírenie, ako napríklad Tampermonkey.

Na inštaláciu tohto skriptu je potrebné nainštalovať rozšírenie správcu používateľských skriptov.

(Už mám správcu používateľských skriptov, nechajte ma ho nainštalovať!)

Na inštaláciu tohto štýlu je potrebné nainštalovať rozšírenie, ako napríklad Stylus.

Na inštaláciu tohto štýlu je potrebné nainštalovať rozšírenie, ako napríklad Stylus.

Na inštaláciu tohto štýlu je potrebné nainštalovať rozšírenie, ako napríklad Stylus.

Na inštaláciu tohto štýlu je potrebné nainštalovať rozšírenie správcu používateľských štýlov.

Na inštaláciu tohto štýlu je potrebné nainštalovať rozšírenie správcu používateľských štýlov.

Na inštaláciu tohto štýlu je potrebné nainštalovať rozšírenie správcu používateľských štýlov.

(Už mám správcu používateľských štýlov, nechajte ma ho nainštalovať!)

// ==UserScript==
// @name         Arena.ai | DOM Performance Optimizer
// @namespace    https://greasyfork.org/en/users/1462137-piknockyou
// @version      2.4
// @author       Piknockyou (vibe-coded)
// @license      AGPL-3.0
// @description  Fixes sluggish scrolling in Arena by virtualizing old messages, flattening 30k+ node Shiki codeblocks, and neutralizing CSS blurs and transitions.
// @match        *://*.arena.ai/*
// @match        *://*.canaryarena.ai/*
// @match        *://chat.lmsys.org/*
// @icon         https://arena.ai/favicon.ico
// @grant        GM_addStyle
// @grant        GM_getValue
// @grant        GM_setValue
// @run-at       document-start
// ==/UserScript==

(() => {
	let hasStarted = false;
	let pendingStart = false;

	// ═══════════════════════════════════════════════════════════════════
	// CONFIGURATION
	// ═══════════════════════════════════════════════════════════════════
	const DEFAULT_CONFIG = {
		visibleMessages: 6, // Number of recent messages to keep fully rendered
		flattenCodeblocks: true, // Strip <span> tags from old code blocks (massive React perf win)
		killTransitions: true, // Disable expensive transition:all globally
		killBlurs: true, // Disable backdrop-filter:blur (GPU win)
		addContainment: true, // Add CSS containment for scroll isolation
		autoClean: true, // Automatically prune on new messages
		autoCleanDelay: 1000, // ms delay after DOM change before cleaning
		showToolbar: true, // Show the bottom status toolbar
	};

	function getConfig() {
		const stored = GM_getValue("arenaPerfConfigDOM", null);
		if (stored) {
			try {
				return { ...DEFAULT_CONFIG, ...JSON.parse(stored) };
			} catch (_e) {}
		}
		return { ...DEFAULT_CONFIG };
	}

	function saveConfig(cfg) {
		GM_setValue("arenaPerfConfigDOM", JSON.stringify(cfg));
	}

	const config = getConfig();

	// ⚡ INSTANT CSS INJECTION (Before body loads)
	// This prevents the "flash" of transitions, blurs, and old messages.
	try {
		const SURGEON_ID = "arena-perf-surgeon";
		const buildInitialCSS = () => {
			const hideThreshold = config.visibleMessages + 1;
			let css = `ol.flex-col-reverse > div:nth-child(n+${hideThreshold + 1}), ol.flex-col-reverse > li:nth-child(n+${hideThreshold + 1}) { display: none !important; }`;
			if (config.killTransitions)
				css += `*, *::before, *::after { transition: none !important; animation-duration: 0s !important; }`;
			if (config.killBlurs)
				css += `[class*="backdrop-blur"], [style*="backdrop-filter"] { backdrop-filter: none !important; -webkit-backdrop-filter: none !important; }`;
			return css;
		};
		const style = document.createElement("style");
		style.id = SURGEON_ID;
		style.textContent = buildInitialCSS();
		(document.head || document.documentElement).appendChild(style);
	} catch (e) {
		console.error("[ArenaPerf] Early CSS fail", e);
	}

	// ═══════════════════════════════════════════════════════════════════
	// INTERNAL STATE
	// ═══════════════════════════════════════════════════════════════════
	let isPaused = false;
	const stats = { hiddenMsgs: 0, totalMsgs: 0, nodesFlattened: 0 };
	let observer = null;
	let cleanTimeout = null;
	let _settingsPopover = null;

	// ═══════════════════════════════════════════════════════════════════
	// MODULE A — CSS SURGEON
	// ═══════════════════════════════════════════════════════════════════
	const SURGEON_ID = "arena-perf-surgeon";

	function buildSurgeonCSS() {
		let css = `/* ── Arena Perf: Base ── */\n`;

		// INSTANT VIRTUALIZATION: Hide messages via CSS before JS even runs
		// We add +1 to account for the .h-0 spacer at index 0
		const hideThreshold = config.visibleMessages + 1;
		css += `
            ol.flex-col-reverse > div:nth-child(n+${hideThreshold + 1}),
            ol.flex-col-reverse > li:nth-child(n+${hideThreshold + 1}) {
                display: none !important;
            }
        `;

		if (config.killTransitions) {
			css += `
                /* Replace global transition:all with safe, cheap subset */
                *, *::before, *::after {
                    transition-property: color, background-color, border-color, opacity, transform !important;
                    transition-duration: 150ms !important;
                    animation-duration: 0s !important;
                }
                /* Kill React max-height layout thrashing */
                [class*="transition-[max-height]"] {
                    transition: none !important;
                }
                /* Kill scroll-behavior jank */[class*="overflow-"], [class*="scroller"], [data-radix-scroll-area-viewport] {
                    scroll-behavior: auto !important;
                }
            `;
		}

		if (config.killBlurs) {
			css += `
                /* Blurs destroy GPU composition on huge DOMs */[class*="backdrop-blur"], [style*="backdrop-filter"] {
                    backdrop-filter: none !important;
                    -webkit-backdrop-filter: none !important;
                }
                .bg-white\\/50 { background-color: rgba(255,255,255,0.95) !important; }
                .bg-black\\/50 { background-color: rgba(0,0,0,0.95) !important; }
            `;
		}

		if (config.addContainment) {
			css += `
                /* CSS Containment fences off layout calculations */
                [data-radix-scroll-area-viewport] { 
                    contain: layout paint; 
                }
                ol.flex-col-reverse > div {
                    contain: content;
                }
                /* Isolate Markdown and Code elements */
                pre, code,[class*="prose"] {
                    contain: content;
                }
            `;
		}

		return css;
	}

	function applySurgeryCSS() {
		let el = document.getElementById(SURGEON_ID);
		if (!el) {
			el = document.createElement("style");
			el.id = SURGEON_ID;
			(document.head || document.documentElement).appendChild(el);
		}
		el.textContent = buildSurgeonCSS();
	}

	// ═══════════════════════════════════════════════════════════════════
	// MODULE B — DOM VIRTUALIZER & FLATTENER
	// ═══════════════════════════════════════════════════════════════════

	// Arena uses `flex-col-reverse` for the chat container.
	// This means DOM index [0] is the NEWEST message (visually at bottom),
	// and DOM index[length - 1] is the OLDEST message (visually at top).
	function getMessageContainer() {
		return document.querySelector("ol.flex-col-reverse");
	}

	function getMessages(container) {
		if (!container) return [];
		return Array.from(container.children).filter((el) => {
			const isMsg = el.tagName === "DIV" || el.tagName === "LI";
			const isNotSpacer = !el.classList.contains("h-0");
			return isMsg && isNotSpacer;
		});
	}

	// Strips out Shiki <span> tags and leaves pure raw text.
	// This stops React from reconciling 25,000+ nodes on every app state change.
	function flattenShikiBlocks(msgNode) {
		if (!config.flattenCodeblocks) return 0;
		let flattened = 0;

		const codes = msgNode.querySelectorAll(
			'pre.shiki code:not([data-perf-flat="1"])',
		);
		codes.forEach((code) => {
			if (code.children.length > 0) {
				// Extract text line by line to preserve line breaks, preventing user messages from being mashed together
				const lines = code.querySelectorAll(".line");
				let rawText = "";
				if (lines.length > 0) {
					rawText = Array.from(lines)
						.map((l) => l.textContent)
						.join("\n");
				} else {
					rawText = code.textContent;
				}

				code.innerHTML = "";
				code.textContent = rawText;
				code.dataset.perfFlat = "1";
				flattened++;
			}
		});
		return flattened;
	}

	function cleanMessages() { 
		if (isPaused || !config.autoClean) return; 
 
		if (observer) observer.disconnect();

		const container = getMessageContainer();
		if (!container) return;

		const msgs = getMessages(container);
		stats.totalMsgs = msgs.length;

		if (msgs.length <= config.visibleMessages) {
			msgs.forEach((m) => {
				m.style.display = "";
			});
			stats.hiddenMsgs = 0;
			updateToolbar();
			return;
		}

		let hidden = 0;
		let newFlattened = 0;

		// Because it's flex-col-reverse, indices 0 to (visibleMessages-1) are the newest.
		// Everything after that is old and should be virtualized/hidden.
		msgs.forEach((msg, index) => {
			if (index < config.visibleMessages) {
				// Keep visible
				if (msg.style.display === "none") {
					msg.style.display = "";
				}
			} else {
				// Flatten and Hide
				newFlattened += flattenShikiBlocks(msg);

				if (msg.style.display !== "none") {
					msg.style.display = "none";
				}
				hidden++;
			}
		});

		stats.hiddenMsgs = hidden;
		stats.nodesFlattened += newFlattened; 

		startObserver();

		if (newFlattened > 0) {
			console.log( 
				`[ArenaPerf] Flattened ${newFlattened} codeblocks in hidden messages.`, 
			); 
		}
		updateToolbar(); 
	}

	function showAllMessages() {
		const container = getMessageContainer();
		if (!container) return;

		getMessages(container).forEach((msg) => {
			if (msg.style.display === "none") {
				msg.style.display = "";
			}
		});

		stats.hiddenMsgs = 0;
		updateToolbar();
	}

	// ═══════════════════════════════════════════════════════════════════
	// MODULE C — TOOLBAR UI
	// ═══════════════════════════════════════════════════════════════════
	const TOOLBAR_ID = "arena-perf-toolbar";
	const POPOVER_ID = "arena-perf-settings";

	let toolbarEl, statsEl, toggleBtn;

	function createToolbar() {
		if (!config.showToolbar || document.getElementById(TOOLBAR_ID)) return;

		GM_addStyle(`
            #${TOOLBAR_ID} {
                position: fixed; bottom: 0; left: 50%; transform: translateX(-50%);
                z-index: 2147483647; background: #09090b; color: #4ade80;
                padding: 0 4px; border-radius: 6px 6px 0 0;
                font: 11px/1.4 system-ui, sans-serif; display: flex; align-items: center; gap: 2px;
                border: 1px solid #27272a; border-bottom: none; box-shadow: 0 -2px 10px rgba(0,0,0,0.5);
                user-select: none;
            }
            #${TOOLBAR_ID} button {
                all: unset; cursor: pointer; padding: 2px 8px; border-radius: 4px;
                color: #e4e4e7; white-space: nowrap; transition: background 0.1s;
            }
            #${TOOLBAR_ID} button:hover { background: rgba(255,255,255,0.1); }
            #${TOOLBAR_ID} .perf-stats { padding: 3px 6px; pointer-events: none; }

            #${POPOVER_ID} {
                position: fixed; bottom: 28px; left: 50%; transform: translateX(-50%);
                z-index: 2147483647; background: #18181b; color: #e4e4e7;
                border-radius: 10px; padding: 14px 18px; font: 13px/1.5 system-ui, sans-serif;
                box-shadow: 0 -4px 24px rgba(0,0,0,0.6); border: 1px solid #27272a;
                display: flex; flex-direction: column; gap: 8px; min-width: 260px;
            }
            #${POPOVER_ID} .perf-row { 
                display: flex; align-items: center; justify-content: space-between; gap: 12px;
                padding: 6px 8px; border-radius: 6px; cursor: pointer;
            }
            #${POPOVER_ID} .perf-row:hover { background: rgba(255,255,255,0.05); }
            #${POPOVER_ID} .perf-on { color: #4ade80; font-weight: bold; }
            #${POPOVER_ID} .perf-off { color: #a1a1aa; font-weight: bold; }
            #${POPOVER_ID} hr { border: 0; border-top: 1px solid #27272a; margin: 4px 0; }
        `);

		toolbarEl = document.createElement("div");
		toolbarEl.id = TOOLBAR_ID;

		toggleBtn = document.createElement("button");
		toggleBtn.addEventListener("click", () => {
			isPaused = !isPaused;
			if (isPaused) showAllMessages();
			else cleanMessages();
			updateToolbar();
		});

		statsEl = document.createElement("span");
		statsEl.className = "perf-stats";

		const forceBtn = document.createElement("button");
		forceBtn.textContent = "🧹";
		forceBtn.title = "Force Clean Now";
		forceBtn.addEventListener("click", () => {
			isPaused = false;
			cleanMessages();
		});

		const settingsBtn = document.createElement("button");
		settingsBtn.textContent = "⚙️";
		settingsBtn.addEventListener("click", toggleSettingsPopover);

		toolbarEl.appendChild(toggleBtn);
		toolbarEl.appendChild(statsEl);
		toolbarEl.appendChild(forceBtn);
		toolbarEl.appendChild(settingsBtn);

		document.body.appendChild(toolbarEl);
		updateToolbar();

		// Start watchdog to prevent React from permanently removing the toolbar
		setInterval(() => {
			if (toolbarEl && !document.body.contains(toolbarEl)) {
				console.log("[ArenaPerf] Watchdog: Re-attaching toolbar");
				document.body.appendChild(toolbarEl);
			}
		}, 2000);
	}

	function updateToolbar() {
		if (!statsEl) return;

		toggleBtn.textContent = isPaused ? "👁️ All" : "🔒 Hide";
		toggleBtn.style.color = isPaused ? "#fcd34d" : "#e4e4e7";

		const statText = isPaused
			? `${stats.totalMsgs} msgs · paused`
			: `${stats.totalMsgs - stats.hiddenMsgs}/${stats.totalMsgs} msgs`;

		statsEl.textContent = statText;
		statsEl.style.color = isPaused
			? "#fcd34d"
			: stats.hiddenMsgs > 0
				? "#4ade80"
				: "#a1a1aa";
	}

	function toggleSettingsPopover() {
		if (_settingsPopover) {
			_settingsPopover.remove();
			_settingsPopover = null;
			return;
		}

		const panel = document.createElement("div");
		panel.id = POPOVER_ID;

		function addToggle(icon, label, getValue, onToggle) {
			const row = document.createElement("div");
			row.className = "perf-row";
			const render = () => {
				const on = getValue();
				row.innerHTML = `<span>${icon} ${label}</span><span class="${on ? "perf-on" : "perf-off"}">${on ? "ON" : "OFF"}</span>`;
			};
			render();
			row.addEventListener("click", () => {
				onToggle();
				render();
				saveConfig(config);
			});
			panel.appendChild(row);
		}

		const head = document.createElement("div");
		head.style.cssText =
			"font-weight:bold; font-size:14px; margin-bottom:4px; text-align:center;";
		head.textContent = "DOM Optimizer Settings";
		panel.appendChild(head);

		addToggle(
			"⚡",
			"Kill Transitions",
			() => config.killTransitions,
			() => {
				config.killTransitions = !config.killTransitions;
				applySurgeryCSS();
			},
		);

		addToggle(
			"🌫️",
			"Kill GPU Blurs",
			() => config.killBlurs,
			() => {
				config.killBlurs = !config.killBlurs;
				applySurgeryCSS();
			},
		);

		addToggle(
			"🔥",
			"Flatten Shiki Spans",
			() => config.flattenCodeblocks,
			() => {
				config.flattenCodeblocks = !config.flattenCodeblocks;
				if (config.flattenCodeblocks) cleanMessages();
			},
		);

		addToggle(
			"📦",
			"CSS Containment",
			() => config.addContainment,
			() => {
				config.addContainment = !config.addContainment;
				applySurgeryCSS();
			},
		);

		panel.appendChild(document.createElement("hr"));

		const numRow = document.createElement("div");
		numRow.className = "perf-row";
		numRow.innerHTML = `<span>📝 Visible Messages</span><span style="color:#60a5fa;font-weight:bold">${config.visibleMessages}</span>`;
		numRow.addEventListener("click", () => {
			const val = prompt(
				"Number of recent messages to keep visible?",
				config.visibleMessages,
			);
			const num = parseInt(val, 10);
			if (num && num > 0) {
				config.visibleMessages = num;
				saveConfig(config);
				cleanMessages();
				panel.remove();
				_settingsPopover = null;
			}
		});
		panel.appendChild(numRow);

		document.body.appendChild(panel);
		_settingsPopover = panel;

		// Auto-close on click outside
		setTimeout(() => {
			const closeHandler = (e) => {
				if (!panel.contains(e.target) && !toolbarEl.contains(e.target)) {
					panel.remove();
					_settingsPopover = null;
					document.removeEventListener("mousedown", closeHandler);
				}
			};
			document.addEventListener("mousedown", closeHandler);
		}, 0);
	}

	// ═══════════════════════════════════════════════════════════════════
	// MODULE D — OBSERVER & ORCHESTRATOR
	// ═══════════════════════════════════════════════════════════════════

	function scheduleClean() {
		if (cleanTimeout) clearTimeout(cleanTimeout);
		cleanTimeout = setTimeout(cleanMessages, config.autoCleanDelay);
	}

	function startObserver() {
		if (observer) observer.disconnect();

		observer = new MutationObserver((mutations) => {
			let shouldClean = false;
			for (const m of mutations) {
				// If a new message is added to the list
				if (m.type === "childList" && m.addedNodes.length > 0) {
					shouldClean = true;
					break;
				}
			}
			if (shouldClean) scheduleClean();
		});

		// Observe the body to handle SPA navigations where the ol container swaps
		observer.observe(document.body, { childList: true, subtree: true });
	}

	function initializeUI() {
		if (document.visibilityState !== "visible") {
			pendingStart = true;
			console.log("[ArenaPerf] Deferring init until tab is visible");
			return;
		}

		if (hasStarted) return;
		hasStarted = true;

		console.log("[ArenaPerf] Booting DOM Virtualizer & Flattener...");
		applySurgeryCSS();
		if (config.showToolbar) createToolbar();
		startObserver();

		// Initial passes
		setTimeout(cleanMessages, 1000);
		setTimeout(cleanMessages, 3000);
	}

	function scheduleInit() {
		if (document.visibilityState !== "visible") {
			pendingStart = true;
			return;
		}

		if (window.requestIdleCallback) {
			const idleId = window.requestIdleCallback(initializeUI, {
				timeout: 1000,
			});
			setTimeout(() => {
				if (!hasStarted) {
					window.cancelIdleCallback(idleId);
					initializeUI();
				}
			}, 1500);
		} else {
			setTimeout(initializeUI, 100);
		}
	}

	function waitForReady() {
		const READY_SELECTOR = "form textarea";

		if (document.querySelector(READY_SELECTOR)) {
			scheduleInit();
			return;
		}

		const readyObserver = new MutationObserver(() => {
			if (document.querySelector(READY_SELECTOR)) {
				readyObserver.disconnect();
				setTimeout(scheduleInit, 200);
			}
		});

		readyObserver.observe(document.body, { childList: true, subtree: true });
	}

	document.addEventListener("visibilitychange", () => {
		if (document.visibilityState !== "visible") return;

		if (pendingStart && !hasStarted) {
			pendingStart = false;
			setTimeout(initializeUI, 250);
		}
	});

	setTimeout(() => {
		if (document.visibilityState !== "visible") {
			pendingStart = true;
			return;
		}
		if (!hasStarted && document.querySelector("form textarea")) {
			console.warn("[ArenaPerf] Emergency init after 5s");
			initializeUI();
		}
	}, 5000);

	// Ultra-fast Body Detection (Faster than DOMContentLoaded)
	if (document.body) {
		waitForReady();
	} else {
		const bodyObserver = new MutationObserver(() => {
			if (document.body) {
				bodyObserver.disconnect();
				waitForReady();
			}
		});
		bodyObserver.observe(document.documentElement, { childList: true });
	}
})();