您需要先安装一个扩展,例如 篡改猴、Greasemonkey 或 暴力猴,之后才能安装此脚本。
您需要先安装一个扩展,例如 篡改猴 或 暴力猴,之后才能安装此脚本。
您需要先安装一个扩展,例如 篡改猴 或 暴力猴,之后才能安装此脚本。
您需要先安装一个扩展,例如 篡改猴 或 Userscripts ,之后才能安装此脚本。
您需要先安装一款用户脚本管理器扩展,例如 Tampermonkey,才能安装此脚本。
您需要先安装用户脚本管理器扩展后才能安装此脚本。
Widescreen for Claude/ChatGPT/Gemini, Claude font fix & menu button. Gemini: code block copy & collapse buttons (header & footer). | 扩展三平台布局,Claude字体及菜单按钮。Gemini:代码块复制及头部/页脚折叠按钮。
当前为
// ==UserScript== // @name Gemini &Gemini & chatgpt // @namespace https://example.com/aiHelp // @match *://claude.ai/* // @match *://chatgpt.com/* // @match *://gemini.google.com/* // @match https://gemini.google.com/app/* // @version 1.3.2 // @author cores // @license MIT // @description Widescreen for Claude/ChatGPT/Gemini, Claude font fix & menu button. Gemini: code block copy & collapse buttons (header & footer). | 扩展三平台布局,Claude字体及菜单按钮。Gemini:代码块复制及头部/页脚折叠按钮。 // @grant none // ==/UserScript== (function() { 'use strict'; const isGemini = window.location.hostname.includes('gemini.google.com'); const isClaude = window.location.hostname.includes('claude.ai'); const isChatGPT = window.location.hostname.includes('chatgpt.com'); const style = document.createElement('style'); const commonFontStyles = ` body { font-family: -apple-system, BlinkMacSystemFont, "Segoe UI", Roboto, Oxygen-Sans, Ubuntu, Cantarell, "Helvetica Neue", sans-serif !important; } `; let platformSpecificStyles = ''; let geminiFeaturesStyles = ''; let claudeSpecificJsExecuted = false; // --- Platform Specific Styles (保持不变) --- if (isGemini) { platformSpecificStyles += `/* Gemini wide screen CSS */ .chat-window, .chat-container, .conversation-container, .gemini-conversation-container { max-width: 95% !important; width: 95% !important; } .input-area-container, textarea, .prompt-textarea, .prompt-container { max-width: 95% !important; width: 95% !important; } textarea { width: 100% !important; } .max-w-3xl, .max-w-4xl, .max-w-screen-md { max-width: 95% !important; } .message-content, .user-message, .model-response { width: 100% !important; max-width: 100% !important; } .pre-fullscreen { height: auto !important; } .input-buttons-wrapper-top { right: 8px !important; }`; } else if (isClaude) { platformSpecificStyles += `/* Claude wide screen CSS */ .max-w-screen-md, .max-w-3xl, .max-w-4xl { max-width: 95% !important; } .w-full.max-w-3xl, .w-full.max-w-4xl { max-width: 95% !important; width: 95% !important; } .w-full.max-w-3xl textarea { width: 100% !important; } .mx-auto { max-width: 95% !important; } [data-message-author-role] { width: 100% !important; } .absolute.right-0 { right: 10px !important; } /* Claude specific font fixes */ p, h1, h2, h3, h4, h5, h6, span, div, textarea, input, button { font-family: -apple-system, BlinkMacSystemFont, "Segoe UI", Roboto, Oxygen-Sans, Ubuntu, Cantarell, "Helvetica Neue", sans-serif !important; font-weight: 400 !important; } pre, code, .font-mono { font-family: ui-monospace, SFMono-Regular, Menlo, Monaco, Consolas, "Liberation Mono", "Courier New", monospace !important; } [data-message-author-role] p { font-size: 16px !important; line-height: 1.5 !important; letter-spacing: normal !important; } h1, h2, h3, h4, h5, h6 { font-family: -apple-system, BlinkMacSystemFont, "Segoe UI", Roboto, Oxygen-Sans, Ubuntu, Cantarell, "Helvetica Neue", sans-serif !important; }`; } else if (isChatGPT) { platformSpecificStyles += `/* ChatGPT wide screen CSS */ .mx-auto { max-width: 100% !important; width: auto !important; } .h-full { height: 100% !important; } .w-full { width: 100% !important; } .message-input, .input-area input, .input-area textarea { width: 100% !important; } .h-\\[116px\\] { height: auto !important; }`; } // --- Gemini Features (Copy Button & Collapse Buttons) --- if (isGemini) { const GEMINI_CODE_BLOCK_SELECTOR = 'div.code-block'; const GEMINI_CODE_CONTENT_SELECTOR = 'code[data-test-id="code-content"], pre code'; const GEMINI_CUSTOM_COPY_BUTTON_CLASS = 'gemini-custom-md-icon-copy-button'; // For custom copy button in footer const GEMINI_FOOTER_CLASS = 'gemini-custom-code-block-footer-centered'; const ATTR_GEMINI_COPY_BUTTON_PROCESSED = 'data-gemini-copy-button-processed'; const GEMINI_CODE_HEADER_SELECTOR = 'div.code-block-decoration.header-formatted.gds-title-s'; const GEMINI_ORIGINAL_BUTTONS_CONTAINER_SELECTOR = 'div.buttons[class*="ng-star-inserted"]'; const GEMINI_COLLAPSIBLE_PANEL_SELECTOR = '.formatted-code-block-internal-container'; const ATTR_HEADER_COLLAPSE_PROCESSED = 'data-gemini-header-collapse-processed'; const ATTR_FOOTER_COLLAPSE_PROCESSED = 'data-gemini-footer-collapse-processed'; const CLASS_HEADER_COLLAPSE_BTN = 'userscript-gemini-header-collapse-btn'; const CLASS_FOOTER_COLLAPSE_BTN = 'userscript-gemini-footer-collapse-btn'; geminiFeaturesStyles = ` .${GEMINI_FOOTER_CLASS} { display: flex; justify-content: center; align-items: center; padding: 8px 0px; margin-top: 8px; gap: 8px; /* Added gap for buttons in footer */ } .${GEMINI_CUSTOM_COPY_BUTTON_CLASS} { background-color: transparent; color: #5f6368; border: none; padding: 0; width: 40px; height: 40px; border-radius: 50%; cursor: pointer; display: inline-flex; align-items: center; justify-content: center; flex-wrap: wrap; transition: background-color 0.2s ease, color 0.2s ease, transform 0.1s ease; outline: none; } .${GEMINI_CUSTOM_COPY_BUTTON_CLASS} .mat-icon { font-size: 24px; display: flex; align-items: center; justify-content: center; line-height: 1;flex-wrap:wrap; } .${GEMINI_CUSTOM_COPY_BUTTON_CLASS}:hover { background-color: rgba(0, 0, 0, 0.08); color: #202124; } .${GEMINI_CUSTOM_COPY_BUTTON_CLASS}:active { background-color: rgba(0, 0, 0, 0.12); transform: scale(0.95); } .${CLASS_HEADER_COLLAPSE_BTN} { margin-right: 4px; } /* Space for header collapse button */ /* Footer collapse button will use GEMINI_CUSTOM_COPY_BUTTON_CLASS for base style, then its own class for identification */ .${CLASS_FOOTER_COLLAPSE_BTN}.mat-mdc-icon-button { /* Ensure it matches other footer buttons if it were generic mdc */ } .userscript-collapsed-panel { display: none !important; } `; function createMaterialIconElement(iconName) { const iconElement = document.createElement('mat-icon'); iconElement.setAttribute('role', 'img'); iconElement.setAttribute('fonticon', iconName); iconElement.className = 'mat-icon notranslate google-symbols mat-ligature-font mat-icon-no-color'; iconElement.setAttribute('aria-hidden', 'true'); iconElement.setAttribute('data-mat-icon-type', 'font'); iconElement.setAttribute('data-mat-icon-name', iconName); iconElement.textContent = iconName; return iconElement; } function updateSingleCollapseButtonIcon(buttonElement, isPanelCollapsed) { if (!buttonElement) return; while (buttonElement.firstChild) buttonElement.removeChild(buttonElement.firstChild); if (isPanelCollapsed) { buttonElement.appendChild(createMaterialIconElement('keyboard_arrow_down')); buttonElement.setAttribute('aria-label', '展开代码块'); buttonElement.setAttribute('mattooltip', '展开代码块'); } else { buttonElement.appendChild(createMaterialIconElement('keyboard_arrow_up')); buttonElement.setAttribute('aria-label', '收起代码块'); buttonElement.setAttribute('mattooltip', '收起代码块'); } } function syncRelatedCollapseButtons(codeBlockElement, panelIsCollapsed) { const headerBtn = codeBlockElement.querySelector(`.${CLASS_HEADER_COLLAPSE_BTN}`); const footerBtn = codeBlockElement.querySelector(`.${CLASS_FOOTER_COLLAPSE_BTN}`); updateSingleCollapseButtonIcon(headerBtn, panelIsCollapsed); updateSingleCollapseButtonIcon(footerBtn, panelIsCollapsed); } function addGeminiCustomCopyButton(codeBlockElement) { if (codeBlockElement.getAttribute(ATTR_GEMINI_COPY_BUTTON_PROCESSED) === 'true') { const existingFooter = codeBlockElement.querySelector('.' + GEMINI_FOOTER_CLASS); const existingButton = existingFooter ? existingFooter.querySelector('.' + GEMINI_CUSTOM_COPY_BUTTON_CLASS) : null; return { copyButton: existingButton, footerDiv: existingFooter }; } const codeContentElement = codeBlockElement.querySelector(GEMINI_CODE_CONTENT_SELECTOR); if (!codeContentElement) return { copyButton: null, footerDiv: null }; const copyButton = document.createElement('button'); copyButton.className = GEMINI_CUSTOM_COPY_BUTTON_CLASS; copyButton.setAttribute('aria-label', '复制代码'); copyButton.setAttribute('title', '复制代码 (Userscript)'); copyButton.appendChild(createMaterialIconElement('content_copy')); copyButton.addEventListener('click', async (event) => { event.stopPropagation(); const codeText = codeContentElement.innerText; try { await navigator.clipboard.writeText(codeText); while (copyButton.firstChild) copyButton.removeChild(copyButton.firstChild); copyButton.appendChild(createMaterialIconElement('check')); } catch (err) { alert('无法复制代码。\nCould not copy code.'); } setTimeout(() => { if (copyButton.isConnected) { while (copyButton.firstChild) copyButton.removeChild(copyButton.firstChild); copyButton.appendChild(createMaterialIconElement('content_copy')); } }, 2500); }); let footerDiv = codeBlockElement.querySelector('.' + GEMINI_FOOTER_CLASS); if (!footerDiv) { footerDiv = document.createElement('div'); footerDiv.className = GEMINI_FOOTER_CLASS; codeBlockElement.appendChild(footerDiv); } footerDiv.appendChild(copyButton); // Append copy button first codeBlockElement.setAttribute(ATTR_GEMINI_COPY_BUTTON_PROCESSED, 'true'); return { copyButton: copyButton, footerDiv: footerDiv }; } function addGeminiHeaderCollapseButton(codeBlockElement, panelToCollapse) { if (codeBlockElement.getAttribute(ATTR_HEADER_COLLAPSE_PROCESSED) === 'true' || !panelToCollapse) return; const headerDiv = codeBlockElement.querySelector(GEMINI_CODE_HEADER_SELECTOR); if (!headerDiv) return; const existingButtonsDiv = headerDiv.querySelector(GEMINI_ORIGINAL_BUTTONS_CONTAINER_SELECTOR); const collapseButton = document.createElement('button'); collapseButton.className = `mdc-icon-button mat-mdc-icon-button mat-mdc-button-base mat-mdc-tooltip-trigger ${CLASS_HEADER_COLLAPSE_BTN}`; collapseButton.setAttribute('mat-icon-button', ''); collapseButton.addEventListener('click', (event) => { event.stopPropagation(); const isCurrentlyCollapsed = panelToCollapse.classList.toggle('userscript-collapsed-panel'); syncRelatedCollapseButtons(codeBlockElement, isCurrentlyCollapsed); }); if (existingButtonsDiv && existingButtonsDiv.parentNode === headerDiv) { headerDiv.insertBefore(collapseButton, existingButtonsDiv); } else { headerDiv.prepend(collapseButton); } codeBlockElement.setAttribute(ATTR_HEADER_COLLAPSE_PROCESSED, 'true'); // Initial icon will be set by a call to syncRelatedCollapseButtons later } function addGeminiFooterCollapseButton(codeBlockElement, panelToCollapse, footerDiv, copyButtonRef) { if (codeBlockElement.getAttribute(ATTR_FOOTER_COLLAPSE_PROCESSED) === 'true' || !panelToCollapse || !footerDiv) return; const collapseButton = document.createElement('button'); // Use GEMINI_CUSTOM_COPY_BUTTON_CLASS as base for footer buttons for visual consistency collapseButton.className = `${GEMINI_CUSTOM_COPY_BUTTON_CLASS} ${CLASS_FOOTER_COLLAPSE_BTN}`; // No need for mat-icon-button attribute if GEMINI_CUSTOM_COPY_BUTTON_CLASS doesn't imply it and handles styling collapseButton.addEventListener('click', (event) => { event.stopPropagation(); const isCurrentlyCollapsed = panelToCollapse.classList.toggle('userscript-collapsed-panel'); syncRelatedCollapseButtons(codeBlockElement, isCurrentlyCollapsed); }); // Insert footer collapse button to the left of the copy button if (copyButtonRef && copyButtonRef.parentNode === footerDiv) { footerDiv.insertBefore(collapseButton, copyButtonRef); } else { footerDiv.appendChild(collapseButton); // Fallback: append if copy button isn't found in footer } codeBlockElement.setAttribute(ATTR_FOOTER_COLLAPSE_PROCESSED, 'true'); // Initial icon will be set by a call to syncRelatedCollapseButtons later } function initializeGeminiCodeBlockFeatures(codeBlockElement) { const panelToCollapse = codeBlockElement.querySelector(GEMINI_COLLAPSIBLE_PANEL_SELECTOR); const { copyButton, footerDiv } = addGeminiCustomCopyButton(codeBlockElement); if (panelToCollapse) { addGeminiHeaderCollapseButton(codeBlockElement, panelToCollapse); if (footerDiv && copyButton) { // Ensure footer and copy button exist before adding footer collapse addGeminiFooterCollapseButton(codeBlockElement, panelToCollapse, footerDiv, copyButton); } // Set initial state for all collapse buttons after they are potentially added syncRelatedCollapseButtons(codeBlockElement, panelToCollapse.classList.contains('userscript-collapsed-panel')); } } function observeGeminiCodeBlocks() { document.querySelectorAll(GEMINI_CODE_BLOCK_SELECTOR).forEach(initializeGeminiCodeBlockFeatures); const geminiCodeBlockObserver = new MutationObserver((mutationsList) => { mutationsList.forEach(mutation => { if (mutation.type === 'childList') { mutation.addedNodes.forEach(node => { if (node.nodeType === Node.ELEMENT_NODE) { if (node.matches && node.matches(GEMINI_CODE_BLOCK_SELECTOR)) { initializeGeminiCodeBlockFeatures(node); } node.querySelectorAll(GEMINI_CODE_BLOCK_SELECTOR).forEach(initializeGeminiCodeBlockFeatures); } }); } }); }); geminiCodeBlockObserver.observe(document.body, { childList: true, subtree: true }); } if (document.readyState === 'loading') { document.addEventListener('DOMContentLoaded', observeGeminiCodeBlocks); } else { observeGeminiCodeBlocks(); } } // --- Claude Specific JavaScript (保持不变) --- if (isClaude && !claudeSpecificJsExecuted) { const CLAUDE_USER_MENU_BUTTON_ID = 'userscript-claude-custom-button'; const CLAUDE_SETTINGS_BUTTON_SELECTOR = 'button[data-testid="user-menu-settings"]'; function addCustomButtonToClaudeMenu() { const settingsButton = document.querySelector(CLAUDE_SETTINGS_BUTTON_SELECTOR); if (!settingsButton) return; const menu = settingsButton.closest('div[role="menu"], div[data-radix-menu-content]'); if (!menu || document.getElementById(CLAUDE_USER_MENU_BUTTON_ID)) return; const newButton = document.createElement('button'); newButton.id = CLAUDE_USER_MENU_BUTTON_ID; newButton.className = settingsButton.className; newButton.setAttribute('role', 'menuitem'); newButton.setAttribute('tabindex', '-1'); newButton.setAttribute('data-orientation', 'vertical'); newButton.textContent = "recents"; newButton.addEventListener('click', () => { window.location.href = 'https://claude.ai/recents'; }); if (settingsButton.parentNode) settingsButton.parentNode.insertBefore(newButton, settingsButton.nextSibling); } const claudeMenuObserver = new MutationObserver(addCustomButtonToClaudeMenu); claudeMenuObserver.observe(document.body, { childList: true, subtree: true }); let claudeInputObserver; function startClaudeInputObserver() { if (claudeInputObserver) return; claudeInputObserver = new MutationObserver(function() { if (claudeInputObserver.timeoutId) return; claudeInputObserver.timeoutId = setTimeout(function() { document.querySelectorAll('textarea, [role="textbox"], div[contenteditable="true"]').forEach(el => { if (el && !el.dataset.widthFixedForWide) { el.style.maxWidth = '100%'; el.dataset.widthFixedForWide = 'true'; } }); delete claudeInputObserver.timeoutId; }, 500); }); const claudeForm = document.querySelector('form[enctype="multipart/form-data"], main form, body'); if (claudeForm) claudeInputObserver.observe(claudeForm, { childList: true, subtree: true, attributes: false }); } if (document.readyState === 'loading') document.addEventListener('DOMContentLoaded', startClaudeInputObserver); else startClaudeInputObserver(); claudeSpecificJsExecuted = true; } // --- Append all styles to head --- style.textContent = commonFontStyles + platformSpecificStyles + geminiFeaturesStyles; document.head.appendChild(style); // --- Widescreen Logic for inline styles (Gemini - 保持不变) --- function applyWideModeToInlineStyles() { if (!isGemini) return; const elements = document.querySelectorAll('[style*="max-width"]'); elements.forEach(el => { if (el.classList.contains('side-panel') || el.classList.contains('navigation-panel')) return; if (typeof GEMINI_FOOTER_CLASS !== 'undefined' && el.closest(`.${GEMINI_FOOTER_CLASS}`)) return; el.style.maxWidth = '95%'; }); } if (document.readyState === 'loading') document.addEventListener('DOMContentLoaded', applyWideModeToInlineStyles); else applyWideModeToInlineStyles(); if (isGemini) { const geminiWidescreenObserver = new MutationObserver(applyWideModeToInlineStyles); geminiWidescreenObserver.observe(document.body, { childList: true, subtree: true, attributes: true, attributeFilter: ['style', 'class'] }); } })();