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 για να εγκαταστήσετε αυτόν τον κώδικα.

θα χρειαστεί να εγκαταστήσετε μια επέκταση όπως το Tampermonkey ή το Userscripts για να εγκαταστήσετε αυτόν τον κώδικα.

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

Θα χρειαστεί να εγκαταστήσετε μια επέκταση διαχείρισης κώδικα χρήστη για να εγκαταστήσετε αυτόν τον κώδικα.

(Έχω ήδη έναν διαχειριστή κώδικα χρήστη, επιτρέψτε μου να τον εγκαταστήσω!)

Advertisement:

You will need to install an extension such as Stylus to install this style.

You will need to install an extension such as Stylus to install this style.

You will need to install an extension such as Stylus to install this style.

You will need to install a user style manager extension to install this style.

You will need to install a user style manager extension to install this style.

You will need to install a user style manager extension to install this style.

(Έχω ήδη έναν διαχειριστή στυλ χρήστη, επιτρέψτε μου να τον εγκαταστήσω!)

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();
})();