Google AI Studio | 清空聊天记录

清空Google AI Studio聊天记录

您需要先安装一个扩展,例如 篡改猴Greasemonkey暴力猴,之后才能安装此脚本。

您需要先安装一个扩展,例如 篡改猴暴力猴,之后才能安装此脚本。

您需要先安装一个扩展,例如 篡改猴暴力猴,之后才能安装此脚本。

您需要先安装一个扩展,例如 篡改猴Userscripts ,之后才能安装此脚本。

您需要先安装一款用户脚本管理器扩展,例如 Tampermonkey,才能安装此脚本。

您需要先安装用户脚本管理器扩展后才能安装此脚本。

(我已经安装了用户脚本管理器,让我安装!)

您需要先安装一款用户样式管理器扩展,比如 Stylus,才能安装此样式。

您需要先安装一款用户样式管理器扩展,比如 Stylus,才能安装此样式。

您需要先安装一款用户样式管理器扩展,比如 Stylus,才能安装此样式。

您需要先安装一款用户样式管理器扩展后才能安装此样式。

您需要先安装一款用户样式管理器扩展后才能安装此样式。

您需要先安装一款用户样式管理器扩展后才能安装此样式。

(我已经安装了用户样式管理器,让我安装!)

// ==UserScript==
// @name         Google AI Studio | 清空聊天记录
// @namespace    https://greasyfork.org/
// @description  清空Google AI Studio聊天记录
// @version      1.1
// @author       Henry
// @match        https://aistudio.google.com/*
// @icon         https://www.google.com/s2/favicons?sz=64&domain=aistudio.google.com
// @license      MIT
// @grant        GM_addStyle
// @run-at       document-idle
// ==/UserScript==

(function() {
    'use strict';
    console.log('[Gemini Chat Cleaner] Script loaded and starting...');
    console.log('[Gemini Chat Cleaner] Current URL:', window.location.href);

    //================================================================================
    // CONFIGURATION
    //================================================================================
    const CHAT_TURN_OPTIONS_SELECTOR = 'ms-chat-turn-options span[class="material-symbols-outlined notranslate ms-button-icon-symbol ng-star-inserted"]';
    const DELETE_BUTTON_MENU_SELECTOR = 'div.mat-mdc-menu-content > button:first-of-type';
    const DELETE_BUTTON_TEXT = "delete Delete";

    // --- 新增: 用于SPA导航检测 ---
    const BUTTON_ID = 'gemini-cleaner-button';
    const TOOLBAR_SELECTOR = 'ms-toolbar .toolbar-right'; 
    const TARGET_URL_PATH = '/prompts/'; 

    //================================================================================
    // STYLES
    //================================================================================
    GM_addStyle(`
        #${BUTTON_ID} {
            margin: 0 4px;
        }
    `);

    //================================================================================
    // HELPER FUNCTIONS (与原版相同)
    //================================================================================
    /**
     * Clicks all elements matching a given CSS selector.
     */
    function clickAllElements(selector) {
        try {
            const elements = document.querySelectorAll(selector);
            if (elements.length === 0) {
                console.warn(`[Gemini Chat Cleaner] No elements found for selector: ${selector}`);
                return;
            }
            elements.forEach(element => {
                element.click();
            });
            console.log(`[Gemini Chat Cleaner] Clicked ${elements.length} elements for selector: ${selector}`);
        } catch (error) {
            console.error(`[Gemini Chat Cleaner] Error clicking elements for selector ${selector}:`, error);
        }
    }

    /**
     * Clicks delete buttons within a menu content, identified by text.
     */
    function clickDeleteButtonsInMenu(selector, text) {
        try {
            const elements = document.querySelectorAll(selector);
            if (elements.length === 0) {
                console.warn(`[Gemini Chat Cleaner] No menu elements found for selector: ${selector}`);
                return;
            }
            let clickedCount = 0;
            elements.forEach(element => {
                if (element.textContent.trim() === text) {
                    element.click();
                    clickedCount++;
                }
            });
            if (clickedCount > 0) {
                 console.log(`[Gemini Chat Cleaner] Clicked ${clickedCount} delete button(s) with text: "${text}"`);
            }
        } catch (error) {
            console.error(`[Gemini Chat Cleaner] Error clicking delete buttons in menu for selector ${selector}:`, error);
        }
    }

    //================================================================================
    // MAIN EXECUTION (与原版相同)
    //================================================================================
    function main() {
        console.log('[Gemini Chat Cleaner] Main function triggered');
        // 点击所有聊天回合的 "选项" 图标
        clickAllElements(CHAT_TURN_OPTIONS_SELECTOR);

        // **重要**: 
        // 因为点击 "选项" 会在 <body> 下创建多个分离的菜单
        // 所以下一步的 `querySelectorAll` 可以找到所有菜单并点击。
        // 为确保菜单有时间渲染,加一个小延迟。
        setTimeout(() => {
            clickDeleteButtonsInMenu(DELETE_BUTTON_MENU_SELECTOR, DELETE_BUTTON_TEXT);
        }, 100); // 延迟100毫秒
    }

    //================================================================================
    // REFACTORED: Button Injection and SPA Handling
    //================================================================================

    /**
     * 实际创建和注入按钮的函数
     * @param {Element} toolbarRight - 已经找到的工具栏元素
     */
    function injectButton(toolbarRight) {
        console.log('[Gemini Chat Cleaner] Injecting button...');

        const button = document.createElement('button');
        button.id = BUTTON_ID;
        button.title = 'Clear Chat Turns';
        button.setAttribute('ms-button', '');
        button.setAttribute('variant', 'icon-borderless');
        button.setAttribute('mattooltip', 'Clear Chat Turns');
        button.setAttribute('mattooltipposition', 'below');
        button.setAttribute('iconname', 'refresh');
        button.className = 'mat-mdc-tooltip-trigger ng-tns-c2648639672-5 ms-button-borderless ms-button-icon ng-star-inserted';
        button.setAttribute('aria-label', 'Clear Chat Turns');
        button.setAttribute('aria-disabled', 'false');
        button.addEventListener('click', main);

        const iconSpan = document.createElement('span');
        iconSpan.className = 'material-symbols-outlined notranslate ms-button-icon-symbol ng-star-inserted';
        iconSpan.setAttribute('aria-hidden', 'true');
        iconSpan.textContent = 'refresh';

        button.appendChild(iconSpan);

        // 插入到 "more_vert" 按钮之前
        const moreButton = toolbarRight.querySelector('button[iconname="more_vert"]');
        if (moreButton) {
            toolbarRight.insertBefore(button, moreButton);
            console.log('[Gemini Chat Cleaner] Button inserted before more_vert button');
        } else {
            toolbarRight.appendChild(button);
            console.log('[Gemini Chat Cleaner] Button appended to toolbar');
        }
    }

    /**
     * 检查是否需要注入按钮的核心函数
     * (这是 MutationObserver 的回调)
     */
    function checkAndInjectButton() {
        // 1. 检查URL是否匹配
        if (!window.location.href.includes(TARGET_URL_PATH)) {
            // 不在聊天页面,什么也不做
            return;
        }

        // 2. 检查工具栏是否存在
        const toolbarRight = document.querySelector(TOOLBAR_SELECTOR);

        if (toolbarRight) {
            // 3. 工具栏存在,检查按钮是否 *不* 存在
            if (!document.getElementById(BUTTON_ID)) {
                // 工具栏存在,但按钮不存在 -> 注入
                console.log('[Gemini Chat Cleaner] Chat page and toolbar found. Button missing. Injecting...');
                injectButton(toolbarRight);
            }
            // (如果工具栏和按钮都存在,什么也不做)
        }
        // (如果工具栏不存在,什么也不做,等待下一次DOM变化)
    }

    /**
     * 启动 MutationObserver 来监视DOM变化
     */
    function startObserver() {
        console.log('[Gemini Chat Cleaner] Starting MutationObserver...');

        const observer = new MutationObserver((mutationsList, obs) => {
            // 每当DOM变化时,重新运行检查
            // 可以添加 "debounce" 来防止过于频繁的检查,但现在保持简单
            checkAndInjectButton();
        });

        // 配置观察者:
        // childList: true - 监视子元素的添加或删除
        // subtree: true   - 监视所有后代节点
        observer.observe(document.body, { childList: true, subtree: true });
    }

    /**
     * 脚本初始化入口
     */
    function init() {
        console.log('[Gemini Chat Cleaner] Initializing...');
        // 1. 立即检查一次,应对 "F5 刷新"
        //    为确保 F5 刷新时 DOM 完全准备好,稍微延迟一下
        setTimeout(checkAndInjectButton, 500);

        // 2. 启动观察者,应对 "SPA 导航"
        startObserver();
    }

    //================================================================================
    // SCRIPT ENTRY POINT
    //================================================================================
    if (document.readyState === 'loading') {
        document.addEventListener('DOMContentLoaded', init);
    } else {
        // DOM 已经加载完成
        init();
    }

})();