您需要先安装一个扩展,例如 篡改猴、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 & Claude & ChatGPT // @namespace https://example.com/aiHelp // @match *://claude.ai/* // @match *://chatgpt.com/* // @match *://gemini.google.com/* // @match https://gemini.google.com/app/* // @version 1.4.0 // @author cores (Refactored by Gemini AI) // @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 PLATFORM_HOSTS = { GEMINI: 'gemini.google.com', CLAUDE: 'claude.ai', CHATGPT: 'chatgpt.com' }; // Gemini 相关选择器和类名 const GEMINI_SELECTORS = { CODE_BLOCK: 'div.code-block', CODE_CONTENT: 'code[data-test-id="code-content"], pre code', CODE_HEADER: 'div.code-block-decoration.header-formatted.gds-title-s', ORIGINAL_BUTTONS_CONTAINER: 'div.buttons[class*="ng-star-inserted"]', COLLAPSIBLE_PANEL: '.formatted-code-block-internal-container', SIDE_PANEL: '.side-panel', // 用于排除宽屏调整 NAVIGATION_PANEL: '.navigation-panel' // 用于排除宽屏调整 }; const GEMINI_CLASSES = { CUSTOM_COPY_BUTTON: 'userscript-gemini-custom-copy-button', FOOTER_CONTAINER: 'userscript-gemini-code-block-footer', HEADER_COLLAPSE_BUTTON: 'userscript-gemini-header-collapse-btn', FOOTER_COLLAPSE_BUTTON: 'userscript-gemini-footer-collapse-btn', COLLAPSED_PANEL: 'userscript-collapsed-panel' }; const GEMINI_ATTRIBUTES = { COPY_BUTTON_PROCESSED: 'data-userscript-copy-processed', HEADER_COLLAPSE_PROCESSED: 'data-userscript-header-collapse-processed', FOOTER_COLLAPSE_PROCESSED: 'data-userscript-footer-collapse-processed' }; // Claude 相关选择器和ID const CLAUDE_SELECTORS = { SETTINGS_BUTTON: 'button[data-testid="user-menu-settings"]', MENU_CONTAINER: 'div[role="menu"], div[data-radix-menu-content]', TEXT_INPUT_ELEMENTS: 'textarea, [role="textbox"], div[contenteditable="true"]', FORM_CONTAINER: 'form[enctype="multipart/form-data"], main form, body' }; const CLAUDE_IDS = { CUSTOM_MENU_BUTTON: 'userscript-claude-custom-recents-button' }; const CLAUDE_ATTRIBUTES = { WIDTH_FIXED: 'data-userscript-width-fixed' }; // --- 工具函数 --- const Utils = { /** * 创建 Material Design 图标元素 (用于 Gemini) * @param {string} iconName - 图标名称 (例如 'content_copy') * @returns {HTMLElement} mat-icon 元素 */ createMaterialIconElement: function(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; }, /** * 向文档头部添加样式 * @param {string} cssString - 要添加的 CSS 字符串 * @returns {HTMLStyleElement} 添加的 style 元素 */ addStylesToHead: function(cssString) { const styleElement = document.createElement('style'); styleElement.textContent = cssString; document.head.appendChild(styleElement); return styleElement; }, /** * 观察 DOM 变化并执行回调 * @param {Node} targetNode - 被观察的 DOM 节点 * @param {MutationObserverInit} observerOptions - MutationObserver 的配置选项 * @param {Function} callback - 当检测到变化时执行的回调函数 * @returns {MutationObserver} 创建的 MutationObserver 实例 */ observeDOMChanges: function(targetNode, observerOptions, callback) { const observer = new MutationObserver(callback); observer.observe(targetNode, observerOptions); return observer; } }; // --- 样式管理器 --- const StyleManager = { commonStyles: ` body { font-family: -apple-system, BlinkMacSystemFont, "Segoe UI", Roboto, Oxygen-Sans, Ubuntu, Cantarell, "Helvetica Neue", sans-serif !important; } `, platformStyles: '', featureStyles: '', /** * 添加平台特定的 CSS * @param {string} css - 平台 CSS */ addPlatformStyles: function(css) { this.platformStyles += css; }, /** * 添加功能特定的 CSS (例如 Gemini 按钮) * @param {string} css - 功能 CSS */ addFeatureStyles: function(css) { this.featureStyles += css; }, /** * 将所有收集到的样式应用到文档 */ applyAllStyles: function() { Utils.addStylesToHead(this.commonStyles + this.platformStyles + this.featureStyles); } }; // --- 平台模块 --- // Gemini 模块 const GeminiModule = { isCurrent: window.location.hostname.includes(PLATFORM_HOSTS.GEMINI), copyButtonProcessedAttr: GEMINI_ATTRIBUTES.COPY_BUTTON_PROCESSED, headerCollapseProcessedAttr: GEMINI_ATTRIBUTES.HEADER_COLLAPSE_PROCESSED, footerCollapseProcessedAttr: GEMINI_ATTRIBUTES.FOOTER_COLLAPSE_PROCESSED, /** * 获取 Gemini 平台的特定样式 * @returns {string} CSS 字符串 */ getStyles: function() { return ` /* Gemini 宽屏 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; } /* Gemini 代码块功能 CSS */ .${GEMINI_CLASSES.FOOTER_CONTAINER} { display: flex; justify-content: center; align-items: center; padding: 8px 0px; margin-top: 8px; gap: 8px; /* 页脚按钮之间的间距 */ } .${GEMINI_CLASSES.CUSTOM_COPY_BUTTON} { background-color: transparent; color: #5f6368; /* Google 图标颜色 */ border: none; padding: 0; width: 40px; /* Material Design 图标按钮标准尺寸 */ height: 40px; border-radius: 50%; cursor: pointer; display: inline-flex; align-items: center; justify-content: center; transition: background-color 0.2s ease, color 0.2s ease, transform 0.1s ease; outline: none; } .${GEMINI_CLASSES.CUSTOM_COPY_BUTTON} .mat-icon { font-size: 24px; /* Material Design 图标标准尺寸 */ display: flex; align-items: center; justify-content: center; line-height: 1; flex-wrap:wrap; } .${GEMINI_CLASSES.CUSTOM_COPY_BUTTON}:hover { background-color: rgba(0, 0, 0, 0.08); /* Material Design 悬停效果 */ color: #202124; /* 深色图标悬停颜色 */ } .${GEMINI_CLASSES.CUSTOM_COPY_BUTTON}:active { background-color: rgba(0, 0, 0, 0.12); /* Material Design 点击效果 */ transform: scale(0.95); } .${GEMINI_CLASSES.HEADER_COLLAPSE_BUTTON} { margin-right: 4px; /* 头部折叠按钮与原始按钮的间距 */ } .${GEMINI_CLASSES.COLLAPSED_PANEL} { display: none !important; /* 隐藏折叠的内容 */ } `; }, /** * 更新单个折叠按钮的图标和提示文本 * @param {HTMLElement} buttonElement - 按钮元素 * @param {boolean} isPanelCollapsed - 面板是否已折叠 */ updateSingleCollapseButtonIcon: function(buttonElement, isPanelCollapsed) { if (!buttonElement) return; while (buttonElement.firstChild) buttonElement.removeChild(buttonElement.firstChild); // 清空现有图标 const iconName = isPanelCollapsed ? 'keyboard_arrow_down' : 'keyboard_arrow_up'; const label = isPanelCollapsed ? '展开代码块 (Userscript)' : '收起代码块 (Userscript)'; buttonElement.appendChild(Utils.createMaterialIconElement(iconName)); buttonElement.setAttribute('aria-label', label); buttonElement.setAttribute('mattooltip', label); // Gemini 使用 mattooltip buttonElement.setAttribute('title', label); // 备用 title }, /** * 同步代码块头部和底部相关折叠按钮的图标状态 * @param {HTMLElement} codeBlockElement - 代码块元素 * @param {boolean} panelIsCollapsed - 面板是否已折叠 */ syncRelatedCollapseButtons: function(codeBlockElement, panelIsCollapsed) { const headerBtn = codeBlockElement.querySelector(`.${GEMINI_CLASSES.HEADER_COLLAPSE_BUTTON}`); const footerBtn = codeBlockElement.querySelector(`.${GEMINI_CLASSES.FOOTER_COLLAPSE_BUTTON}`); this.updateSingleCollapseButtonIcon(headerBtn, panelIsCollapsed); this.updateSingleCollapseButtonIcon(footerBtn, panelIsCollapsed); }, /** * 为 Gemini 代码块添加自定义复制按钮 * @param {HTMLElement} codeBlockElement - 代码块元素 * @returns {{copyButton: HTMLElement|null, footerDiv: HTMLElement|null}} 包含复制按钮和页脚容器的对象 */ addCustomCopyButton: function(codeBlockElement) { if (codeBlockElement.getAttribute(this.copyButtonProcessedAttr) === 'true') { const existingFooter = codeBlockElement.querySelector('.' + GEMINI_CLASSES.FOOTER_CONTAINER); const existingButton = existingFooter ? existingFooter.querySelector('.' + GEMINI_CLASSES.CUSTOM_COPY_BUTTON) : null; return { copyButton: existingButton, footerDiv: existingFooter }; } const codeContentElement = codeBlockElement.querySelector(GEMINI_SELECTORS.CODE_CONTENT); if (!codeContentElement) return { copyButton: null, footerDiv: null }; const copyButton = document.createElement('button'); copyButton.className = GEMINI_CLASSES.CUSTOM_COPY_BUTTON; copyButton.setAttribute('aria-label', '复制代码 (Userscript)'); copyButton.setAttribute('title', '复制代码 (Userscript)'); copyButton.appendChild(Utils.createMaterialIconElement('content_copy')); copyButton.addEventListener('click', async (event) => { event.stopPropagation(); // 防止事件冒泡 const codeText = codeContentElement.innerText; try { await navigator.clipboard.writeText(codeText); // 更改图标为 'check' while (copyButton.firstChild) copyButton.removeChild(copyButton.firstChild); copyButton.appendChild(Utils.createMaterialIconElement('check')); } catch (err) { console.error('Userscript: 无法复制代码', err); // 可以考虑使用一个小的提示框代替 alert alert('无法复制代码 (Userscript)'); // 出错时恢复图标 while (copyButton.firstChild) copyButton.removeChild(copyButton.firstChild); copyButton.appendChild(Utils.createMaterialIconElement('content_copy')); return; // 提前返回,不执行后续的图标恢复 } // 2.5秒后恢复图标 setTimeout(() => { if (copyButton.isConnected) { // 检查按钮是否仍在 DOM 中 while (copyButton.firstChild) copyButton.removeChild(copyButton.firstChild); copyButton.appendChild(Utils.createMaterialIconElement('content_copy')); } }, 2500); }); let footerDiv = codeBlockElement.querySelector('.' + GEMINI_CLASSES.FOOTER_CONTAINER); if (!footerDiv) { footerDiv = document.createElement('div'); footerDiv.className = GEMINI_CLASSES.FOOTER_CONTAINER; // 将页脚添加到代码块的末尾,但在可折叠面板之后(如果存在)或直接添加 const panel = codeBlockElement.querySelector(GEMINI_SELECTORS.COLLAPSIBLE_PANEL); if (panel && panel.nextSibling) { panel.parentNode.insertBefore(footerDiv, panel.nextSibling); } else if (panel) { panel.parentNode.appendChild(footerDiv); } else { codeBlockElement.appendChild(footerDiv); } } footerDiv.appendChild(copyButton); // 复制按钮始终在页脚的最后 codeBlockElement.setAttribute(this.copyButtonProcessedAttr, 'true'); return { copyButton: copyButton, footerDiv: footerDiv }; }, /** * 为 Gemini 代码块头部添加折叠/展开按钮 * @param {HTMLElement} codeBlockElement - 代码块元素 * @param {HTMLElement} panelToCollapse - 需要折叠/展开的面板元素 */ addHeaderCollapseButton: function(codeBlockElement, panelToCollapse) { if (codeBlockElement.getAttribute(this.headerCollapseProcessedAttr) === 'true' || !panelToCollapse) return; const headerDiv = codeBlockElement.querySelector(GEMINI_SELECTORS.CODE_HEADER); if (!headerDiv) return; const collapseButton = document.createElement('button'); // 沿用 Gemini 原生按钮的样式类,并添加自定义类以供识别 collapseButton.className = `mdc-icon-button mat-mdc-icon-button mat-mdc-button-base mat-mdc-tooltip-trigger ${GEMINI_CLASSES.HEADER_COLLAPSE_BUTTON}`; collapseButton.setAttribute('mat-icon-button', ''); // Gemini 按钮需要的属性 collapseButton.addEventListener('click', (event) => { event.stopPropagation(); const isCurrentlyCollapsed = panelToCollapse.classList.toggle(GEMINI_CLASSES.COLLAPSED_PANEL); this.syncRelatedCollapseButtons(codeBlockElement, isCurrentlyCollapsed); }); const existingButtonsDiv = headerDiv.querySelector(GEMINI_SELECTORS.ORIGINAL_BUTTONS_CONTAINER); if (existingButtonsDiv && existingButtonsDiv.parentNode === headerDiv) { // 插入到原生按钮组之前 headerDiv.insertBefore(collapseButton, existingButtonsDiv); } else { // 如果原生按钮组不存在,则添加到头部最前面 headerDiv.prepend(collapseButton); } codeBlockElement.setAttribute(this.headerCollapseProcessedAttr, 'true'); // 初始图标状态将在 initializeCodeBlockFeatures 中通过 syncRelatedCollapseButtons 设置 }, /** * 为 Gemini 代码块页脚添加折叠/展开按钮 * @param {HTMLElement} codeBlockElement - 代码块元素 * @param {HTMLElement} panelToCollapse - 需要折叠/展开的面板元素 * @param {HTMLElement} footerDiv - 页脚容器元素 * @param {HTMLElement} copyButtonRef - 复制按钮的引用,用于定位 */ addFooterCollapseButton: function(codeBlockElement, panelToCollapse, footerDiv, copyButtonRef) { if (codeBlockElement.getAttribute(this.footerCollapseProcessedAttr) === 'true' || !panelToCollapse || !footerDiv) return; const collapseButton = document.createElement('button'); // 页脚折叠按钮使用与复制按钮相同的基类以保持视觉一致性,并添加自己的标识类 collapseButton.className = `${GEMINI_CLASSES.CUSTOM_COPY_BUTTON} ${GEMINI_CLASSES.FOOTER_COLLAPSE_BUTTON}`; collapseButton.addEventListener('click', (event) => { event.stopPropagation(); const isCurrentlyCollapsed = panelToCollapse.classList.toggle(GEMINI_CLASSES.COLLAPSED_PANEL); this.syncRelatedCollapseButtons(codeBlockElement, isCurrentlyCollapsed); }); // 将页脚折叠按钮插入到复制按钮之前 if (copyButtonRef && copyButtonRef.parentNode === footerDiv) { footerDiv.insertBefore(collapseButton, copyButtonRef); } else { // 如果复制按钮不存在于页脚中(理论上不应发生),则添加到页脚末尾 footerDiv.appendChild(collapseButton); } codeBlockElement.setAttribute(this.footerCollapseProcessedAttr, 'true'); }, /** * 初始化单个 Gemini 代码块的功能 (复制、折叠) * @param {HTMLElement} codeBlockElement - 要初始化的代码块元素 */ initializeCodeBlockFeatures: function(codeBlockElement) { const panelToCollapse = codeBlockElement.querySelector(GEMINI_SELECTORS.COLLAPSIBLE_PANEL); // 始终先添加复制按钮,因为它会创建页脚容器(如果不存在) const { copyButton, footerDiv } = this.addCustomCopyButton(codeBlockElement); if (panelToCollapse) { this.addHeaderCollapseButton(codeBlockElement, panelToCollapse); // 确保页脚和复制按钮已成功创建后再添加页脚折叠按钮 if (footerDiv && copyButton) { this.addFooterCollapseButton(codeBlockElement, panelToCollapse, footerDiv, copyButton); } // 在所有按钮都可能已添加后,设置所有折叠按钮的初始图标状态 this.syncRelatedCollapseButtons(codeBlockElement, panelToCollapse.classList.contains(GEMINI_CLASSES.COLLAPSED_PANEL)); } }, /** * 观察并初始化页面上所有 Gemini 代码块的功能 */ observeAndInitializeCodeBlocks: function() { // 初始化已存在的代码块 document.querySelectorAll(GEMINI_SELECTORS.CODE_BLOCK).forEach(block => this.initializeCodeBlockFeatures(block)); // 观察后续动态添加的代码块 Utils.observeDOMChanges(document.body, { childList: true, subtree: true }, (mutationsList) => { mutationsList.forEach(mutation => { if (mutation.type === 'childList') { mutation.addedNodes.forEach(node => { if (node.nodeType === Node.ELEMENT_NODE) { // 检查节点本身是否是代码块 if (node.matches && node.matches(GEMINI_SELECTORS.CODE_BLOCK)) { this.initializeCodeBlockFeatures(node); } // 检查节点的子元素中是否包含代码块 node.querySelectorAll(GEMINI_SELECTORS.CODE_BLOCK).forEach(block => this.initializeCodeBlockFeatures(block)); } }); } }); }); }, /** * 应用宽屏模式到 Gemini 的内联样式元素 */ applyWidescreenToInlineStyles: function() { const elements = document.querySelectorAll('[style*="max-width"]'); elements.forEach(el => { // 排除侧边栏和导航面板,以及我们自定义的页脚中的元素 if (el.closest(GEMINI_SELECTORS.SIDE_PANEL) || el.closest(GEMINI_SELECTORS.NAVIGATION_PANEL) || el.closest(`.${GEMINI_CLASSES.FOOTER_CONTAINER}`)) { return; } // 仅修改那些看起来是主要内容区域限制的 max-width // 这个判断可能需要根据实际情况调整,避免过度修改 const currentMaxWidth = el.style.maxWidth; if (currentMaxWidth && (currentMaxWidth.includes('px') || currentMaxWidth.includes('rem') || currentMaxWidth.includes('em') || currentMaxWidth.includes('%'))) { if (parseInt(currentMaxWidth, 10) < 1200 && !currentMaxWidth.includes('95%')) { // 避免重复设置 el.style.maxWidth = '95%'; } } }); }, /** * 观察并应用宽屏模式到 Gemini 的内联样式元素 */ observeAndApplyWidescreenInlineStyles: function() { this.applyWidescreenToInlineStyles(); // 初始应用 Utils.observeDOMChanges(document.body, { childList: true, subtree: true, attributes: true, attributeFilter: ['style', 'class'] // 观察 style 和 class 变化 }, () => this.applyWidescreenToInlineStyles()); }, /** * 初始化 Gemini 平台的所有特定功能 */ init: function() { StyleManager.addFeatureStyles(this.getStyles()); // 添加 Gemini 特定样式 if (document.readyState === 'loading') { document.addEventListener('DOMContentLoaded', () => { this.observeAndInitializeCodeBlocks(); this.observeAndApplyWidescreenInlineStyles(); }); } else { this.observeAndInitializeCodeBlocks(); this.observeAndApplyWidescreenInlineStyles(); } } }; // Claude 模块 const ClaudeModule = { isCurrent: window.location.hostname.includes(PLATFORM_HOSTS.CLAUDE), jsExecuted: false, // 防止重复执行 JS /** * 获取 Claude 平台的特定样式 * @returns {string} CSS 字符串 */ getStyles: function() { return ` /* Claude 宽屏 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 特定字体修复 (如果通用字体不能完全覆盖) */ 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.6 !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; } `; }, /** * 向 Claude 用户菜单添加自定义按钮 (例如 "Recents") */ addCustomButtonToMenu: function() { const settingsButton = document.querySelector(CLAUDE_SELECTORS.SETTINGS_BUTTON); if (!settingsButton) return; // 设置按钮不存在 const menu = settingsButton.closest(CLAUDE_SELECTORS.MENU_CONTAINER); if (!menu || document.getElementById(CLAUDE_IDS.CUSTOM_MENU_BUTTON)) return; // 菜单不存在或按钮已添加 const newButton = document.createElement('button'); newButton.id = CLAUDE_IDS.CUSTOM_MENU_BUTTON; 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); } }, /** * 观察 Claude 菜单的出现并添加自定义按钮 */ observeMenuForButtonAddition: function() { // 初始尝试添加,以防菜单已存在 this.addCustomButtonToMenu(); // 观察 body 的子节点变化,因为菜单可能是动态添加到 body 直属的 Utils.observeDOMChanges(document.body, { childList: true, subtree: true }, () => { this.addCustomButtonToMenu(); }); }, /** * 修正 Claude 输入区域的宽度,确保其适应宽屏布局 */ fixInputWidths: function() { document.querySelectorAll(CLAUDE_SELECTORS.TEXT_INPUT_ELEMENTS).forEach(el => { if (el && !el.getAttribute(CLAUDE_ATTRIBUTES.WIDTH_FIXED)) { el.style.maxWidth = '100%'; // 确保输入框最大宽度为100% el.setAttribute(CLAUDE_ATTRIBUTES.WIDTH_FIXED, 'true'); } }); }, /** * 观察 Claude 输入区域的变化以修正宽度 (使用节流) */ observeAndFixInputWidths: function() { let timeoutId; const debouncedFixWidths = () => { if (timeoutId) clearTimeout(timeoutId); timeoutId = setTimeout(() => { this.fixInputWidths(); }, 300); // 300ms 防抖 }; // 初始修正 this.fixInputWidths(); const formContainer = document.querySelector(CLAUDE_SELECTORS.FORM_CONTAINER) || document.body; Utils.observeDOMChanges(formContainer, { childList: true, subtree: true, attributes: false // 通常不需要观察属性变化来修正宽度 }, debouncedFixWidths); }, /** * 初始化 Claude 平台的所有特定功能 */ init: function() { if (this.jsExecuted) return; // 防止重复初始化 StyleManager.addPlatformStyles(this.getStyles()); // 添加 Claude 特定样式 if (document.readyState === 'loading') { document.addEventListener('DOMContentLoaded', () => { this.observeMenuForButtonAddition(); this.observeAndFixInputWidths(); }); } else { this.observeMenuForButtonAddition(); this.observeAndFixInputWidths(); } this.jsExecuted = true; } }; // ChatGPT 模块 const ChatGPTModule = { isCurrent: window.location.hostname.includes(PLATFORM_HOSTS.CHATGPT), /** * 获取 ChatGPT 平台的特定样式 * @returns {string} CSS 字符串 */ getStyles: function() { return ` /* ChatGPT 宽屏 CSS */ /* 主内容区域,移除最大宽度限制 */ .text-base.gap-4.md\\:gap-6.md\\:max-w-2xl.lg\\:max-w-xl.xl\\:max-w-3xl.p-4.md\\:py-6.flex.lg\\:px-0.m-auto, .md\\:max-w-2xl.lg\\:max-w-xl.xl\\:max-w-3xl, /* 针对不同层级的容器 */ main .text-token-text-primary .w-full .max-w-agw { /* 2024-05-09 新选择器 */ max-width: 95% !important; margin-left: auto !important; margin-right: auto !important; } /* 输入区域 */ form .w-full { /* 确保表单内的输入区域也扩展 */ max-width: 95% !important; /* 或者根据实际情况调整为更具体的选择器 */ margin-left: auto !important; margin-right: auto !important; } /* 针对特定布局的微调,可能需要根据 ChatGPT UI 更新而调整 */ .stretch { width: 100% !important; /* 确保某些弹性元素完全填充 */ } /* 确保聊天记录容器也扩展 */ .h-full { /* 如果有高度限制的容器,可能也需要调整 */ /* height: 100% !important; */ /* 这个要小心,可能导致布局问题 */ } /* 针对消息输入框的父容器 */ .md\\:flex.md\\:items-end.md\\:gap-4 .w-full { max-width: 100% !important; /* 覆盖内部的 max-width */ } /* 针对包含输入框和按钮的 flex 容器 */ .relative.flex.h-full.max-w-full.flex-1.flex-col { max-width: 95% !important; margin: 0 auto !important; } `; }, /** * 初始化 ChatGPT 平台的所有特定功能 */ init: function() { StyleManager.addPlatformStyles(this.getStyles()); // 添加 ChatGPT 特定样式 // ChatGPT 目前主要通过 CSS 实现宽屏,如果未来需要 JS 功能可在此添加 } }; // --- 主逻辑:初始化和执行 --- function main() { // 确定当前平台并执行相应的初始化 if (GeminiModule.isCurrent) { GeminiModule.init(); } else if (ClaudeModule.isCurrent) { ClaudeModule.init(); } else if (ChatGPTModule.isCurrent) { ChatGPTModule.init(); } // 应用所有收集到的样式 StyleManager.applyAllStyles(); } // 执行主函数 main(); })();