Gemini & Claude & ChatGPT

Widescreen for Claude/ChatGPT/Gemini, Claude font fix & menu button. Gemini: code block copy & collapse buttons (header & footer). | 扩展三平台布局,Claude字体及菜单按钮。Gemini:代码块复制及头部/页脚折叠按钮。(重构版)

当前为 2025-05-10 提交的版本,查看 最新版本

您需要先安装一款用户脚本管理器扩展,例如 Tampermonkey 篡改猴Greasemonkey 油猴子Violentmonkey 暴力猴,才能安装此脚本。

您需要先安装一款用户脚本管理器扩展,例如 Tampermonkey 篡改猴,才能安装此脚本。

您需要先安装一款用户脚本管理器扩展,例如 Tampermonkey 篡改猴Violentmonkey 暴力猴,才能安装此脚本。

您需要先安装一款用户脚本管理器扩展,例如 Tampermonkey 篡改猴Userscripts ,才能安装此脚本。

您需要先安装一款用户脚本管理器扩展,例如 Tampermonkey 篡改猴,才能安装此脚本。

您需要先安装一款用户脚本管理器扩展后才能安装此脚本。

(我已经安装了用户脚本管理器,让我安装!)

您需要先安装一款用户样式管理器扩展,比如 Stylus,才能安装此样式。

您需要先安装一款用户样式管理器扩展,比如 Stylus,才能安装此样式。

您需要先安装一款用户样式管理器扩展,比如 Stylus,才能安装此样式。

您需要先安装一款用户样式管理器扩展后才能安装此样式。

您需要先安装一款用户样式管理器扩展后才能安装此样式。

您需要先安装一款用户样式管理器扩展后才能安装此样式。

(我已经安装了用户样式管理器,让我安装!)

// ==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();

})();