Google AI Studio 增强

提供护眼样式、元素显隐控制和自动折叠左右侧面板功能,优化 Google AI Studio 使用体验。

  1. // ==UserScript==
  2. // @name Google AI Studio Enhancer
  3. // @name:zh-CN Google AI Studio 增强
  4. // @namespace http://tampermonkey.net/
  5. // @version 2.3.2
  6. // @description Eye-Friendly Styles, Element Control, Auto Collapse Panels.
  7. // @description:zh-CN 提供护眼样式、元素显隐控制和自动折叠左右侧面板功能,优化 Google AI Studio 使用体验。
  8. // @author Claude 3.5 Sonnet & Gemini 2.0 Flash Thinking Experimental 01-21 & Gemini 2.5 Pro Preview 03-25
  9. // @match https://aistudio.google.com/prompts/*
  10. // @match https://aistudio.google.com/*/prompts/*
  11. // @grant GM_addStyle
  12. // @grant GM_getValue
  13. // @grant GM_setValue
  14. // @grant GM_registerMenuCommand
  15. // @grant GM_unregisterMenuCommand
  16. // @license MIT
  17. // ==/UserScript==
  18.  
  19. (function() {
  20. 'use strict';
  21.  
  22. console.log('[AI Studio Enhancer+] Initializing v2.1...');
  23. // --- Default Settings ---
  24. const defaultSettings = {
  25. useCustomStyles: true,
  26. showUserPrompts: true,
  27. showThinkingProcess: true,
  28. showAIMessages: true,
  29. showInputBox: true,
  30. showTopNavBar: true,
  31. showChatTitleBar: true,
  32. showTokenCount: true,
  33. autoCollapseRightPanel: false,
  34. autoCollapseLeftPanel: false
  35. };
  36.  
  37. // --- Initialize Settings ---
  38. let settings = {};
  39. for (const key in defaultSettings) {
  40. settings[key] = GM_getValue(key, defaultSettings[key]);
  41. if (GM_getValue(key) === undefined) {
  42. GM_setValue(key, defaultSettings[key]);
  43. console.log(`[AI Studio Enhancer+] Initialized new setting: ${key} = ${defaultSettings[key]}`);
  44. }
  45. }
  46. console.log('[AI Studio Enhancer+] Current Settings:', settings);
  47.  
  48.  
  49. // --- Menu Definition ---
  50. var menu_ALL = [
  51. [
  52. "useCustomStyles",
  53. "Custom Styles",
  54. ],
  55. [
  56. "autoCollapseLeftPanel",
  57. "Auto Collapse Left Panel",
  58. ],
  59. [
  60. "autoCollapseRightPanel",
  61. "Auto Collapse Right Panel",
  62. ],
  63. [
  64. "showTopNavBar",
  65. "Top Navigation Bar Display",
  66. ],
  67. [
  68. "showChatTitleBar",
  69. "Chat Title Bar Display",
  70. ],
  71. [
  72. "showUserPrompts",
  73. "User Messages Display",
  74. ],
  75. [
  76. "showThinkingProcess",
  77. "Thinking Process Display",
  78. ],
  79. [
  80. "showAIMessages",
  81. "AI Messages Display",
  82. ],
  83. [
  84. "showInputBox",
  85. "Input Box Display",
  86. ],
  87. [
  88. "toggleAllDisplays", // Special key for toggle all visibility settings
  89. "Toggle All Displays",
  90. ],
  91. ];
  92. var menu_ID = []; // Array to store menu command IDs for unregistering
  93.  
  94. // --- CSS Styles ---
  95. const customStyles = `
  96. .chunk-editor-main {
  97. background: #e6e5e0 !important;
  98. font-size: 2em !important;
  99. }
  100. .chunk-editor-main p {
  101. font-family: "Times New Roman", "思源宋体", "思源宋体 CN" !important;
  102. }
  103. .user-prompt-container .text-chunk {
  104. background: #d6d5b7 !important;
  105. }
  106. .model-prompt-container {
  107. background: #f3f2ee !important;
  108. padding: 15px !important;
  109. border-radius: 16px !important;
  110. }
  111. .model-prompt-container:has(.mat-accordion) {
  112. background: none !important;
  113. }
  114. .turn-footer {
  115. font-size: 10px !important;
  116. background: none !important;
  117. }
  118. .user-prompt-container p {
  119. font-size: 15px !important;
  120. line-height: 1.3 !important;
  121. }
  122. .model-prompt-container p {
  123. font-size: 20px !important;
  124. line-height: 2 !important;
  125. }
  126. .mat-accordion p {
  127. font-size: 15px !important;
  128. }
  129.  
  130. .page-header {
  131. height: 50px !important;
  132. }
  133. .top-nav {
  134. font-size: .1em !important;
  135. }
  136. .toolbar-container {
  137. height: 40px !important;
  138. padding: 0 !important;
  139. }
  140.  
  141. .prompt-input-wrapper {
  142. padding: 5px 10px !important;
  143. }
  144. .prompt-input-wrapper-container {
  145. padding: 0 !important;
  146. font-size: .5em !important;
  147. }
  148. .prompt-chip-button {
  149. background: #eee !important;
  150. }
  151. .prompt-chip-button:hover {
  152. background: #dadada !important;
  153. }
  154. ms-thought-chunk{
  155. max-width:1300px !important;
  156. }
  157.  
  158. `;
  159.  
  160. const hideUserPromptsStyle = `.chat-turn-container.user { display: none !important; }`;
  161. const hideThinkingProcessStyle = `.chat-turn-container .thought-container {display: none !important;}`;
  162. const hideAIMessagesStyle = `.chat-turn-container.model { display: none !important; }`;
  163. const hideInputBoxStyle = `footer:has(.prompt-input-wrapper) { display: none !important; }`;
  164. const hideTopNavBarStyle = `.header-container { display: none !important; }`;
  165. const hideChatTitleBarStyle = `.toolbar-container { display: none !important; }`;
  166.  
  167. // --- Style Application Function ---
  168. function updateStyles() {
  169. // Remove existing style elements managed by this script
  170. const existingStyles = document.querySelectorAll('style[data-enhancer-style]');
  171. existingStyles.forEach(style => style.remove());
  172.  
  173. if (settings.useCustomStyles) {
  174. const styleElement = document.createElement('style');
  175. styleElement.setAttribute('data-enhancer-style', 'base');
  176. styleElement.textContent = customStyles;
  177. document.head.appendChild(styleElement);
  178. }
  179. if (!settings.showUserPrompts) {
  180. const hideUserStyle = document.createElement('style');
  181. hideUserStyle.setAttribute('data-enhancer-style', 'user-visibility');
  182. hideUserStyle.textContent = hideUserPromptsStyle;
  183. document.head.appendChild(hideUserStyle);
  184. }
  185. if (!settings.showThinkingProcess) {
  186. const hideThinkingStyle = document.createElement('style');
  187. hideThinkingStyle.setAttribute('data-enhancer-style', 'thinking-visibility');
  188. hideThinkingStyle.textContent = hideThinkingProcessStyle;
  189. document.head.appendChild(hideThinkingStyle);
  190. }
  191. if (!settings.showAIMessages) {
  192. const hideAIStyle = document.createElement('style');
  193. hideAIStyle.setAttribute('data-enhancer-style', 'ai-message-visibility');
  194. hideAIStyle.textContent = hideAIMessagesStyle;
  195. document.head.appendChild(hideAIStyle);
  196. }
  197. if (!settings.showInputBox) {
  198. const hideInputBox = document.createElement('style');
  199. hideInputBox.setAttribute('data-enhancer-style', 'input-box-visibility');
  200. hideInputBox.textContent = hideInputBoxStyle;
  201. document.head.appendChild(hideInputBox);
  202. }
  203. if (!settings.showTopNavBar) {
  204. const hideTopNav = document.createElement('style');
  205. hideTopNav.setAttribute('data-enhancer-style', 'top-nav-visibility');
  206. hideTopNav.textContent = hideTopNavBarStyle;
  207. document.head.appendChild(hideTopNav);
  208. }
  209. if (!settings.showChatTitleBar) {
  210. const hideChatTitle = document.createElement('style');
  211. hideChatTitle.setAttribute('data-enhancer-style', 'chat-title-visibility');
  212. hideChatTitle.textContent = hideChatTitleBarStyle;
  213. document.head.appendChild(hideChatTitle);
  214. }
  215. console.log('[AI Studio Enhancer+] Styles updated based on settings.');
  216. }
  217.  
  218. // --- Floating Notification Function ---
  219. function showNotification(message) {
  220. const notificationId = 'enhancer-notification';
  221. let notification = document.getElementById(notificationId);
  222. if (!notification) {
  223. notification = document.createElement('div');
  224. notification.id = notificationId;
  225. notification.style.cssText = `
  226. position: fixed;
  227. top: 20px;
  228. left: 50%;
  229. transform: translateX(-50%);
  230. background-color: rgba(0, 0, 0, 0.75);
  231. color: white;
  232. padding: 10px 20px;
  233. border-radius: 5px;
  234. z-index: 10000;
  235. opacity: 0;
  236. transition: opacity 0.5s ease-in-out;
  237. font-size: 14px;
  238. `;
  239. document.body.appendChild(notification);
  240. }
  241. if (notification.timeoutId) {
  242. clearTimeout(notification.timeoutId);
  243. }
  244. notification.textContent = message;
  245. notification.style.opacity = '1';
  246. notification.timeoutId = setTimeout(() => {
  247. notification.style.opacity = '0';
  248. setTimeout(() => {
  249. if (notification.parentNode) {
  250. notification.parentNode.removeChild(notification);
  251. }
  252. notification.timeoutId = null;
  253. }, 500);
  254. }, 1500);
  255. }
  256.  
  257.  
  258. // --- Menu Command Functions ---
  259. function registerMenuCommands() {
  260. menu_ID.forEach(id => GM_unregisterMenuCommand(id));
  261. menu_ID = [];
  262.  
  263. console.log("[AI Studio Enhancer+] Registering menu commands...");
  264. menu_ALL.forEach(item => {
  265. const settingKey = item[0];
  266. const baseMenuText = item[1];
  267.  
  268. if (settingKey === "toggleAllDisplays") {
  269. // Updated list of keys considered "display" settings
  270. const displaySettingsKeys = [
  271. "showUserPrompts",
  272. "showThinkingProcess",
  273. "showAIMessages",
  274. "showInputBox",
  275. "showTopNavBar",
  276. "showChatTitleBar"
  277. ];
  278. const allEnabled = displaySettingsKeys.every(key => settings[key]);
  279. const menuText = `${allEnabled ? "🔴 Hide All Displays" : "🟢 Show All Displays"}`;
  280. menu_ID.push(GM_registerMenuCommand(menuText, toggleAllDisplays));
  281. } else {
  282. // Check if the setting exists before trying to access it
  283. if (settings.hasOwnProperty(settingKey)) {
  284. const currentSettingValue = settings[settingKey];
  285. const menuText = `${currentSettingValue ? "🔴 Disable" : "🟢 Enable"} ${baseMenuText}`;
  286. menu_ID.push(GM_registerMenuCommand(
  287. menuText,
  288. () => menuSwitch(settingKey)
  289. ));
  290. } else {
  291. console.warn(`[AI Studio Enhancer+] Setting key "${settingKey}" not found in settings object during menu registration.`);
  292. }
  293. }
  294. });
  295. console.log("[AI Studio Enhancer+] Menu commands registered.");
  296. }
  297.  
  298. // Toggle a single setting via menu
  299. function menuSwitch(settingKey) {
  300. let newValue = !settings[settingKey];
  301. settings[settingKey] = newValue;
  302. GM_setValue(settingKey, newValue);
  303. console.log(`[AI Studio Enhancer+] Toggled ${settingKey} to ${newValue}`);
  304.  
  305. const menuItem = menu_ALL.find(item => item[0] === settingKey);
  306. if (!menuItem) {
  307. console.error(`[AI Studio Enhancer+] Could not find menu item for setting key: ${settingKey}`);
  308. return;
  309. }
  310. const baseMenuText = menuItem[1];
  311.  
  312. // Apply immediate changes based on the setting toggled
  313. // Added new keys to this check
  314. if (['useCustomStyles', 'showUserPrompts', 'showThinkingProcess', 'showAIMessages', 'showInputBox', 'showTopNavBar', 'showChatTitleBar'].includes(settingKey)) {
  315. updateStyles();
  316. } else if (settingKey === 'autoCollapseRightPanel') {
  317. if (newValue) {
  318. console.log('[AI Studio Enhancer+] Auto-collapse Right Panel enabled, attempting initial check/click.');
  319. setTimeout(triggerAutoCollapseRightPanelIfNeeded, 500); // Check right panel
  320. }
  321. } else if (settingKey === 'autoCollapseLeftPanel') { // Handle left panel toggle
  322. if (newValue) {
  323. console.log('[AI Studio Enhancer+] Auto-collapse Left Panel enabled, attempting collapse.');
  324. setTimeout(triggerAutoCollapseLeftPanelIfNeeded, 500); // Collapse left panel
  325. }
  326. // No immediate action needed if disabling left panel auto-collapse
  327. }
  328.  
  329. registerMenuCommands(); // Re-register menus to update text and emoji
  330. showNotification(`${baseMenuText} ${newValue ? 'Enabled' : 'Disabled'}`); // Show confirmation
  331. }
  332.  
  333. // Toggle all display-related settings
  334. function toggleAllDisplays() {
  335. // Updated list of keys considered "display" settings
  336. const displaySettingsKeys = [
  337. "showUserPrompts",
  338. "showThinkingProcess",
  339. "showAIMessages",
  340. "showInputBox",
  341. "showTopNavBar",
  342. "showChatTitleBar"
  343. ];
  344. const enableAll = !displaySettingsKeys.every(key => settings[key]);
  345.  
  346. console.log(`[AI Studio Enhancer+] Toggling all displays to: ${enableAll}`);
  347. displaySettingsKeys.forEach(key => {
  348. // Ensure the key exists in settings before modifying
  349. if (settings.hasOwnProperty(key)) {
  350. settings[key] = enableAll;
  351. GM_setValue(key, enableAll);
  352. } else {
  353. console.warn(`[AI Studio Enhancer+] Setting key "${key}" not found during toggleAllDisplays.`);
  354. }
  355. });
  356.  
  357. updateStyles();
  358. registerMenuCommands();
  359. showNotification(`All Displays ${enableAll ? 'Enabled' : 'Disabled'}`);
  360. }
  361.  
  362. // --- Auto Collapse Right Panel Logic ---
  363.  
  364. const RUN_SETTINGS_BUTTON_SELECTOR = '.toggles-container button[aria-label="Run settings"]';
  365. const RIGHT_PANEL_TAG_NAME = 'MS-RIGHT-SIDE-PANEL';
  366. const NGTNS_REGEX = /ng-tns-c\d+-\d+/;
  367.  
  368. let lastNgTnsClass = null;
  369. let clickDebounceTimeoutRight = null; // Renamed for clarity
  370. let panelObserver = null;
  371.  
  372. // Function to safely click the "Run settings" button if needed (Right Panel)
  373. function clickRunSettingsButton() {
  374. if (clickDebounceTimeoutRight) {
  375. clearTimeout(clickDebounceTimeoutRight);
  376. clickDebounceTimeoutRight = null;
  377. }
  378. if (!settings.autoCollapseRightPanel) return;
  379.  
  380. const button = document.querySelector(RUN_SETTINGS_BUTTON_SELECTOR);
  381. if (button) {
  382. const style = window.getComputedStyle(button);
  383. const panel = button.closest(RIGHT_PANEL_TAG_NAME);
  384. if (panel && style.display !== 'none' && style.visibility !== 'hidden' && !button.disabled) {
  385. console.log('[AI Studio Enhancer+] Auto-collapsing Right Panel: Clicking "Run settings" button.');
  386. button.click();
  387. }
  388. } else {
  389. // console.log('[AI Studio Enhancer+] Auto-collapse Right: "Run settings" button not found.');
  390. }
  391. }
  392.  
  393. // Helper to get the ng-tns class from an element
  394. function getNgTnsClass(element) {
  395. if (!element || !element.classList) return null;
  396. for (const className of element.classList) {
  397. if (NGTNS_REGEX.test(className)) {
  398. return className;
  399. }
  400. }
  401. return null;
  402. }
  403.  
  404. // Function to trigger the right panel collapse check/action
  405. function triggerAutoCollapseRightPanelIfNeeded() {
  406. if (!settings.autoCollapseRightPanel) return;
  407.  
  408. // console.log('[AI Studio Enhancer+] Checking if Right Panel auto-collapse is needed...');
  409. const panel = document.querySelector(RIGHT_PANEL_TAG_NAME);
  410. if (panel) {
  411. const currentNgTnsClass = getNgTnsClass(panel);
  412. if (!lastNgTnsClass || currentNgTnsClass !== lastNgTnsClass) {
  413. // console.log(`[AI Studio Enhancer+] Right Panel state potentially changed (or first load). Triggering click.`);
  414. lastNgTnsClass = currentNgTnsClass;
  415. if (clickDebounceTimeoutRight) clearTimeout(clickDebounceTimeoutRight);
  416. clickDebounceTimeoutRight = setTimeout(clickRunSettingsButton, 300);
  417. }
  418. } else {
  419. // console.log('[AI Studio Enhancer+] Right side panel not found during check.');
  420. lastNgTnsClass = null;
  421. }
  422. }
  423.  
  424. // --- Mutation Observer for Right Panel Changes ---
  425. const panelObserverCallback = function(mutationsList, observer) {
  426. if (!settings.autoCollapseRightPanel) return; // Only observe if right panel auto-collapse is on
  427.  
  428. let panelPotentiallyChanged = false;
  429.  
  430. for (const mutation of mutationsList) {
  431. if (mutation.type === 'attributes' &&
  432. mutation.attributeName === 'class' &&
  433. mutation.target.tagName === RIGHT_PANEL_TAG_NAME)
  434. {
  435. const targetPanel = mutation.target;
  436. const currentNgTnsClass = getNgTnsClass(targetPanel);
  437. if (currentNgTnsClass !== lastNgTnsClass) {
  438. // console.log(`[AI Studio Enhancer+] Panel Observer: NgTns class changed! (${lastNgTnsClass} -> ${currentNgTnsClass})`);
  439. lastNgTnsClass = currentNgTnsClass;
  440. panelPotentiallyChanged = true;
  441. break;
  442. }
  443. }
  444. else if (mutation.type === 'childList' && mutation.addedNodes.length > 0) {
  445. for (const node of mutation.addedNodes) {
  446. if (node.nodeType === Node.ELEMENT_NODE) {
  447. let potentialPanel = null;
  448. if (node.tagName === RIGHT_PANEL_TAG_NAME) potentialPanel = node;
  449. else if (node.querySelector) potentialPanel = node.querySelector(RIGHT_PANEL_TAG_NAME);
  450.  
  451. if (potentialPanel) {
  452. const currentNgTnsClass = getNgTnsClass(potentialPanel);
  453. // console.log(`[AI Studio Enhancer+] Panel Observer: Detected addition of ${RIGHT_PANEL_TAG_NAME} or container. NgTns: ${currentNgTnsClass}`);
  454. if (currentNgTnsClass !== lastNgTnsClass || (!lastNgTnsClass && currentNgTnsClass)) {
  455. // console.log(`[AI Studio Enhancer+] Panel Observer: Added panel has different/new NgTns class!`);
  456. lastNgTnsClass = currentNgTnsClass;
  457. panelPotentiallyChanged = true;
  458. }
  459. if(panelPotentiallyChanged) break;
  460. }
  461. }
  462. }
  463. if (panelPotentiallyChanged) break;
  464. }
  465. }
  466.  
  467. if (panelPotentiallyChanged) {
  468. // console.log('[AI Studio Enhancer+] Right Panel change detected, scheduling debounced auto-collapse click.');
  469. if (clickDebounceTimeoutRight) clearTimeout(clickDebounceTimeoutRight);
  470. clickDebounceTimeoutRight = setTimeout(clickRunSettingsButton, 300);
  471. }
  472. };
  473.  
  474. // --- Initialize Right Panel Observer ---
  475. function initializePanelObserver() {
  476. if (panelObserver) panelObserver.disconnect();
  477.  
  478. const observerConfig = {
  479. attributes: true, attributeFilter: ['class'],
  480. childList: true, subtree: true
  481. };
  482. panelObserver = new MutationObserver(panelObserverCallback);
  483. panelObserver.observe(document.body, observerConfig);
  484. console.log('[AI Studio Enhancer+] Right Panel MutationObserver started.');
  485. }
  486.  
  487. // --- Auto Collapse Left Panel Logic ---
  488. const LEFT_PANEL_TOGGLE_BUTTON_SELECTOR = '.navbar-content-wrapper button[aria-label="Expand or collapse navigation menu"]';
  489. let clickDebounceTimeoutLeft = null; // Separate debounce for left panel
  490.  
  491. // Function to safely click the Left Panel toggle button if needed
  492. function clickLeftPanelToggleButton() {
  493. // Clear any pending debounce timeout for the left panel
  494. if (clickDebounceTimeoutLeft) {
  495. clearTimeout(clickDebounceTimeoutLeft);
  496. clickDebounceTimeoutLeft = null;
  497. }
  498. // Only proceed if the setting is enabled
  499. if (!settings.autoCollapseLeftPanel) {
  500. // console.log('[AI Studio Enhancer+] Auto-collapse Left Panel is disabled, skipping click.');
  501. return;
  502. }
  503.  
  504. const button = document.querySelector(LEFT_PANEL_TOGGLE_BUTTON_SELECTOR);
  505. if (button) {
  506. // Simple check: If the button exists, assume we want to click it to ensure collapsed state.
  507. // A more robust check could involve checking if the panel is actually expanded,
  508. // e.g., by looking for a class on the body or a parent element, or the button's own state if it changes.
  509. // For simplicity, we'll just click if the button exists and the setting is on.
  510. // Clicking when already collapsed might visually do nothing or briefly flash the expand icon.
  511. console.log('[AI Studio Enhancer+] Auto-collapsing Left Panel: Clicking toggle button.');
  512. button.click();
  513. } else {
  514. console.log('[AI Studio Enhancer+] Auto-collapse Left: Toggle button not found.');
  515. }
  516. }
  517.  
  518. // Function to trigger the left panel collapse check/action
  519. function triggerAutoCollapseLeftPanelIfNeeded() {
  520. if (!settings.autoCollapseLeftPanel) return; // Exit if feature is disabled
  521.  
  522. console.log('[AI Studio Enhancer+] Checking if Left Panel auto-collapse is needed...');
  523. // Use a debounced click to avoid rapid clicks if called multiple times
  524. if (clickDebounceTimeoutLeft) clearTimeout(clickDebounceTimeoutLeft);
  525. // Add a small delay before clicking, allows UI to potentially settle
  526. clickDebounceTimeoutLeft = setTimeout(clickLeftPanelToggleButton, 200);
  527. }
  528.  
  529.  
  530. // --- Script Initialization ---
  531. function initializeScript() {
  532. console.log('[AI Studio Enhancer+] Running initialization...');
  533. updateStyles(); // Apply initial styles
  534. registerMenuCommands(); // Setup Tampermonkey menu
  535. initializePanelObserver(); // Start watching for right panel changes
  536.  
  537. // Perform initial check/click for auto-collapse after a delay (allow page load)
  538. // Use slightly different delays to avoid potential race conditions if both are enabled
  539. setTimeout(triggerAutoCollapseLeftPanelIfNeeded, 1500); // Check Left Panel after 1.5s
  540. setTimeout(triggerAutoCollapseRightPanelIfNeeded, 1800); // Check Right Panel after 1.8s
  541.  
  542. console.log('[AI Studio Enhancer+] Initialization complete.');
  543. }
  544.  
  545. // --- Start the script ---
  546. if (document.readyState === 'loading') {
  547. document.addEventListener('DOMContentLoaded', initializeScript);
  548. } else {
  549. initializeScript();
  550. }
  551.  
  552. // Optional: Cleanup observer on page unload
  553. window.addEventListener('unload', () => {
  554. if (panelObserver) {
  555. panelObserver.disconnect();
  556. console.log('[AI Studio Enhancer+] Right Panel MutationObserver disconnected.');
  557. }
  558. if (clickDebounceTimeoutRight) clearTimeout(clickDebounceTimeoutRight);
  559. if (clickDebounceTimeoutLeft) clearTimeout(clickDebounceTimeoutLeft); // Clear left panel timeout too
  560. });
  561.  
  562. })();

QingJ © 2025

镜像随时可能失效,请加Q群300939539或关注我们的公众号极客氢云获取最新地址