您需要先安装一个扩展,例如 篡改猴、Greasemonkey 或 暴力猴,之后才能安装此脚本。
您需要先安装一个扩展,例如 篡改猴 或 暴力猴,之后才能安装此脚本。
您需要先安装一个扩展,例如 篡改猴 或 暴力猴,之后才能安装此脚本。
您需要先安装一个扩展,例如 篡改猴 或 Userscripts ,之后才能安装此脚本。
您需要先安装一款用户脚本管理器扩展,例如 Tampermonkey,才能安装此脚本。
您需要先安装用户脚本管理器扩展后才能安装此脚本。
当 AI Studio 页面加载时,自动关闭设置面板以节省页面空间。顺带一提,这个脚本是 Gemini 在 AI Studio 中编写的。
- // ==UserScript==
- // @name Automatically Close Google AI Studio Settings Panel
- // @name:zh-CN 自动关闭 Google AI Studio 设置面板
- // @namespace Violentmonkey Scripts
- // @match https://aistudio.google.com/*
- // @grant GM_addStyle
- // @run-at document-start
- // @version 1.1.20250413
- // @author plasma-green & Gemini 2.5 Pro Preview 03-25
- // @description When the AI Studio page loads, the settings panel is automatically closed to save page space. By the way, this script was written by Gemini in AI Studio.
- // @description:zh-CN 当 AI Studio 页面加载时,自动关闭设置面板以节省页面空间。顺带一提,这个脚本是 Gemini 在 AI Studio 中编写的。
- // @license AGPL-3.0
- // ==/UserScript==
- (function() {
- 'use strict';
- // --- 配置 ---
- const buttonSelector = 'button[aria-label="Close run settings panel"]'; // 目标按钮的选择器
- const logPrefix = '[AutoClick RunSettings] '; // 日志前缀,方便调试
- const clickDelay = 1200; // 延迟时间 (毫秒),2000ms = 2秒
- console.log(logPrefix + '脚本启动,开始监控按钮: ' + buttonSelector + ',延迟 ' + (clickDelay / 1000) + ' 秒');
- // 使用 WeakMap 来跟踪哪些按钮已经有待处理的点击计时器
- // WeakMap 的好处是当按钮元素从 DOM 中移除并被垃圾回收时,对应的条目也会自动移除,避免内存泄漏。
- const pendingClickTimeouts = new WeakMap();
- // --- 实际执行点击操作的函数 ---
- function performActualClick(button) {
- try {
- console.log(logPrefix + '执行点击...');
- // 尝试 dispatchEvent (无 view)
- const event = new MouseEvent('click', {
- bubbles: true,
- cancelable: true
- });
- if (button.dispatchEvent(event)) {
- console.log(logPrefix + 'dispatchEvent(click) 成功。');
- return true;
- } else {
- console.log(logPrefix + 'dispatchEvent(click) 执行但可能被取消。');
- return true; // 认为已尝试
- }
- } catch (dispatchError) {
- console.warn(logPrefix + 'dispatchEvent 点击失败:', dispatchError);
- console.log(logPrefix + '尝试回退到 button.click()...');
- try {
- button.click(); // 回退
- console.log(logPrefix + 'button.click() 成功。');
- return true;
- } catch (clickError) {
- console.error(logPrefix + 'button.click() 也失败:', clickError);
- return false;
- }
- }
- }
- // --- 检查按钮状态并计划(或取消)延迟点击 ---
- function scheduleOrCancelClick(button) {
- // 检查按钮当前是否可见且可交互
- const isButtonClickable = button && document.body.contains(button) && button.offsetParent !== null && !button.disabled;
- if (isButtonClickable) {
- // 如果按钮可点击,并且 *没有* 正在等待的计时器
- if (!pendingClickTimeouts.has(button)) {
- console.log(logPrefix + '检测到可点击按钮,将在 ' + (clickDelay / 1000) + ' 秒后点击...');
- const timeoutId = setTimeout(() => {
- // --- Timeout 回调 ---
- // 再次检查按钮状态,确保在延迟期间它没有消失或变得不可点击
- if (button && document.body.contains(button) && button.offsetParent !== null && !button.disabled) {
- console.log(logPrefix + '延迟时间到,按钮仍然可点击。');
- performActualClick(button);
- } else {
- console.log(logPrefix + '延迟时间到,但按钮已不可点击,取消本次点击。');
- }
- // 无论是否点击,都要从 WeakMap 中移除记录,允许下次重新调度
- pendingClickTimeouts.delete(button);
- // --- Timeout 回调结束 ---
- }, clickDelay);
- // 将计时器 ID 存入 WeakMap,标记这个按钮正在等待点击
- pendingClickTimeouts.set(button, timeoutId);
- } else {
- // console.log(logPrefix + '按钮已在等待点击计时中,忽略本次检测。'); // 可选调试日志
- }
- } else {
- // 如果按钮变得不可点击 (隐藏、禁用、移除) 并且 *有* 正在等待的计时器
- if (pendingClickTimeouts.has(button)) {
- console.log(logPrefix + '按钮变得不可点击,取消之前计划的延迟点击。');
- clearTimeout(pendingClickTimeouts.get(button)); // 清除计时器
- pendingClickTimeouts.delete(button); // 从 WeakMap 中移除记录
- }
- }
- }
- // --- MutationObserver 回调 ---
- const observerCallback = (mutationsList, observer) => {
- // 简单起见,每次DOM变化都重新查找按钮并评估状态
- // 优化:可以只检查与 buttonSelector 相关的变化,但通常 querySelector 性能足够
- const targetButton = document.querySelector(buttonSelector);
- if (targetButton) {
- // 如果按钮存在,调用调度函数来决定是否启动或什么都不做
- scheduleOrCancelClick(targetButton);
- } else {
- // 如果按钮在 DOM 中找不到了,我们需要检查是否有对应的计时器需要取消
- // (注意:如果按钮实例还在内存中且在WeakMap里,下面的逻辑可能不完美,
- // 但通常按钮不存在于DOM时,scheduleOrCancelClick(null)不会执行,
- // 而如果按钮实例还在但被隐藏/禁用,scheduleOrCancelClick会处理取消)
- // 简单起见,依赖 scheduleOrCancelClick 在按钮不可见时处理取消。
- // console.log(logPrefix + '未找到目标按钮。');
- }
- // --- 更精细的检查(可选)---
- // 如果性能有问题,可以像之前版本那样检查 mutationsList
- // 但需要在找到按钮或按钮消失时都调用 scheduleOrCancelClick
- /*
- for (const mutation of mutationsList) {
- // ... 检查添加、移除、属性变化 ...
- if (/* 按钮出现或变得可点击 * /) {
- const button = mutation.target or found button;
- scheduleOrCancelClick(button);
- } else if (/* 按钮消失或变得不可点击 * /) {
- const button = mutation.target or previously known button;
- // 需要一种方法获取到旧按钮的引用来取消计时器
- // 这使得简单地每次重新 querySelector 更方便
- }
- }
- */
- };
- // --- 创建并配置观察器 ---
- const observer = new MutationObserver(observerCallback);
- const observerConfig = {
- childList: true,
- subtree: true,
- attributes: true, // 监控属性变化也很重要,因为按钮可能通过 disabled 或 style/class 变化
- attributeFilter: ['disabled', 'class', 'style'] // 可以限定关心的属性
- };
- // --- 初始化脚本 ---
- function initialize() {
- if (document.body) {
- console.log(logPrefix + 'DOM 已准备好,执行首次检查并启动 MutationObserver...');
- const initialButton = document.querySelector(buttonSelector);
- if (initialButton) {
- scheduleOrCancelClick(initialButton); // 初始检查也走调度逻辑
- }
- observer.observe(document.body, observerConfig);
- console.log(logPrefix + 'MutationObserver 已启动。');
- } else {
- requestAnimationFrame(initialize);
- }
- }
- // --- 启动逻辑 ---
- if (document.readyState === 'loading') {
- document.addEventListener('DOMContentLoaded', initialize);
- } else {
- initialize();
- }
- // --- 清理 ---
- window.addEventListener('unload', () => {
- if (observer) {
- observer.disconnect();
- console.log(logPrefix + '页面卸载,MutationObserver 已断开。');
- }
- // 清理所有可能存在的待处理计时器
- // 遍历当前页面上所有匹配的按钮来查找并清除计时器
- document.querySelectorAll(buttonSelector).forEach(button => {
- if (pendingClickTimeouts.has(button)) {
- clearTimeout(pendingClickTimeouts.get(button));
- pendingClickTimeouts.delete(button);
- console.log(logPrefix + '页面卸载,清除了一个待处理的点击计时器。');
- }
- });
- });
- GM_addStyle(`
- /* 隐藏不需要的元素 */
- .header-container, .placeholder-overlay.ng-star-inserted {
- display: none !important;
- }`);
- })();