AI Chat Window Enhancer Pro

Enhanced chat window for various AI platforms: ChatGPT, Claude, Kimi, Tongyi, ChatGLM, Tiangong, Deepseek, Gemini

您需要先安裝使用者腳本管理器擴展,如 TampermonkeyGreasemonkeyViolentmonkey 之後才能安裝該腳本。

You will need to install an extension such as Tampermonkey to install this script.

您需要先安裝使用者腳本管理器擴充功能,如 TampermonkeyViolentmonkey 後才能安裝該腳本。

您需要先安裝使用者腳本管理器擴充功能,如 TampermonkeyUserscripts 後才能安裝該腳本。

你需要先安裝一款使用者腳本管理器擴展,比如 Tampermonkey,才能安裝此腳本

您需要先安裝使用者腳本管理器擴充功能後才能安裝該腳本。

(我已經安裝了使用者腳本管理器,讓我安裝!)

你需要先安裝一款使用者樣式管理器擴展,比如 Stylus,才能安裝此樣式

你需要先安裝一款使用者樣式管理器擴展,比如 Stylus,才能安裝此樣式

你需要先安裝一款使用者樣式管理器擴展,比如 Stylus,才能安裝此樣式

你需要先安裝一款使用者樣式管理器擴展後才能安裝此樣式

你需要先安裝一款使用者樣式管理器擴展後才能安裝此樣式

你需要先安裝一款使用者樣式管理器擴展後才能安裝此樣式

(我已經安裝了使用者樣式管理器,讓我安裝!)

// ==UserScript==
// @name         AI Chat Window Enhancer Pro
// @name:zh-CN   AI对话窗口增强专业版
// @namespace    http://tampermonkey.net/
// @version      0.3.4
// @description  Enhanced chat window for various AI platforms: ChatGPT, Claude, Kimi, Tongyi, ChatGLM, Tiangong, Deepseek, Gemini
// @description:zh-CN  为主流AI平台优化对话窗口体验:支持ChatGPT、Claude、Kimi、通义千问、智谱GLM、天工、Deepseek、Gemini
// @author       Claude
// @match        *://chatgpt.com/*
// @match        *://new.oaifree.com/*
// @match        *://shared.oaifree.com/*
// @match        *://www.aicnn.cn/oaifree/*
// @match        *://chat.aicnn.xyz/*
// @match        *://plus.aivvm.com/*
// @match        *://kimi.moonshot.cn/*
// @match        *://tongyi.aliyun.com/qianwen*
// @match        *://www.tiangong.cn/*
// @match        *://chatglm.cn/*
// @match        *://claude.ai/*
// @match        *://chat.deepseek.com/*
// @include      *://*claude*/*
// @match        *://chat.kelaode.ai/*
// @match        *://gemini.google.com/*
// @icon         data:image/svg+xml;base64,PHN2ZyB2aWV3Qm94PSIwIDAgMjQgMjQiIHhtbG5zPSJodHRwOi8vd3d3LnczLm9yZy8yMDAwL3N2ZyI+CiAgPHJlY3Qgd2lkdGg9IjI0IiBoZWlnaHQ9IjI0IiByeD0iNSIgcnk9IjUiIGZpbGw9IiM2YmIiLz4KICA8cGF0aCBkPSJtMTcgMTMuNyA0LTQtNC00bS0xMCA4LTQtNCA0LTRtMTMuOTk5IDMuODI1SDMuMjE1Ii8+CiAgPHBhdGggZD0iTTE5IDE2aC0yLjVhLjk5LjkgMCAwIDAtLjc3NS4zNzVsLTIuOSAzLjY1Yy0uNC41LTEuMTYyLjUtMS41NjMgMGwtMi45MjUtMy42NUEuOTkuOSAwIDAgMCA3LjUgMTZINWMtMS42NjMgMC0zLTEuMzM4LTMtM1Y2YzAtMS42NjIgMS4zNS0zIDMtM2gxNGEzIDMgMCAwIDEgMyAzdjdjMCAxLjY2Mi0xLjM1IDMtMyAzWiIgZmlsbC1vcGFjaXR5PSIuMTYiIHN0cm9rZT0iI0VFRSIgc3Ryb2tlLXdpZHRoPSIxLjUiIHN0cm9rZS1taXRlcmxpbWl0PSIxMCIvPgo8L3N2Zz4K
// @license      AGPL-3.0
// @grant        GM_addStyle
// @noframes
// ==/UserScript==

(function() {
    'use strict';

    // 统一的样式配置
    const STYLE_CONFIG = {
        maxWidth: '95%',
        maxViewportWidth: '90vw',
        scrollbarWidth: 'thin',
        scrollbarThumbColor: '#aaaa',
        scrollbarTrackColor: '#1111',
        codeBlockScrollbarHeight: '8px',
        codeBlockScrollbarThumbColor: '#666',
        codeBlockScrollbarTrackColor: '#f1f1f1',
        // 代码块选中样式
        codeSelectionBgColor: 'rgba(70, 130, 180, 0.5)',     // 深色主题选中背景色
        codeSelectionTextColor: 'white',                     // 深色主题选中文本色
        lightCodeSelectionBgColor: 'rgba(0, 120, 215, 0.3)', // 浅色主题选中背景色
        lightCodeSelectionTextColor: 'black'                 // 浅色主题选中文本色
    };

    // 通用代码块样式
    const CODE_BLOCK_STYLES = `
        /* 代码块样式优化 */
        pre > div.rounded-md,
        .code-block__code {
            min-height: 1.5em;
            height: auto !important;
        }
        /* 保持代码块的水平滚动,移除纵向限制 */
        pre > div.rounded-md > div.overflow-y-auto,
        .code-block__code {
            max-height: none !important;
            height: auto !important;
            overflow-y: visible !important;
            overflow-x: auto !important;
        }
        /* 移除代码块的折叠按钮 */
        button[class*="code-block-collapse-button"],
        div[class*="code-block-collapse"] {
            display: none !important;
        }
        /* 确保代码块始终展开 */
        div[class*="code-block-wrapper"].collapsed {
            max-height: none !important;
            height: auto !important;
        }
        /* 优化代码块滚动条样式 */
        pre > div.rounded-md > div.overflow-y-auto::-webkit-scrollbar,
        .code-block__code::-webkit-scrollbar {
            height: ${STYLE_CONFIG.codeBlockScrollbarHeight};
            width: ${STYLE_CONFIG.codeBlockScrollbarHeight};
        }
        pre > div.rounded-md > div.overflow-y-auto::-webkit-scrollbar-thumb,
        .code-block__code::-webkit-scrollbar-thumb {
            background: ${STYLE_CONFIG.codeBlockScrollbarThumbColor};
            border-radius: 4px;
        }
        pre > div.rounded-md > div.overflow-y-auto::-webkit-scrollbar-track,
        .code-block__code::-webkit-scrollbar-track {
            background: ${STYLE_CONFIG.codeBlockScrollbarTrackColor};
        }

        /* 代码块文本选中样式 - 适用于深色背景 */
        pre code::selection,
        .code-block__code code::selection,
        pre div::selection,
        .code-block__code div::selection,
        pre span::selection,
        .code-block__code span::selection,
        div[class*="codeBlockContainer"] *::selection,
        div[class*="code-block"] *::selection {
            background-color: ${STYLE_CONFIG.codeSelectionBgColor} !important;
            color: ${STYLE_CONFIG.codeSelectionTextColor} !important;
        }

        /* 浅色背景代码块的选中样式 */
        pre.bg-white code::selection,
        pre.bg-gray-50 code::selection,
        pre.bg-slate-50 code::selection,
        .light-theme pre code::selection,
        .light pre code::selection {
            background-color: ${STYLE_CONFIG.lightCodeSelectionBgColor} !important;
            color: ${STYLE_CONFIG.lightCodeSelectionTextColor} !important;
        }

        /* 提升代码块hover时的可识别性 */
        pre:hover,
        .code-block__code:hover,
        div[class*="codeBlockContainer"]:hover,
        div[class*="code-block"]:hover {
            box-shadow: 0 0 0 1px rgba(255, 255, 255, 0.1);
            transition: box-shadow 0.2s ease-in-out;
        }
    `;

    // 平台特定的样式定义
    const PLATFORM_STYLES = {
        kimi: `
            div[data-testid] div[data-index] div.MuiBox-root {
                max-width: 100% !important;
            }
            div[class^=mainContent] div.MuiBox-root > div[class^=chatBottom_] {
                max-width: calc(100% - 100px);
            }
            div[class^=mainContent] div[class^=chatInput_] div[class^=inputInner_] div[class^=editor] {
                max-height: 360px;
            }
            #scroll-list div[class^=chatItemBox_].MuiBox-root {
                max-width: 100%;
            }
            div.MuiBox-root[class^=homepage] div[class^=mainContent] div[class^=chatInput_] div[class^=inputInner_] div[class^=editor] {
                max-height: 600px;
            }
            #root > div > div[class*=mainContent] > div[class*=layoutContent] > div.MuiBox-root > div.MuiBox-root[class*=homepage] > div.MuiContainer-root.MuiContainer-maxWidthMd {
                max-width: calc(100% - 100px);
            }
            ${CODE_BLOCK_STYLES}
        `,
        deepseek: `
            div:has(> #latest-context-divider) {
                width: ${STYLE_CONFIG.maxWidth} !important;
            }
            div:has(> div > #chat-input) {
                width: ${STYLE_CONFIG.maxWidth} !important;
                max-width: ${STYLE_CONFIG.maxViewportWidth};
            }
            ${CODE_BLOCK_STYLES}
        `,
        tongyi: `
            div[class^=mainContent] div[class^=questionItem--],
            div[class^=mainContent] div[class^=answerItem--] {
                width: 90% !important;
                max-width: ${STYLE_CONFIG.maxViewportWidth};
            }
            ${CODE_BLOCK_STYLES}
        `,
        tiangong: `
            #app > div > div > main > div.overflow-y-scroll.w-full > div.search-content.relative.flex.w-full.flex-row.justify-center,
            #app > div > div > main > div.overflow-y-scroll.w-full > div.search-content.relative.flex.w-full.flex-row.justify-center > label.w-full.cursor-default.select-auto,
            label.w-full {
                max-width: calc(100% - 100px);
                --search-max-width: calc(100% - 100px);
            }
            :root {
                --search-max-width: calc(100% - 100px);
            }
            ${CODE_BLOCK_STYLES}
        `,
        chatglm: `
            div.conversation-inner.dialogue > div.conversation-list.detail > div.item.conversation-item,
            .markdown-body.md-body {
                max-width: ${STYLE_CONFIG.maxViewportWidth} !important;
            }
            ${CODE_BLOCK_STYLES}
        `,
        gemini: `
            #chat-history > infinite-scroller > div,
            #app-root > main > side-navigation-v2 > bard-sidenav-container > bard-sidenav-content > div > div > div.content-container > chat-window > div.chat-container.ng-star-inserted > div.bottom-container.response-optimization.ng-star-inserted,
            #app-root > main > side-navigation-v2 > bard-sidenav-container > bard-sidenav-content > div > div > div.content-container > chat-window > div.chat-container.ng-star-inserted > div.bottom-container.response-optimization.ng-star-inserted > div.input-area-container.ng-star-inserted {
                max-width: calc(100% - 20px);
            }
            ${CODE_BLOCK_STYLES}
        `,
        default: `
            .xl\\:max-w-\\[48rem\\] {
                width: ${STYLE_CONFIG.maxWidth} !important;
                max-width: 96% !important;
            }
            div.mx-auto.md\\:max-w-3xl,
            div.mx-auto.flex {
                max-width: calc(100% - 10px);
            }
            ${CODE_BLOCK_STYLES}
            .ProseMirror.break-words.ProseMirror-focused {
                max-width: 100%;
            }
            body > div.flex.min-h-screen.w-full div.flex.flex-col div.flex.gap-2 div.mt-1.max-h-96.w-full.overflow-y-auto.break-words > div.ProseMirror.break-words {
                max-width: 90%;
            }
            main > div.composer-parent article > div.text-base > div.mx-auto,
            main article > div.text-base > div.mx-auto {
                max-width: ${STYLE_CONFIG.maxWidth};
            }
            body > div.flex.min-h-screen.w-full > div > main > div.top-5.z-10.mx-auto.w-full.max-w-2xl.md,
            body > div.flex.min-h-screen.w-full > div > main > div.mx-auto.w-full.max-w-2xl.px-1.md {
                max-width: 100%;
            }
            body > div.flex.min-h-screen.w-full > div > main.max-w-7xl {
                max-width: 90rem;
            }
        `
    };

    // 平台映射配置
    const PLATFORM_MAP = {
        'kimi.moonshot.cn': 'kimi',
        'chat.deepseek.com': 'deepseek',
        'tongyi.aliyun.com': 'tongyi',
        'tiangong.cn': 'tiangong',
        'chatglm.cn': 'chatglm',
        'gemini.google.com': 'gemini'
    };

    // 检测当前平台并应用相应样式
    function applyPlatformStyles() {
        try {
            const host = window.location.hostname;
            let platformKey = 'default';
            
            // 使用平台映射检测当前平台
            for (const [domain, key] of Object.entries(PLATFORM_MAP)) {
                if (host.includes(domain)) {
                    platformKey = key;
                    break;
                }
            }
            
            // 应用对应平台的样式
            const styleToApply = PLATFORM_STYLES[platformKey] || PLATFORM_STYLES.default;
            GM_addStyle(styleToApply);
            
            console.log(`[AI Chat Enhancer] Applied styles for platform: ${platformKey}`);
        } catch (error) {
            console.error('[AI Chat Enhancer] Error applying styles:', error);
        }
    }

    // 优化链接处理
    function enhanceLinks() {
        // 使用防抖避免频繁调用
        if (enhanceLinks.timer) {
            clearTimeout(enhanceLinks.timer);
        }
        
        try {
            const links = document.querySelectorAll('div[data-message-id] a[rel="noreferrer"]');
            
            if (links.length > 0) {
                links.forEach(link => {
                    if (!link.href && link.innerText && link.innerText.trim()) {
                        const linkText = link.innerText.trim();
                        // 仅处理有效链接文本
                        if (linkText.startsWith('http') || linkText.includes('www.')) {
                            link.href = linkText;
                            link.target = "_blank";
                            link.rel = "noopener noreferrer";
                        }
                    }
                });
            }
        } catch (error) {
            console.error('[AI Chat Enhancer] Error enhancing links:', error);
        }
        
        // 使用MutationObserver替代递归调用,降低性能消耗
        enhanceLinks.timer = setTimeout(() => {
            if (!enhanceLinks.observer) {
                // 创建观察器,监听DOM变化
                enhanceLinks.observer = new MutationObserver((mutations) => {
                    const shouldProcess = mutations.some(mutation => 
                        mutation.addedNodes.length > 0 || 
                        (mutation.type === 'attributes' && mutation.attributeName === 'href')
                    );
                    
                    if (shouldProcess) {
                        enhanceLinks();
                    }
                });
                
                // 开始观察文档变化
                enhanceLinks.observer.observe(document.body, {
                    childList: true,
                    subtree: true,
                    attributes: true,
                    attributeFilter: ['href']
                });
            }
        }, 2000);
    }

    // 初始化
    function init() {
        // 在DOMContentLoaded后执行
        if (document.readyState === 'loading') {
            document.addEventListener('DOMContentLoaded', () => {
                applyPlatformStyles();
                enhanceLinks();
            });
        } else {
            // 文档已经加载完成
            applyPlatformStyles();
            enhanceLinks();
        }
        
        // 监听页面变化,适应单页应用
        window.addEventListener('popstate', applyPlatformStyles);
        window.addEventListener('pushstate', applyPlatformStyles);
        window.addEventListener('replacestate', applyPlatformStyles);
    }

    // 启动脚本
    init();
})();