您需要先安装一个扩展,例如 篡改猴、Greasemonkey 或 暴力猴,之后才能安装此脚本。
您需要先安装一个扩展,例如 篡改猴 或 暴力猴,之后才能安装此脚本。
您需要先安装一个扩展,例如 篡改猴 或 暴力猴,之后才能安装此脚本。
您需要先安装一个扩展,例如 篡改猴 或 Userscripts ,之后才能安装此脚本。
您需要先安装一款用户脚本管理器扩展,例如 Tampermonkey,才能安装此脚本。
您需要先安装用户脚本管理器扩展后才能安装此脚本。
提供护眼样式、元素显隐控制、自动折叠左右侧面板功能、优化输入框体验及宽度,优化 Google AI Studio 使用体验。
// ==UserScript== // @name Google AI Studio Optimization // @name:zh-CN Google AI Studio 页面优化 // @namespace http://tampermonkey.net/ // @version 1.0.1 // @description Eye-Friendly Styles, Element Control, Auto Collapse Panels, Enhanced Input Width. // @description:zh-CN 提供护眼样式、元素显隐控制、自动折叠左右侧面板功能、优化输入框体验及宽度,优化 Google AI Studio 使用体验。 // @author Gemini // @match https://aistudio.google.com/prompts/* // @match https://aistudio.google.com/*/prompts/* // @grant GM_addStyle // @grant GM_getValue // @grant GM_setValue // @grant GM_registerMenuCommand // @grant GM_unregisterMenuCommand // @grant GM_info // @license MIT // ==/UserScript== (function() { 'use strict'; const SCRIPT_PREFIX = '[AI Studio 增强+]'; console.log(`${SCRIPT_PREFIX} 初始化 v${GM_info.script.version}...`); // --- 默认设置 --- const defaultSettings = { useCustomStyles: true, showUserPrompts: true, showThinkingProcess: true, showAIMessages: true, showInputBox: true, showTopNavBar: true, showChatTitleBar: true, showTokenCount: true, // 注意: 此设置目前在脚本中未实际控制任何元素的显隐 autoCollapseRightPanel: false, autoCollapseLeftPanel: false }; // --- 初始化设置 --- let settings = {}; for (const key in defaultSettings) { settings[key] = GM_getValue(key, defaultSettings[key]); if (GM_getValue(key) === undefined) { GM_setValue(key, defaultSettings[key]); console.log(`${SCRIPT_PREFIX} 初始化新设置: ${key} = ${defaultSettings[key]}`); } } console.log(`${SCRIPT_PREFIX} 当前设置:`, settings); // --- 菜单文本定义 --- const MENU_TEXT_ENABLED = "🟢 启用"; const MENU_TEXT_DISABLED = "🔴 禁用"; const MENU_TEXT_SHOW_ALL = "🟢 显示所有"; const MENU_TEXT_HIDE_ALL = "🔴 隐藏所有"; // --- 菜单定义 --- var menu_ALL = [ [ "useCustomStyles", "自定义样式", ], [ "autoCollapseLeftPanel", "自动折叠左侧面板", ], [ "autoCollapseRightPanel", "自动折叠右侧面板", ], [ "showTopNavBar", "顶部导航栏", ], [ "showChatTitleBar", "聊天标题栏", ], [ "showUserPrompts", "用户消息", ], [ "showThinkingProcess", "AI 思考过程", ], [ "showAIMessages", "AI 消息", ], [ "showInputBox", "底部输入框", ], [ "toggleAllDisplays", // 特殊键名,用于切换所有可见性设置 "切换所有显示项", // 这个基础文本在 toggleAllDisplays 中会进一步动态调整 ], ]; var menu_ID = []; // 用于存储菜单命令ID,以便注销 // --- CSS 样式 --- const customStyles = ` :root { /* 主题颜色变量 */ --enhancer-bg-main: #f8f9fa; --enhancer-text-main: #212529; --enhancer-bg-user-prompt: #e9ecef; --enhancer-text-user-prompt: #343a40; --enhancer-bg-model-prompt: #ffffff; --enhancer-text-model-prompt: #212529; --enhancer-border-color: #dee2e6; --enhancer-button-bg: #f1f3f5; --enhancer-button-hover-bg: #e9ecef; --enhancer-header-bg: #ffffff; --enhancer-input-bg: #ffffff; /* 用于输入框区域的背景,可与主背景区分 */ --enhancer-link-color: #007bff; --enhancer-code-bg: #e9ecef; --enhancer-code-text: #212529; --enhancer-thinking-bg: #f8f9fa; /* 字体大小 - 显著缩小 */ --enhancer-font-size-base: 13px; /* 基础字号调小 */ --enhancer-font-size-xs: 0.78rem; /* 约 10px, 用于页脚等辅助文字 */ --enhancer-font-size-sm: 0.88rem; /* 约 11.5px, 用于次要文字 */ --enhancer-font-size-md: 0.94rem; /* 约 12.2px, 用于正文和主要内容 */ --enhancer-font-size-lg: 1.0rem; /* 约 13px, 用于小标题等 */ --enhancer-line-height-base: 1.45; /* 减小行高 */ } @media (prefers-color-scheme: dark) { :root { --enhancer-bg-main: #212529; --enhancer-text-main: #e9ecef; --enhancer-bg-user-prompt: #343a40; --enhancer-text-user-prompt: #f8f9fa; --enhancer-bg-model-prompt: #2c3034; --enhancer-text-model-prompt: #f8f9fa; --enhancer-border-color: #495057; --enhancer-button-bg: #495057; --enhancer-button-hover-bg: #5a6268; --enhancer-header-bg: #2c3034; --enhancer-input-bg: #212529; /* 暗黑模式下输入框区域背景,可与主背景一致或稍有区别 */ --enhancer-link-color: #6cb2eb; --enhancer-code-bg: #343a40; --enhancer-code-text: #f8f9fa; --enhancer-thinking-bg: #2c3034; } } /* 页面整体字体和背景 */ body { font-family: -apple-system, BlinkMacSystemFont, "Segoe UI", Roboto, "Helvetica Neue", Arial, "Noto Sans", "Liberation Sans", "思源黑体", "思源黑体 CN", "Microsoft YaHei", "微软雅黑", sans-serif, "Apple Color Emoji", "Segoe UI Emoji", "Segoe UI Symbol", "Noto Color Emoji" !important; font-size: var(--enhancer-font-size-md) !important; line-height: var(--enhancer-line-height-base) !important; color: var(--enhancer-text-main) !important; background-color: var(--enhancer-bg-main) !important; } .chunk-editor-main { background: var(--enhancer-bg-main) !important; padding: 8px !important; } .chunk-editor-main p { font-family: inherit !important; margin-bottom: 0.5em !important; } /* 用户消息气泡 */ .user-prompt-container .text-chunk { background: var(--enhancer-bg-user-prompt) !important; color: var(--enhancer-text-user-prompt) !important; padding: 7px 10px !important; border-radius: 6px !important; box-shadow: 0 1px 2px rgba(0,0,0,0.04); margin-bottom: 6px !important; } .user-prompt-container p { font-size: var(--enhancer-font-size-md) !important; line-height: var(--enhancer-line-height-base) !important; color: var(--enhancer-text-user-prompt) !important; } /* AI 模型消息气泡 */ .model-prompt-container { background: var(--enhancer-bg-model-prompt) !important; color: var(--enhancer-text-model-prompt) !important; padding: 8px 12px !important; border-radius: 8px !important; border: 1px solid var(--enhancer-border-color) !important; box-shadow: 0 1px 3px rgba(0,0,0,0.06); margin-bottom: 10px !important; } .model-prompt-container:has(.mat-accordion) { background: transparent !important; border: none !important; box-shadow: none !important; padding: 5px !important; } .model-prompt-container p { font-size: var(--enhancer-font-size-md) !important; line-height: var(--enhancer-line-height-base) !important; color: var(--enhancer-text-model-prompt) !important; } .model-prompt-container pre, .model-prompt-container code { background-color: var(--enhancer-code-bg) !important; color: var(--enhancer-code-text) !important; padding: 0.15em 0.3em; font-size: 0.88em; border-radius: 3px; font-family: "SFMono-Regular", Consolas, "Liberation Mono", Menlo, Courier, monospace !important; } .model-prompt-container pre { padding: 0.8em !important; margin: 0.3em 0 !important; overflow-x: auto !important; } /* AI 思考过程 / Accordion 折叠内容 */ ms-thought-chunk, .mat-accordion { background-color: var(--enhancer-thinking-bg) !important; border-radius: 5px !important; padding: 6px 8px !important; margin-bottom: 6px !important; } .mat-accordion p { font-size: var(--enhancer-font-size-sm) !important; color: var(--enhancer-text-main) !important; opacity: 0.85; } ms-thought-chunk{ max-width:1300px !important; } /* 每轮对话的页脚 (Token 数量等) */ .turn-footer { font-size: var(--enhancer-font-size-xs) !important; background: none !important; opacity: 0.65; color: var(--enhancer-text-main) !important; padding-top: 5px !important; margin-bottom: 5px !important; } /* 顶部导航和头部区域 */ .page-header, .header-container { height: auto !important; padding: 5px 10px !important; background-color: var(--enhancer-header-bg) !important; border-bottom: 1px solid var(--enhancer-border-color) !important; } .top-nav { font-size: var(--enhancer-font-size-sm) !important; display: flex; align-items: center; } /* 聊天区域的工具栏 */ .toolbar-container { height: auto !important; padding: 5px 10px !important; background-color: var(--enhancer-header-bg) !important; border-bottom: 1px solid var(--enhancer-border-color) !important; } /* 链接样式 */ a { color: var(--enhancer-link-color) !important; text-decoration: none !important; } a:hover { text-decoration: underline !important; } /* --------------------------------------------------- */ /* Enhanced Input Area Styles (Footer) */ /* --------------------------------------------------- */ footer:has(.prompt-input-wrapper) { background-color: var(--enhancer-input-bg) !important; border-top: 1px solid var(--enhancer-border-color) !important; padding: 10px 10px !important; /* MODIFIED: Reduced horizontal padding */ position: sticky !important; bottom: 0 !important; z-index: 100 !important; box-shadow: 0 -2px 5px rgba(0,0,0,0.05); } @media (prefers-color-scheme: dark) { footer:has(.prompt-input-wrapper) { box-shadow: 0 -2px 8px rgba(0,0,0,0.15) !important; } } .prompt-input-wrapper { display: flex !important; align-items: center !important; gap: 8px !important; /* MODIFIED: Slightly reduced gap */ } .prompt-input-wrapper-container { flex-grow: 1 !important; display: flex !important; position: relative !important; } .prompt-input-wrapper-container textarea { flex-grow: 1 !important; background-color: var(--enhancer-bg-main) !important; /* Use main background for textarea */ color: var(--enhancer-text-main) !important; border: 1px solid var(--enhancer-border-color) !important; border-radius: 22px !important; padding: 10px 18px !important; font-size: var(--enhancer-font-size-md) !important; line-height: var(--enhancer-line-height-base) !important; min-height: 44px !important; max-height: 200px !important; resize: none !important; box-shadow: 0 1px 2px rgba(0,0,0,0.03) inset !important; transition: border-color 0.2s ease, box-shadow 0.2s ease; } .prompt-input-wrapper-container textarea::placeholder { color: color-mix(in srgb, var(--enhancer-text-main) 60%, transparent) !important; opacity: 1 !important; } .prompt-input-wrapper-container textarea:focus { outline: none !important; border-color: var(--enhancer-link-color) !important; background-color: var(--enhancer-bg-main) !important; box-shadow: 0 0 0 3px color-mix(in srgb, var(--enhancer-link-color) 20%, transparent), 0 1px 2px rgba(0,0,0,0.03) inset !important; } /* Standard Icon Buttons (Mic, Attach, etc.) */ footer .prompt-input-wrapper button.mat-icon-button, footer .prompt-input-wrapper button[mat-icon-button] { background-color: transparent !important; color: var(--enhancer-text-main) !important; border: none !important; border-radius: 50% !important; width: 40px !important; height: 40px !important; padding: 0 !important; display: inline-flex !important; align-items: center !important; justify-content: center !important; flex-shrink: 0; transition: background-color 0.2s ease, color 0.2s ease; } footer .prompt-input-wrapper button.mat-icon-button:hover, footer .prompt-input-wrapper button[mat-icon-button]:hover { background-color: var(--enhancer-button-hover-bg) !important; } footer .prompt-input-wrapper button.mat-icon-button mat-icon, footer .prompt-input-wrapper button[mat-icon-button] mat-icon, footer .prompt-input-wrapper button.mat-icon-button svg, footer .prompt-input-wrapper button[mat-icon-button] svg { font-size: 22px !important; width: 22px !important; height: 22px !important; fill: currentColor !important; } /* Prominent Action Button (FAB-like: Send, Stop) */ footer .prompt-input-wrapper button.mat-fab, footer .prompt-input-wrapper button[mat-fab], footer .prompt-input-wrapper button.mat-flat-button.send-button-class, /* Add .send-button-class to your Send button if it's flat */ footer .prompt-input-wrapper button[mat-flat-button].send-button-class { background-color: var(--enhancer-link-color) !important; color: white !important; border: none !important; border-radius: 50% !important; width: 44px !important; height: 44px !important; padding: 0 !important; display: inline-flex !important; align-items: center !important; justify-content: center !important; box-shadow: 0 2px 5px rgba(0,0,0,0.12), 0 1px 3px rgba(0,0,0,0.08) !important; flex-shrink: 0; transition: background-color 0.2s ease, box-shadow 0.2s ease; } footer .prompt-input-wrapper button.mat-fab:hover, footer .prompt-input-wrapper button[mat-fab]:hover, footer .prompt-input-wrapper button.mat-flat-button.send-button-class:hover, footer .prompt-input-wrapper button[mat-flat-button].send-button-class:hover { background-color: color-mix(in srgb, var(--enhancer-link-color) 90%, black) !important; box-shadow: 0 4px 8px rgba(0,0,0,0.15), 0 2px 4px rgba(0,0,0,0.1) !important; } footer .prompt-input-wrapper button.mat-fab mat-icon, footer .prompt-input-wrapper button[mat-fab] mat-icon, footer .prompt-input-wrapper button.mat-fab svg, footer .prompt-input-wrapper button[mat-fab] svg, footer .prompt-input-wrapper button.mat-flat-button.send-button-class mat-icon, footer .prompt-input-wrapper button[mat-flat-button].send-button-class svg { fill: white !important; color: white !important; font-size: 24px !important; width: 24px !important; height: 24px !important; } /* Rectangular Flat Button (if used for e.g. a text "Send" or "Run" button and not covered by .send-button-class) */ footer .prompt-input-wrapper button.mat-flat-button:not([class*='mat-fab']):not([class*='mat-icon-button']):not(.send-button-class), footer .prompt-input-wrapper button[mat-flat-button]:not([class*='mat-fab']):not([class*='mat-icon-button']):not(.send-button-class) { background-color: var(--enhancer-button-bg) !important; color: var(--enhancer-text-main) !important; border-radius: 20px !important; padding: 0 12px !important; /* MODIFIED: Reduced horizontal padding for text buttons */ height: 40px !important; font-size: var(--enhancer-font-size-sm) !important; font-weight: 500 !important; border: 1px solid var(--enhancer-border-color) !important; box-shadow: none !important; transition: background-color 0.2s ease, border-color 0.2s ease; white-space: nowrap; /* Prevent text like "Ctrl+Enter" from wrapping if it's part of button text */ } footer .prompt-input-wrapper button.mat-flat-button:not([class*='mat-fab']):not([class*='mat-icon-button']):not(.send-button-class):hover { background-color: var(--enhancer-button-hover-bg) !important; } footer .prompt-input-wrapper button.mat-flat-button:not([class*='mat-fab']):not([class*='mat-icon-button']):not(.send-button-class) mat-icon, footer .prompt-input-wrapper button[mat-flat-button]:not([class*='mat-fab']):not([class*='mat-icon-button']):not(.send-button-class) svg { margin-right: 6px !important; /* MODIFIED: Reduced margin for icon in text button */ font-size: 18px !important; width: 18px !important; height: 18px !important; } /* User Avatar (adjust selector if needed: e.g., img.user-avatar or button.avatar-button img) */ footer .prompt-input-wrapper img[src*="googleusercontent.com"], footer .prompt-input-wrapper .user-avatar-selector { /* Placeholder for more specific avatar selector */ width: 36px !important; height: 36px !important; border-radius: 50% !important; object-fit: cover !important; margin-left: 4px !important; /* MODIFIED: Reduced margin */ flex-shrink: 0; border: 1px solid var(--enhancer-border-color); } /* .prompt-chip-button style (for chips like "Add file", usually above or separate from main input actions) */ .prompt-chip-button { background: var(--enhancer-button-bg) !important; color: var(--enhancer-text-main) !important; border: 1px solid var(--enhancer-border-color) !important; border-radius: 16px !important; /* Pill-shaped for chips */ padding: 5px 12px !important; font-size: var(--enhancer-font-size-sm) !important; transition: background-color 0.15s ease, border-color 0.15s ease; margin: 4px !important; height: auto !important; width: auto !important; display: inline-flex !important; align-items: center !important; justify-content: center !important; text-decoration: none !important; } .prompt-chip-button:hover { background: var(--enhancer-button-hover-bg) !important; text-decoration: none !important; } `; const hideUserPromptsStyle = `.chat-turn-container.user { display: none !important; }`; const hideThinkingProcessStyle = `.chat-turn-container .thought-container, ms-thought-chunk {display: none !important;}`; const hideAIMessagesStyle = `.chat-turn-container.model { display: none !important; }`; const hideInputBoxStyle = `footer:has(.prompt-input-wrapper) { display: none !important; }`; const hideTopNavBarStyle = `.header-container { display: none !important; }`; const hideChatTitleBarStyle = `.page-header > .content > .title-toolbar, .toolbar-container { display: none !important; }`; // --- 样式应用函数 --- function updateStyles() { const existingStyles = document.querySelectorAll('style[data-enhancer-style]'); existingStyles.forEach(style => style.remove()); if (settings.useCustomStyles) { const styleElement = document.createElement('style'); styleElement.setAttribute('data-enhancer-style', 'base'); styleElement.textContent = customStyles; document.head.appendChild(styleElement); } if (!settings.showUserPrompts) { const hideUserStyle = document.createElement('style'); hideUserStyle.setAttribute('data-enhancer-style', 'user-visibility'); hideUserStyle.textContent = hideUserPromptsStyle; document.head.appendChild(hideUserStyle); } if (!settings.showThinkingProcess) { const hideThinkingStyle = document.createElement('style'); hideThinkingStyle.setAttribute('data-enhancer-style', 'thinking-visibility'); hideThinkingStyle.textContent = hideThinkingProcessStyle; document.head.appendChild(hideThinkingStyle); } if (!settings.showAIMessages) { const hideAIStyle = document.createElement('style'); hideAIStyle.setAttribute('data-enhancer-style', 'ai-message-visibility'); hideAIStyle.textContent = hideAIMessagesStyle; document.head.appendChild(hideAIStyle); } if (!settings.showInputBox) { const hideInputBox = document.createElement('style'); hideInputBox.setAttribute('data-enhancer-style', 'input-box-visibility'); hideInputBox.textContent = hideInputBoxStyle; document.head.appendChild(hideInputBox); } if (!settings.showTopNavBar) { const hideTopNav = document.createElement('style'); hideTopNav.setAttribute('data-enhancer-style', 'top-nav-visibility'); hideTopNav.textContent = hideTopNavBarStyle; document.head.appendChild(hideTopNav); } if (!settings.showChatTitleBar) { const hideChatTitle = document.createElement('style'); hideChatTitle.setAttribute('data-enhancer-style', 'chat-title-visibility'); hideChatTitle.textContent = hideChatTitleBarStyle; document.head.appendChild(hideChatTitle); } console.log(`${SCRIPT_PREFIX} 已根据设置更新样式.`); } // --- 浮动通知函数 --- function showNotification(message) { const notificationId = 'enhancer-notification'; let notification = document.getElementById(notificationId); if (!notification) { notification = document.createElement('div'); notification.id = notificationId; notification.style.cssText = ` position: fixed; bottom: 20px; left: 50%; transform: translateX(-50%); background-color: rgba(40, 40, 40, 0.9); color: white; padding: 12px 22px; border-radius: 8px; box-shadow: 0 4px 15px rgba(0,0,0,0.2); z-index: 10000; opacity: 0; transition: opacity 0.4s ease-in-out, bottom 0.4s ease-in-out; font-size: 14px; text-align: center; max-width: 80%; `; document.body.appendChild(notification); } if (notification.timeoutId) { clearTimeout(notification.timeoutId); notification.style.bottom = '0px'; notification.style.opacity = '0'; } notification.textContent = message; setTimeout(() => { notification.style.opacity = '1'; notification.style.bottom = '20px'; }, 50); notification.timeoutId = setTimeout(() => { notification.style.opacity = '0'; notification.style.bottom = '0px'; setTimeout(() => { if (notification.parentNode) { notification.parentNode.removeChild(notification); } notification.timeoutId = null; }, 400); }, 2500); } // --- 菜单命令函数 --- function registerMenuCommands() { menu_ID.forEach(id => GM_unregisterMenuCommand(id)); menu_ID = []; console.log(`${SCRIPT_PREFIX} 注册(不可用)菜单命令...`); menu_ALL.forEach(item => { const settingKey = item[0]; const baseMenuText = item[1]; if (settingKey === "toggleAllDisplays") { const displaySettingsKeys = [ "showUserPrompts", "showThinkingProcess", "showAIMessages", "showInputBox", "showTopNavBar", "showChatTitleBar" ]; const allEnabled = displaySettingsKeys.every(key => settings[key]); const menuText = `${allEnabled ? MENU_TEXT_HIDE_ALL : MENU_TEXT_SHOW_ALL} (${baseMenuText})`; menu_ID.push(GM_registerMenuCommand(menuText, toggleAllDisplays)); } else { if (settings.hasOwnProperty(settingKey)) { const currentSettingValue = settings[settingKey]; const menuText = `${currentSettingValue ? MENU_TEXT_DISABLED : MENU_TEXT_ENABLED} ${baseMenuText}`; menu_ID.push(GM_registerMenuCommand( menuText, () => menuSwitch(settingKey) )); } else { console.warn(`${SCRIPT_PREFIX} 注册(不可用)菜单时未找到设置键 "${settingKey}".`); } } }); console.log(`${SCRIPT_PREFIX} 菜单命令已注册(不可用).`); } function menuSwitch(settingKey) { let newValue = !settings[settingKey]; settings[settingKey] = newValue; GM_setValue(settingKey, newValue); console.log(`${SCRIPT_PREFIX} 已切换 ${settingKey} 至 ${newValue}`); const menuItem = menu_ALL.find(item => item[0] === settingKey); if (!menuItem) { console.error(`${SCRIPT_PREFIX} 未能找到设置键的菜单项: ${settingKey}`); return; } const baseMenuText = menuItem[1]; if (['useCustomStyles', 'showUserPrompts', 'showThinkingProcess', 'showAIMessages', 'showInputBox', 'showTopNavBar', 'showChatTitleBar'].includes(settingKey)) { updateStyles(); } else if (settingKey === 'autoCollapseRightPanel') { if (newValue) { console.log(`${SCRIPT_PREFIX} 自动折叠右侧面板已启用, 尝试初始检查/点击.`); setTimeout(triggerAutoCollapseRightPanelIfNeeded, 500); } } else if (settingKey === 'autoCollapseLeftPanel') { if (newValue) { console.log(`${SCRIPT_PREFIX} 自动折叠左侧面板已启用, 尝试折叠.`); setTimeout(triggerAutoCollapseLeftPanelIfNeeded, 500); } } registerMenuCommands(); showNotification(`${baseMenuText} ${newValue ? '已启用' : '已禁用'}`); } function toggleAllDisplays() { const displaySettingsKeys = [ "showUserPrompts", "showThinkingProcess", "showAIMessages", "showInputBox", "showTopNavBar", "showChatTitleBar" ]; const enableAll = !displaySettingsKeys.every(key => settings[key]); console.log(`${SCRIPT_PREFIX} 切换所有显示项至: ${enableAll ? '显示' : '隐藏'}`); displaySettingsKeys.forEach(key => { if (settings.hasOwnProperty(key)) { settings[key] = enableAll; GM_setValue(key, enableAll); } else { console.warn(`${SCRIPT_PREFIX} 切换所有显示项时未找到设置键 "${key}".`); } }); updateStyles(); registerMenuCommands(); showNotification(`所有显示项 ${enableAll ? '已启用 (显示)' : '已禁用 (隐藏)'}`); } // --- 自动折叠右侧面板逻辑 --- const RUN_SETTINGS_BUTTON_SELECTOR = '.toggles-container button[aria-label="Run settings"], .toggles-container button[aria-label="运行设置"]'; const RIGHT_PANEL_TAG_NAME = 'MS-RIGHT-SIDE-PANEL'; const NGTNS_REGEX = /ng-tns-c\d+-\d+/; let lastNgTnsClass = null; let clickDebounceTimeoutRight = null; let panelObserver = null; function clickRunSettingsButton() { if (clickDebounceTimeoutRight) { clearTimeout(clickDebounceTimeoutRight); clickDebounceTimeoutRight = null; } if (!settings.autoCollapseRightPanel) return; const button = document.querySelector(RUN_SETTINGS_BUTTON_SELECTOR); if (button) { const style = window.getComputedStyle(button); const panel = button.closest(RIGHT_PANEL_TAG_NAME); if (panel && style.display !== 'none' && style.visibility !== 'hidden' && !button.disabled) { if (getNgTnsClass(panel)) { console.log(`${SCRIPT_PREFIX} 自动折叠右侧面板: 点击 "运行设置" 按钮.`); button.click(); } } } } function getNgTnsClass(element) { if (!element || !element.classList) return null; for (const className of element.classList) { if (NGTNS_REGEX.test(className)) { return className; } } return null; } function triggerAutoCollapseRightPanelIfNeeded() { if (!settings.autoCollapseRightPanel) return; const panel = document.querySelector(RIGHT_PANEL_TAG_NAME); if (panel) { const currentNgTnsClass = getNgTnsClass(panel); if (currentNgTnsClass && (!lastNgTnsClass || currentNgTnsClass !== lastNgTnsClass)) { lastNgTnsClass = currentNgTnsClass; if (clickDebounceTimeoutRight) clearTimeout(clickDebounceTimeoutRight); clickDebounceTimeoutRight = setTimeout(clickRunSettingsButton, 300); } else if (!currentNgTnsClass && lastNgTnsClass) { lastNgTnsClass = null; } } else { lastNgTnsClass = null; } } const panelObserverCallback = function(mutationsList, observer) { if (!settings.autoCollapseRightPanel) return; let panelPotentiallyChanged = false; for (const mutation of mutationsList) { if (mutation.type === 'attributes' && mutation.attributeName === 'class' && mutation.target.tagName === RIGHT_PANEL_TAG_NAME) { const targetPanel = mutation.target; const currentNgTnsClass = getNgTnsClass(targetPanel); if (currentNgTnsClass && currentNgTnsClass !== lastNgTnsClass) { lastNgTnsClass = currentNgTnsClass; panelPotentiallyChanged = true; break; } else if (!currentNgTnsClass && lastNgTnsClass) { lastNgTnsClass = null; } } else if (mutation.type === 'childList' && mutation.addedNodes.length > 0) { for (const node of mutation.addedNodes) { if (node.nodeType === Node.ELEMENT_NODE) { let potentialPanel = null; if (node.tagName === RIGHT_PANEL_TAG_NAME) potentialPanel = node; else if (node.querySelector) potentialPanel = node.querySelector(RIGHT_PANEL_TAG_NAME); if (potentialPanel) { const currentNgTnsClass = getNgTnsClass(potentialPanel); if (currentNgTnsClass && (!lastNgTnsClass || currentNgTnsClass !== lastNgTnsClass) ) { lastNgTnsClass = currentNgTnsClass; panelPotentiallyChanged = true; } if (panelPotentiallyChanged) break; } } } if (panelPotentiallyChanged) break; } } if (panelPotentiallyChanged) { if (clickDebounceTimeoutRight) clearTimeout(clickDebounceTimeoutRight); clickDebounceTimeoutRight = setTimeout(clickRunSettingsButton, 300); } }; function initializePanelObserver() { if (panelObserver) panelObserver.disconnect(); const observerConfig = { attributes: true, attributeFilter: ['class'], childList: true, subtree: true }; panelObserver = new MutationObserver(panelObserverCallback); panelObserver.observe(document.body, observerConfig); console.log(`${SCRIPT_PREFIX} 右侧面板 MutationObserver 已启动.`); } // --- 自动折叠左侧面板逻辑 --- const LEFT_PANEL_TOGGLE_BUTTON_SELECTOR = '.navbar-content-wrapper button[aria-label="Expand or collapse navigation menu"], .navbar-content-wrapper button[aria-label="展开或收起导航菜单"]'; const LEFT_PANEL_EXPANDED_CLASS = 'sidenav-opened'; let clickDebounceTimeoutLeft = null; function clickLeftPanelToggleButton() { if (clickDebounceTimeoutLeft) { clearTimeout(clickDebounceTimeoutLeft); clickDebounceTimeoutLeft = null; } if (!settings.autoCollapseLeftPanel) { return; } const button = document.querySelector(LEFT_PANEL_TOGGLE_BUTTON_SELECTOR); if (button && document.body.classList.contains(LEFT_PANEL_EXPANDED_CLASS)) { console.log(`${SCRIPT_PREFIX} 自动折叠左侧面板: 点击切换按钮.`); button.click(); } else if (!button) { console.log(`${SCRIPT_PREFIX} 自动折叠左侧: 未找到切换按钮.`); } } function triggerAutoCollapseLeftPanelIfNeeded() { if (!settings.autoCollapseLeftPanel) return; if (clickDebounceTimeoutLeft) clearTimeout(clickDebounceTimeoutLeft); clickDebounceTimeoutLeft = setTimeout(clickLeftPanelToggleButton, 200); } // --- 脚本初始化 --- function initializeScript() { console.log(`${SCRIPT_PREFIX} 正在运行初始化...`); updateStyles(); registerMenuCommands(); initializePanelObserver(); setTimeout(() => { if (settings.autoCollapseLeftPanel) { triggerAutoCollapseLeftPanelIfNeeded(); } }, 1500); setTimeout(() => { if (settings.autoCollapseRightPanel) { const panel = document.querySelector(RIGHT_PANEL_TAG_NAME); if (panel) { const initialNgTns = getNgTnsClass(panel); if (initialNgTns) { lastNgTnsClass = initialNgTns; triggerAutoCollapseRightPanelIfNeeded(); } else { lastNgTnsClass = null; } } else { lastNgTnsClass = null; } } }, 1800); console.log(`${SCRIPT_PREFIX} 初始化完成.`); } // --- 启动脚本 --- if (document.readyState === 'loading') { document.addEventListener('DOMContentLoaded', initializeScript); } else { initializeScript(); } window.addEventListener('unload', () => { if (panelObserver) { panelObserver.disconnect(); console.log(`${SCRIPT_PREFIX} 右侧面板 MutationObserver 已断开.`); } if (clickDebounceTimeoutRight) clearTimeout(clickDebounceTimeoutRight); if (clickDebounceTimeoutLeft) clearTimeout(clickDebounceTimeoutLeft); const notification = document.getElementById('enhancer-notification'); if (notification && notification.timeoutId) { clearTimeout(notification.timeoutId); } }); })();
QingJ © 2025
镜像随时可能失效,请加Q群300939539或关注我们的公众号极客氢云获取最新地址