Google AI Studio - Ultimate Optimizer UI

Collapse code + Infinite Restore (Bug fixed) + Persistent Config + Reduce lag

ही स्क्रिप्ट इंस्टॉल करण्यासाठी तुम्हाला Tampermonkey, Greasemonkey किंवा Violentmonkey यासारखे एक्स्टेंशन इंस्टॉल करावे लागेल.

You will need to install an extension such as Tampermonkey to install this script.

ही स्क्रिप्ट इंस्टॉल करण्यासाठी तुम्हाला Tampermonkey किंवा Violentmonkey यासारखे एक्स्टेंशन इंस्टॉल करावे लागेल..

You will need to install an extension such as Tampermonkey or Userscripts to install this script.

ही स्क्रिप्ट इंस्टॉल करण्यासाठी तुम्हाला Tampermonkey यासारखे एक्स्टेंशन इंस्टॉल करावे लागेल..

ही स्क्रिप्ट इंस्टॉल करण्यासाठी तुम्हाला एक युझर स्क्रिप्ट व्यवस्थापक एक्स्टेंशन इंस्टॉल करावे लागेल.

(माझ्याकडे आधीच युझर स्क्रिप्ट व्यवस्थापक आहे, मला इंस्टॉल करू द्या!)

Advertisement:

ही स्टाईल इंस्टॉल करण्यासाठी तुम्हाला Stylus सारखे एक्स्टेंशन इंस्टॉल करावे लागेल.

ही स्टाईल इंस्टॉल करण्यासाठी तुम्हाला Stylus सारखे एक्स्टेंशन इंस्टॉल करावे लागेल.

ही स्टाईल इंस्टॉल करण्यासाठी तुम्हाला Stylus सारखे एक्स्टेंशन इंस्टॉल करावे लागेल.

ही स्टाईल इंस्टॉल करण्यासाठी तुम्हाला एक युझर स्टाईल व्यवस्थापक इंस्टॉल करावे लागेल.

ही स्टाईल इंस्टॉल करण्यासाठी तुम्हाला एक युझर स्टाईल व्यवस्थापक इंस्टॉल करावे लागेल.

ही स्टाईल इंस्टॉल करण्यासाठी तुम्हाला एक युझर स्टाईल व्यवस्थापक इंस्टॉल करावे लागेल.

(माझ्याकडे आधीच युझर स्टाईल व्यवस्थापक आहे, मला इंस्टॉल करू द्या!)

Advertisement:

// ==UserScript==
// @name         Google AI Studio - Ultimate Optimizer UI
// @namespace    http://tampermonkey.net/
// @version      5.3
// @description  Collapse code + Infinite Restore (Bug fixed) + Persistent Config + Reduce lag
// @match        https://aistudio.google.com/*
// @author       Kfayyy
// @license      MIT
// @icon         https://aistudio.google.com/favicon.ico
// @grant        none
// ==/UserScript==

(function() {
    'use strict';

    // ==============================
    // ⚙️ CONFIGURATION PERSISTANTE
    // ==============================
    const CONFIG_KEY = 'ai_studio_optimizer_config';

    let BASE_MAX_VISIBLE = 35;
    let MAX_VISIBLE = 35;
    let MAX_CACHE = 50;
    let RESTORE_COUNT = 10;
    let enabled = true;

    function loadConfig() {
        try {
            const saved = localStorage.getItem(CONFIG_KEY);
            if (saved) {
                const parsed = JSON.parse(saved);
                BASE_MAX_VISIBLE = parsed.BASE_MAX_VISIBLE ?? 35;
                MAX_CACHE = parsed.MAX_CACHE ?? 50;
                RESTORE_COUNT = parsed.RESTORE_COUNT ?? 10;
                enabled = parsed.enabled ?? true;
            }
        } catch (e) { console.error("[Optimizer] Erreur config", e); }
        MAX_VISIBLE = BASE_MAX_VISIBLE;
    }

    function saveConfig() {
        try {
            localStorage.setItem(CONFIG_KEY, JSON.stringify({
                BASE_MAX_VISIBLE, MAX_CACHE, RESTORE_COUNT, enabled
            }));
        } catch (e) {}
    }

    loadConfig();

    // ==============================
    // 🧠 GESTION MÉMOIRE & DOM
    // ==============================

    let hiddenMessages = [];
    let detachedMessages = [];
    let chatContainerRef = null;

    function debounce(func, wait) {
        let timeout;
        return function(...args) {
            clearTimeout(timeout);
            timeout = setTimeout(() => func.apply(this, args), wait);
        };
    }

    function getMessages() {
        return Array.from(document.querySelectorAll('ms-chat-turn'));
    }

    function updateRestoreBtnLabel() {
        if (typeof restoreBtn !== 'undefined' && restoreBtn) {
            const total = hiddenMessages.length + detachedMessages.length;
            restoreBtn.innerText = `⬆ Restore (${total} dispo)`;
        }
    }

    function optimizeDOM() {
        if (!enabled) return;

        const messages = getMessages();

        if (messages.length <= MAX_VISIBLE) {
            messages.forEach(el => {
                if (el.style.display === 'none') el.style.display = '';
            });
            hiddenMessages = [];
            updateRestoreBtnLabel();
            return;
        }

        const toHide = messages.length - MAX_VISIBLE;
        hiddenMessages = [];

        messages.forEach((el, index) => {
            if (index < toHide) {
                if (el.style.display !== 'none') el.style.display = 'none';
                hiddenMessages.push(el);
            } else {
                if (el.style.display === 'none') el.style.display = '';
            }
        });

        if (hiddenMessages.length > MAX_CACHE) {
            const excess = hiddenMessages.length - MAX_CACHE;
            const toPurge = hiddenMessages.slice(0, excess);

            toPurge.forEach(el => {
                if (!chatContainerRef && el.parentNode) chatContainerRef = el.parentNode;
                el.remove();
                detachedMessages.push(el);
            });

            hiddenMessages = hiddenMessages.slice(excess);
        }
        updateRestoreBtnLabel();
    }

    const debouncedOptimizeDOM = debounce(optimizeDOM, 200);

    function restoreMessages() {
        const totalAvailable = hiddenMessages.length + detachedMessages.length;
        if (totalAvailable === 0) {
            alert("Tout l'historique disponible est déjà affiché !");
            return;
        }

        const count = Math.min(RESTORE_COUNT, totalAvailable);
        MAX_VISIBLE += count;

        const currentDomCount = getMessages().length;
        const neededFromDetached = MAX_VISIBLE - currentDomCount;

        if (neededFromDetached > 0 && detachedMessages.length > 0) {
            const actualToReattach = Math.min(neededFromDetached, detachedMessages.length);
            const toReattach = detachedMessages.splice(-actualToReattach);
            const referenceNode = getMessages()[0];

            if (referenceNode && referenceNode.parentNode) {
                toReattach.forEach(el => referenceNode.parentNode.insertBefore(el, referenceNode));
            } else if (chatContainerRef) {
                toReattach.forEach(el => chatContainerRef.appendChild(el));
            }
        }

        optimizeDOM();
    }

    function cleanAgain() {
        MAX_VISIBLE = BASE_MAX_VISIBLE;
        optimizeDOM();
    }

    // ==============================
    // 👀 OBSERVERS & COLLAPSE
    // ==============================

    const observer = new MutationObserver((mutations) => {
        let shouldOptimize = false;
        for (let m of mutations) {
            if (m.addedNodes.length > 0) { shouldOptimize = true; break; }
        }
        if (shouldOptimize) debouncedOptimizeDOM();
    });

    observer.observe(document.body, { childList: true, subtree: true });
    setTimeout(optimizeDOM, 2000);

    let autoMode = true;
    function collapseAll() {
        const openBtns = document.querySelectorAll('ms-code-block button[data-test-id="expand-icon-button"] span');
        let c = 0;
        openBtns.forEach(span => {
            if (span.textContent.trim() === 'expand_less') {
                const b = span.closest('button');
                if (b) { b.click(); c++; }
            }
        });
        return c;
    }
    function stopAutoMode() { autoMode = false; collapseObserver.disconnect(); }

    const collapseObserver = new MutationObserver(() => {
        if (!autoMode) return;
        if (collapseAll() === 0) stopAutoMode();
    });
    collapseObserver.observe(document.body, { childList: true, subtree: true });
    setTimeout(() => { if (collapseAll() === 0) stopAutoMode(); }, 1200);

    // ==============================
    // 🎨 UI & PANEL (REFONTE)
    // ==============================

    const btnContainer = document.createElement('div');
    Object.assign(btnContainer.style, {
        position: 'fixed', bottom: '25px', left: '50%', transform: 'translateX(-50%)',
        display: 'flex', gap: '12px', zIndex: 99999
    });

    const baseBtnStyle = {
        padding: '10px 18px',
        background: '#333', color: '#fff', border: 'none', borderRadius: '8px',
        cursor: 'pointer', boxShadow: '0 4px 12px rgba(0,0,0,0.3)',
        fontSize: '15px',
        display: 'flex', alignItems: 'center', justifyContent: 'center',
        transition: 'background 0.2s, transform 0.1s'
    };

    const collapseBtn = document.createElement('button');
    collapseBtn.innerText = '▼';
    collapseBtn.title = "Fermer les codes";
    Object.assign(collapseBtn.style, baseBtnStyle);

    collapseBtn.onclick = () => { autoMode = false; collapseAll(); };

    const optimizerBtn = document.createElement('button');
    optimizerBtn.innerText = '\u00A0🚀\u00A0';
    Object.assign(optimizerBtn.style, baseBtnStyle);

    [collapseBtn, optimizerBtn].forEach(btn => {
        btn.onmouseover = () => btn.style.background = '#444';
        btn.onmouseout = () => btn.style.background = '#333';
        btn.onmousedown = () => btn.style.transform = 'scale(0.95)';
        btn.onmouseup = () => btn.style.transform = 'scale(1)';
    });

    btnContainer.appendChild(collapseBtn);
    btnContainer.appendChild(optimizerBtn);

    const panel = document.createElement('div');
    Object.assign(panel.style, {
        position: 'fixed', bottom: '80px', left: '50%', transform: 'translateX(-50%)', zIndex: 99999,
        background: '#2b2b2b', padding: '16px', borderRadius: '12px', color: '#fff', fontSize: '13px',
        display: 'none', flexDirection: 'column', gap: '10px', minWidth: '240px', boxShadow: '0 8px 24px rgba(0,0,0,0.6)'
    });

    function createRow(text, el) {
        const r = document.createElement('div'); r.style.display = 'flex'; r.style.justifyContent = 'space-between'; r.style.alignItems = 'center';
        const l = document.createElement('span'); l.innerText = text; r.appendChild(l); r.appendChild(el); return r;
    }

    const inputStyle = { width: '60px', padding: '4px', borderRadius: '4px', border: '1px solid #555', background: '#1e1e1e', color: '#fff', textAlign: 'center' };

    const maxInput = document.createElement('input'); maxInput.type = 'number'; maxInput.value = BASE_MAX_VISIBLE; Object.assign(maxInput.style, inputStyle);
    maxInput.onchange = () => { BASE_MAX_VISIBLE = parseInt(maxInput.value) || 35; MAX_VISIBLE = BASE_MAX_VISIBLE; saveConfig(); debouncedOptimizeDOM(); };

    const restoreInput = document.createElement('input'); restoreInput.type = 'number'; restoreInput.value = RESTORE_COUNT; Object.assign(restoreInput.style, inputStyle);
    restoreInput.onchange = () => { RESTORE_COUNT = parseInt(restoreInput.value) || 10; saveConfig(); };

    const cacheInput = document.createElement('input'); cacheInput.type = 'number'; cacheInput.value = MAX_CACHE; Object.assign(cacheInput.style, inputStyle);
    cacheInput.onchange = () => { MAX_CACHE = parseInt(cacheInput.value) || 50; saveConfig(); debouncedOptimizeDOM(); };

    function makeBtn(txt, act, color = '#3c3c3c') {
        const b = document.createElement('button'); b.innerText = txt;
        Object.assign(b.style, { background: color, color: '#fff', border: 'none', padding: '8px', borderRadius: '6px', cursor: 'pointer', width: '100%', marginTop: '4px', fontWeight: 'bold' });
        b.onclick = act;
        b.onmouseover = () => b.style.opacity = '0.8';
        b.onmouseout = () => b.style.opacity = '1';
        return b;
    }

    let restoreBtn;

    const toggleBtn = makeBtn(enabled ? '⚡ Optimizer ON' : '⛔ Optimizer OFF', () => {
        enabled = !enabled;
        toggleBtn.innerText = enabled ? '⚡ Optimizer ON' : '⛔ Optimizer OFF';
        toggleBtn.style.background = enabled ? '#3c3c3c' : '#8b0000';
        saveConfig();

        if (enabled) {
            debouncedOptimizeDOM();
        } else {
            // RESTAURATION COMPLÈTE (comme si le script n'existait pas)
            const currentMessages = getMessages();
            const referenceNode = currentMessages.length > 0 ? currentMessages[0] : null;
            const container = referenceNode ? referenceNode.parentNode : chatContainerRef;

            // 1. Réinsérer les éléments retirés du DOM à leur bonne place et dans le bon ordre
            if (detachedMessages.length > 0 && container) {
                detachedMessages.forEach(el => {
                    if (referenceNode) {
                        container.insertBefore(el, referenceNode); // Insère avant le 1er visible actuel
                    } else {
                        container.appendChild(el);
                    }
                });
                detachedMessages = [];
            }

            // 2. Réafficher tous les messages qui étaient juste masqués
            getMessages().forEach(el => {
                if (el.style.display === 'none') el.style.display = '';
            });

            // 3. Réinitialiser la mémoire
            hiddenMessages = [];
            updateRestoreBtnLabel();
        }
    }, enabled ? '#3c3c3c' : '#8b0000');

    restoreBtn = makeBtn('⬆ Restore', restoreMessages, '#0056b3');
    const cleanBtn = makeBtn('⬇ Clean', cleanAgain, '#d35400');

    panel.appendChild(createRow('Max visible', maxInput));
    panel.appendChild(createRow('Restore count', restoreInput));
    panel.appendChild(createRow('Cache size', cacheInput));

    const divider = document.createElement('hr');
    divider.style.borderColor = '#444'; divider.style.margin = '4px 0';
    panel.appendChild(divider);

    panel.appendChild(toggleBtn); panel.appendChild(restoreBtn); panel.appendChild(cleanBtn);

    optimizerBtn.onclick = () => panel.style.display = panel.style.display === 'none' ? 'flex' : 'none';
    document.addEventListener('click', (e) => { if (!panel.contains(e.target) && !btnContainer.contains(e.target)) panel.style.display = 'none'; });

    function waitForBody() {
        if (!document.body) { requestAnimationFrame(waitForBody); return; }
        document.body.appendChild(btnContainer);
        document.body.appendChild(panel);
    }
    waitForBody();
})();