Skip to content

使用方法,粘贴到控制台确定,之后点击任意对话标题自动删除! 小心操作!!

// ==UserScript==
// @name         Gemini 极速删除助手 (安全版+AltQ开关)
// @namespace    http://tampermonkey.net/
// @version      5.0
// @description  绕过 TrustedHTML 限制,按 Alt+Q 显示删除图标,点击直接删除(无确认)。
// @author       Frank Salvio & Gemini
// @match        https://gemini.google.com/*
// @grant        GM_setValue
// @grant        GM_getValue
// @grant        GM_registerMenuCommand
// ==/UserScript==

(function() {
    'use strict';

    const STORAGE_KEY = 'gemini_quick_del_mode';
    let isEnabled = GM_getValue(STORAGE_KEY, false);

    // --- 1. 安全创建 SVG 图标 (绕过 innerHTML) ---
    function createTrashIcon() {
        const svg = document.createElementNS("http://www.w3.org/2000/svg", "svg");
        svg.setAttribute("viewBox", "0 -960 960 960");
        svg.setAttribute("height", "20px");
        svg.setAttribute("width", "20px");
        svg.setAttribute("fill", "currentColor");

        const path = document.createElementNS("http://www.w3.org/2000/svg", "path");
        path.setAttribute("d", "M280-120q-33 0-56.5-23.5T200-200v-520h-40v-80h200v-40h240v40h200v80h-40v520q0 33-23.5 56.5T680-120H280Zm400-600H280v520h400v-520ZM360-280h80v-360h-80v360Zm160 0h80v-360h-80v360ZM280-720v520-520Z");

        svg.appendChild(path);
        return svg;
    }

    // --- 2. 注入 CSS (使用 textContent 安全注入) ---
    const style = document.createElement('style');
    style.textContent = `
        /* 仅在激活模式下,强制显示操作容器 */
        body.del-mode-active .conversation-actions-container {
            display: flex !important;
            opacity: 1 !important;
            visibility: visible !important;
            padding-right: 4px;
        }

        /* 默认隐藏删除按钮 */
        .safe-del-btn {
            display: none;
            align-items: center;
            justify-content: center;
            width: 28px;
            height: 28px;
            cursor: pointer;
            border-radius: 50%;
            margin-right: 2px;
            color: #ea4335;
            background-color: rgba(234, 67, 53, 0.05);
            transition: all 0.2s;
        }

        /* 激活模式下显示按钮 */
        body.del-mode-active .safe-del-btn {
            display: flex !important;
        }

        .safe-del-btn:hover {
            background-color: #ea4335;
            color: white !important;
            box-shadow: 0 1px 3px rgba(0,0,0,0.2);
        }
    `;
    document.head.appendChild(style);

    // --- 3. 提示框 Toast (DOM 创建) ---
    function showToast(text, active) {
        const existing = document.getElementById('g-del-toast');
        if (existing) existing.remove();

        const toast = document.createElement('div');
        toast.id = 'g-del-toast';
        toast.textContent = text;
        Object.assign(toast.style, {
            position: 'fixed',
            bottom: '30px',
            left: '50%',
            transform: 'translateX(-50%)',
            background: active ? '#188038' : '#5f6368',
            color: 'white',
            padding: '8px 16px',
            borderRadius: '24px',
            fontSize: '14px',
            zIndex: '999999',
            pointerEvents: 'none',
            opacity: '0',
            transition: 'opacity 0.3s'
        });

        document.body.appendChild(toast);
        requestAnimationFrame(() => toast.style.opacity = '1');
        setTimeout(() => {
            toast.style.opacity = '0';
            setTimeout(() => toast.remove(), 300);
        }, 2000);
    }

    // --- 4. 模式切换 ---
    function applyMode() {
        if (isEnabled) {
            document.body.classList.add('del-mode-active');
        } else {
            document.body.classList.remove('del-mode-active');
        }
    }

    function toggleMode() {
        isEnabled = !isEnabled;
        GM_setValue(STORAGE_KEY, isEnabled);
        applyMode();
        showToast(isEnabled ? "⚡️ 极速删除模式:ON" : "💤 极速删除模式:OFF", isEnabled);
    }

    // --- 5. 删除逻辑 (无确认弹窗) ---
    function executeDelete(container) {
        const menuBtn = container.querySelector('button[data-test-id="actions-menu-button"]');
        if (!menuBtn) return;

        // 视觉反馈
        const btn = container.querySelector('.safe-del-btn');
        if(btn) btn.style.opacity = '0.5';

        menuBtn.click(); // 打开菜单

        // 轮询点击删除
        const waiter = setInterval(() => {
            const delOption = document.querySelector('button[data-test-id="delete-button"]');
            if (delOption) {
                clearInterval(waiter);
                delOption.click();

                // 轮询点击确认
                const confirmer = setInterval(() => {
                    const confirmBtn = document.querySelector('button[data-test-id="confirm-button"]');
                    if (confirmBtn) {
                        clearInterval(confirmer);
                        confirmBtn.click();
                        console.log('Gemini 删除助手:已删除');
                    }
                }, 20);
            }
        }, 20);

        // 超时清除
        setTimeout(() => clearInterval(waiter), 2000);
    }

    // --- 6. 注入按钮 (DOM 操作) ---
    function inject() {
        // 查找所有尚未注入的容器
        const containers = document.querySelectorAll('.conversation-actions-container:not(.has-safe-del)');

        containers.forEach(container => {
            // 创建按钮容器
            const btn = document.createElement('div');
            btn.className = 'safe-del-btn';
            btn.title = '直接删除 (无确认)';

            // 插入 SVG 图标
            btn.appendChild(createTrashIcon());

            // 绑定点击事件 (无 confirm)
            btn.addEventListener('click', (e) => {
                e.preventDefault();
                e.stopPropagation();
                executeDelete(container);
            });

            // 插入到 DOM
            const menuBtn = container.querySelector('button[data-test-id="actions-menu-button"]');
            if (menuBtn) {
                container.insertBefore(btn, menuBtn);
                container.classList.add('has-safe-del');
            }
        });
    }

    // --- 7. 初始化与监听 ---
    applyMode();
    inject();

    // 监听 Alt+Q
    window.addEventListener('keydown', (e) => {
        if (e.altKey && e.key.toLowerCase() === 'q') {
            e.preventDefault();
            toggleMode();
        }
    });

    // 观察页面变化 (动态加载)
    const observer = new MutationObserver(inject);
    observer.observe(document.body, { childList: true, subtree: true });

    // 菜单命令
    GM_registerMenuCommand("切换删除模式 (Alt+Q)", toggleMode);
})();

新版

// ==UserScript==
// @name         Gemini 效率增强助手 (阻止滚动 + 极速删除 + 划词引用 + 快速跳转)
// @namespace    http://tampermonkey.net/
// @version      8.5
// @description  整合版:1. 阻止自动滚动;2. 极速删除图标(Alt+Q);3. 划词悬浮引用;4. 添加面板折叠开关;5. 修复特定容器的顶部/底部快速跳转。(v8.5 解决双通道点击冒泡冲突,完美修复极速删除流)
// @author       Gemini & Frank Salvio
// @match        https://gemini.google.com/*
// @icon         https://www.google.com/s2/favicons?sz=64&domain=gemini.google.com
// @grant        none
// ==/UserScript==

(function() {
    'use strict';

    console.log('%c[Gemini Helper] 脚本已启动 (v8.5)...', 'color: #1a73e8; font-weight: bold;');

    // --- 状态管理 (从本地存储读取) ---
    let preventScroll = localStorage.getItem('gemini_prevent_scroll') === 'true';
    let delModeActive = localStorage.getItem('gemini_del_mode') === 'true';
    let quoteModeActive = localStorage.getItem('gemini_quote_mode') === 'true';
    let uiVisible = localStorage.getItem('gemini_ui_visible') !== 'false';

    // --- 1. 阻止自动滚动逻辑 (保留原生方法引用以便手动调用) ---
    const originalWindowScrollTo = window.scrollTo;
    const originalElementScrollTo = Element.prototype.scrollTo;
    const originalScrollIntoView = Element.prototype.scrollIntoView;

    window.scrollTo = function(...args) { if (!preventScroll) originalWindowScrollTo.apply(this, args); };
    Element.prototype.scrollTo = function(...args) { if (!preventScroll) originalElementScrollTo.apply(this, args); };
    Element.prototype.scrollIntoView = function(...args) { if (!preventScroll) originalScrollIntoView.apply(this, args); };

    const scrollTopDescriptor = Object.getOwnPropertyDescriptor(Element.prototype, 'scrollTop');
    if (scrollTopDescriptor) {
        Object.defineProperty(Element.prototype, 'scrollTop', {
            get: function() { return scrollTopDescriptor.get.call(this); },
            set: function(val) { if (!preventScroll) scrollTopDescriptor.set.call(this, val); }
        });
    }

    // --- 2. 极速删除逻辑 ---
    function createTrashIcon() {
        const svg = document.createElementNS("http://www.w3.org/2000/svg", "svg");
        svg.setAttribute("viewBox", "0 -960 960 960");
        svg.setAttribute("height", "18px");
        svg.setAttribute("width", "18px");
        svg.setAttribute("fill", "currentColor");
        const path = document.createElementNS("http://www.w3.org/2000/svg", "path");
        path.setAttribute("d", "M280-120q-33 0-56.5-23.5T200-200v-520h-40v-80h200v-40h240v40h200v80h-40v520q0 33-23.5 56.5T680-120H280Zm400-600H280v520h400v-520ZM360-280h80v-360h-80v360Zm160 0h80v-360h-80v360ZM280-720v520-520Z");
        svg.appendChild(path);
        return svg;
    }

    function executeDelete(container) {
        // 1. 定位菜单按钮包裹层 (gem-icon-button)
        const menuWrapper = container.querySelector('[data-test-id="actions-menu-button"]');
        if (!menuWrapper) return;
        
        // 【单点精准冒泡策略】
        // 优先点击最内部的原生 button,让事件自然冒泡到包装器上触发 Angular 监听器。
        // 绝不能同时调用 menuWrapper.click() 和 innerButton.click(),否则会引发双击冲突导致菜单瞬间关闭!
        const realMenuBtn = menuWrapper.querySelector('button') || menuWrapper;
        realMenuBtn.click();
        
        const waiter = setInterval(() => {
            // 2. 定位弹出的“删除”选项按钮 (通常在 cdk-overlay 中)
            const delOption = document.querySelector('[data-test-id="delete-button"]');
            if (delOption) {
                clearInterval(waiter);
                
                // 同样采用单点精准点击
                const realDelBtn = delOption.querySelector('button') || delOption;
                realDelBtn.click();
                
                const confirmer = setInterval(() => {
                    // 3. 定位弹出的“确认删除”确认框按钮 (gem-button)
                    const confirmWrapper = document.querySelector('[data-test-id="confirm-button"]');
                    if (confirmWrapper) {
                        clearInterval(confirmer);
                        
                        // 同样采用单点精准点击
                        const realConfirmBtn = confirmWrapper.querySelector('button') || confirmWrapper;
                        realConfirmBtn.click();
                        
                        console.log('[Gemini Helper] 消息已通过单点精准冒泡方案安全删除');
                    }
                }, 20);
                setTimeout(() => clearInterval(confirmer), 2000);
            }
        }, 20);
        setTimeout(() => clearInterval(waiter), 2000);
    }

    // --- 3. 样式注入 ---
    const style = document.createElement('style');
    style.textContent = `
        /* 删除按钮样式 */
        .safe-del-btn {
            display: none; align-items: center; justify-content: center;
            width: 28px; height: 28px; cursor: pointer; border-radius: 50%;
            margin-right: 6px; color: #ea4335; background: rgba(234, 67, 53, 0.1);
            transition: all 0.2s;
            z-index: 10;
            align-self: center;
            vertical-align: middle;
            flex-shrink: 0; /* 防止图标被弹性盒压缩 */
        }
        body.del-mode-on .safe-del-btn { display: inline-flex !important; }
        
        /* 兼容新旧版本的控制容器:强行应用弹性行布局实现左右并排,并垂直居中对齐 */
        body.del-mode-on .conversation-actions-container,
        body.del-mode-on .hovered-trailing-content {
            display: flex !important;
            flex-direction: row !important;      /* 强行设为水平排列,解决上下并排问题 */
            align-items: center !important;     /* 强行垂直居中对齐 */
            justify-content: flex-end !important;/* 靠右侧对齐 */
            opacity: 1 !important;
            visibility: visible !important;
        }
        .safe-del-btn:hover { background: #ea4335; color: white !important; }

        /* 面板显示/隐藏开关 */
        #gemini-toggle-btn {
            position: fixed; top: 65px; right: 20px; z-index: 10000;
            width: 32px; height: 32px; border-radius: 50%; border: none;
            background: #5f6368; color: white; font-size: 16px; cursor: pointer;
            box-shadow: 0 2px 5px rgba(0,0,0,0.2); transition: all 0.3s;
            display: flex; justify-content: center; align-items: center;
            user-select: none;
        }

        /* 悬浮按钮组容器 */
        #gemini-tools-container {
            position: fixed; top: 105px; right: 20px; z-index: 9999;
            display: flex; flex-direction: column; gap: 8px;
            transition: opacity 0.3s, transform 0.3s; transform-origin: top right;
        }
        #gemini-tools-container.hidden {
            opacity: 0; pointer-events: none; transform: scale(0.9);
        }

        .gemini-tool-btn {
            padding: 6px 12px; border: none; border-radius: 12px;
            color: white; font-size: 12px; font-weight: 500; cursor: pointer;
            box-shadow: 0 2px 5px rgba(0,0,0,0.2); transition: 0.3s;
            font-family: sans-serif; text-align: center; min-width: 110px;
        }

        /* 底部跳转按钮行 */
        .gemini-btn-row {
            display: flex; gap: 4px; width: 100%;
        }
        .gemini-tool-btn-small {
            flex: 1; min-width: 0; padding: 6px 4px; font-size: 11px;
            background-color: #5f6368; border-radius: 8px; color: white;
            border: none; cursor: pointer; box-shadow: 0 2px 5px rgba(0,0,0,0.1);
        }
        .gemini-tool-btn-small:hover { background-color: #444; }

        /* 划词悬浮引用按钮 */
        #gemini-quote-popup {
            position: absolute; display: none; background: #1a73e8; color: white;
            padding: 6px 12px; border-radius: 8px; font-size: 13px; font-weight: bold;
            cursor: pointer; box-shadow: 0 4px 10px rgba(0,0,0,0.2); z-index: 10000;
            user-select: none; transition: background 0.2s; font-family: sans-serif;
        }
    `;
    document.head.appendChild(style);

    // --- 4. 划词引用逻辑 ---
    const quotePopup = document.createElement('div');
    quotePopup.id = 'gemini-quote-popup';
    quotePopup.innerText = '💬 引用';
    document.body.appendChild(quotePopup);

    document.addEventListener('mouseup', (e) => {
        if (!quoteModeActive) return;
        const selectedText = window.getSelection().toString().trim();
        if (selectedText && e.target.id !== 'gemini-quote-popup') {
            quotePopup.style.display = 'block';
            quotePopup.style.left = `${e.pageX + 10}px`;
            quotePopup.style.top = `${e.pageY + 15}px`;
        } else if (e.target.id !== 'gemini-quote-popup') {
            quotePopup.style.display = 'none';
        }
    });

    quotePopup.addEventListener('mousedown', (e) => e.preventDefault());
    quotePopup.addEventListener('click', () => {
        const selectedText = window.getSelection().toString().trim();
        if (!selectedText) return;
        const editor = document.querySelector('rich-textarea div[contenteditable="true"]') || document.querySelector('[contenteditable="true"]');
        if (editor) {
            editor.focus();
            const quoteStr = `\`\`\`\n${selectedText}\n\`\`\`\n\n`;
            document.execCommand('insertText', false, quoteStr);
            editor.dispatchEvent(new Event('input', { bubbles: true }));
            console.log('[Gemini Helper] 已引用文本');
        }
        quotePopup.style.display = 'none';
        window.getSelection().removeAllRanges();
    });

    // --- 5. UI 注入与更新 ---
    function updateUIVisibility() {
        const container = document.getElementById('gemini-tools-container');
        const toggleBtn = document.getElementById('gemini-toggle-btn');
        if (!container || !toggleBtn) return;
        if (uiVisible) {
            container.classList.remove('hidden');
            toggleBtn.style.background = '#1a73e8';
            toggleBtn.title = '隐藏助手面板';
        } else {
            container.classList.add('hidden');
            toggleBtn.style.background = '#5f6368';
            toggleBtn.title = '显示助手面板';
        }
    }

    function updateUI() {
        const sBtn = document.getElementById('btn-scroll-lock');
        const dBtn = document.getElementById('btn-del-mode');
        const qBtn = document.getElementById('btn-quote-mode');

        if (sBtn) {
            sBtn.innerText = preventScroll ? '🚫 滚动已锁定' : '↔️ 自由滚动';
            sBtn.style.backgroundColor = preventScroll ? '#d32f2f' : '#5f6368';
        }
        if (dBtn) {
            dBtn.innerText = delModeActive ? '⚡ 极速删除: ON' : '💤 极速删除: OFF';
            dBtn.style.backgroundColor = delModeActive ? '#188038' : '#5f6368';
        }
        if (qBtn) {
            qBtn.innerText = quoteModeActive ? '💬 划词引用: ON' : '💬 划词引用: OFF';
            qBtn.style.backgroundColor = quoteModeActive ? '#1a73e8' : '#5f6368';
        }
        if (delModeActive) document.body.classList.add('del-mode-on');
        else document.body.classList.remove('del-mode-on');
    }

    function createUI() {
        if (document.getElementById('gemini-helper-wrapper')) return;

        const wrapper = document.createElement('div');
        wrapper.id = 'gemini-helper-wrapper';

        const toggleBtn = document.createElement('button');
        toggleBtn.id = 'gemini-toggle-btn';
        toggleBtn.innerText = '⚙️';
        toggleBtn.onclick = () => {
            uiVisible = !uiVisible;
            localStorage.setItem('gemini_ui_visible', uiVisible);
            updateUIVisibility();
        };

        const container = document.createElement('div');
        container.id = 'gemini-tools-container';

        const sBtn = document.createElement('button');
        sBtn.id = 'btn-scroll-lock';
        sBtn.className = 'gemini-tool-btn';
        sBtn.onclick = () => {
            preventScroll = !preventScroll;
            localStorage.setItem('gemini_prevent_scroll', preventScroll);
            updateUI();
        };

        const dBtn = document.createElement('button');
        dBtn.id = 'btn-del-mode';
        dBtn.className = 'gemini-tool-btn';
        dBtn.onclick = () => {
            delModeActive = !delModeActive;
            localStorage.setItem('gemini_del_mode', delModeActive);
            updateUI();
        };

        const qBtn = document.createElement('button');
        qBtn.id = 'btn-quote-mode';
        qBtn.className = 'gemini-tool-btn';
        qBtn.onclick = () => {
            quoteModeActive = !quoteModeActive;
            localStorage.setItem('gemini_quote_mode', quoteModeActive);
            updateUI();
        };

        const btnRow = document.createElement('div');
        btnRow.className = 'gemini-btn-row';

        const topBtn = document.createElement('button');
        topBtn.className = 'gemini-tool-btn-small';
        topBtn.innerText = '⬆️ 顶部';
        topBtn.onclick = () => {
            const targetContainer = document.querySelector("#chat-history > infinite-scroller") || document.documentElement;
            if (targetContainer === document.documentElement) {
                originalWindowScrollTo.call(window, { top: 0, behavior: 'smooth' });
            } else {
                originalElementScrollTo.call(targetContainer, { top: 0, behavior: 'smooth' });
            }
        };

        const bottomBtn = document.createElement('button');
        bottomBtn.className = 'gemini-tool-btn-small';
        bottomBtn.innerText = '⬇️ 底部';
        bottomBtn.onclick = () => {
            const targetContainer = document.querySelector("#chat-history > infinite-scroller") || document.documentElement;
            if (targetContainer === document.documentElement) {
                originalWindowScrollTo.call(window, { top: document.body.scrollHeight, behavior: 'smooth' });
            } else {
                originalElementScrollTo.call(targetContainer, { top: targetContainer.scrollHeight, behavior: 'smooth' });
            }
        };

        btnRow.appendChild(topBtn);
        btnRow.appendChild(bottomBtn);

        container.appendChild(sBtn);
        container.appendChild(dBtn);
        container.appendChild(qBtn);
        container.appendChild(btnRow);

        wrapper.appendChild(toggleBtn);
        wrapper.appendChild(container);
        document.body.appendChild(wrapper);

        updateUI();
        updateUIVisibility();
    }

    // --- 6. 动态注入删除图标 ---
    function injectDelIcons() {
        // 先定位页面上所有“更多操作”菜单包裹层
        const menuBtnWrappers = document.querySelectorAll('[data-test-id="actions-menu-button"]');
        
        menuBtnWrappers.forEach(menuBtnWrapper => {
            // 获取它的直接父容器,不管它的 class 变成了什么
            const container = menuBtnWrapper.parentElement;
            if (!container || container.classList.contains('has-safe-del')) return;
            
            // 创建并插入删除垃圾桶图标
            const btn = document.createElement('div');
            btn.className = 'safe-del-btn';
            btn.appendChild(createTrashIcon());
            btn.onclick = (e) => {
                e.preventDefault(); e.stopPropagation();
                executeDelete(container);
            };
            
            // 将按钮插入到三个点菜单前面 (即左侧)
            container.insertBefore(btn, menuBtnWrapper);
            container.classList.add('has-safe-del');
        });
    }

    // --- 7. 监听与初始化 ---
    window.addEventListener('keydown', (e) => {
        if (e.altKey && e.key.toLowerCase() === 'q') {
            delModeActive = !delModeActive;
            localStorage.setItem('gemini_del_mode', delModeActive);
            console.log('[Gemini Helper] 快捷键触发 - 极速删除:', delModeActive);
            updateUI();
        }
    });

    const observer = new MutationObserver(() => {
        createUI();
        injectDelIcons();
    });
    observer.observe(document.body, { childList: true, subtree: true });

    createUI();
    injectDelIcons();
})();