AI助手選擇器 / AI Assistant Selector

一個 Tampermonkey 腳本,提供浮動介面整合多款 AI 助手,支援右鍵呼叫、清空歷史訊息、可調整尺寸,並記錄 AI 選擇狀態與位置,修復 ChatGPT 回應監聽與 UI 更

目前为 2025-03-01 提交的版本。查看 最新版本

// ==UserScript==
// @name         AI助手選擇器 / AI Assistant Selector
// @name:en      AI Assistant Selector
// @namespace    http://tampermonkey.net/
// @version      3.0
// @description  一個 Tampermonkey 腳本,提供浮動介面整合多款 AI 助手,支援右鍵呼叫、清空歷史訊息、可調整尺寸,並記錄 AI 選擇狀態與位置,修復 ChatGPT 回應監聽與 UI 更
// @description:en A Tampermonkey script that provides a floating interface to integrate multiple AI assistants, supporting right-click invocation, clearing history messages, resizable UI, and recording AI selection state and position, with fixes for ChatGPT response monitoring and UI updates
// @author       Your name
// @match        *://*/*
// @match        *://grok.com/*
// @match        *://chatgpt.com/*
// @icon         data:image/gif;base64,R0lGODlhAQABAAAAACH5BAEKAAEALAAAAAABAAEAAAICTAEAOw==
// @grant        GM_addStyle
// @grant        GM_setValue
// @grant        GM_getValue
// @grant        GM_xmlhttpRequest
// @grant        GM_openInTab
// @license      MIT
// ==/UserScript==

(function() {
    'use strict';

    // 樣式定義
    const styles = `
        .ai-selector-container {
            position: fixed;
            background: #2c2c2c;
            padding: 15px;
            border-radius: 10px;
            z-index: 9999;
            min-width: 200px;
            min-height: 200px;
            top: ${GM_getValue('containerTop', '50px')};
            left: ${GM_getValue('containerLeft', '50px')};
            box-shadow: 0 4px 6px rgba(0,0,0,0.1);
            color: white;
            font-family: Arial, sans-serif;
            resize: both;
            overflow: auto;
        }
        .ai-selector-bubble {
            position: fixed;
            width: 40px;
            height: 40px;
            background: #666;
            border-radius: 50%;
            display: flex;
            align-items: center;
            justify-content: center;
            color: white;
            font-size: 16px;
            font-weight: bold;
            cursor: move;
            z-index: 10000;
            box-shadow: 0 2px 4px rgba(0,0,0,0.2);
        }
        .ai-selector-header {
            display: flex;
            justify-content: space-between;
            align-items: center;
            margin-bottom: 10px;
            cursor: move;
            background: #3a3a3a;
            padding: 5px 10px;
            border-radius: 5px;
        }
        .ai-selector-title {
            font-size: 16px;
            font-weight: bold;
        }
        .ai-selector-minimize {
            cursor: pointer;
            padding: 5px;
        }
        .ai-selector-content {
            display: none;
            flex-direction: column;
            gap: 10px;
        }
        .ai-option {
            display: flex;
            align-items: center;
            padding: 8px;
            border-radius: 5px;
            cursor: pointer;
            transition: background 0.3s;
        }
        .ai-option:hover {
            background: rgba(255,255,255,0.1);
        }
        .ai-option.selected {
            background: #4285f4;
        }
        .ai-name {
            margin-left: 10px;
        }
        .question-input {
            width: 100%;
            padding: 8px;
            border-radius: 5px;
            border: 1px solid #444;
            background: #1c1c1c;
            color: white;
            margin-top: 10px;
            box-sizing: border-box;
        }
        .send-button {
            width: 100%;
            padding: 8px;
            border-radius: 5px;
            border: none;
            background: #4285f4;
            color: white;
            cursor: pointer;
            margin-top: 10px;
        }
        .send-button:hover {
            background: #5294ff;
        }
        .send-button:disabled {
            background: #666;
            cursor: not-allowed;
        }
        .clear-button {
            width: 100%;
            padding: 8px;
            border-radius: 5px;
            border: none;
            background: #e74c3c;
            color: white;
            cursor: pointer;
            margin-bottom: 10px;
        }
        .clear-button:hover {
            background: #ff6655;
        }
        .grok-response-container, .chatgpt-response-container {
            position: fixed;
            background: #2c2c2c;
            padding: 15px;
            border-radius: 10px;
            z-index: 9998;
            min-width: 200px;
            min-height: 200px;
            top: ${GM_getValue('grokResponseTop', '100px')};
            left: ${GM_getValue('grokResponseLeft', '100px')};
            box-shadow: 0 4px 6px rgba(0,0,0,0.1);
            color: white;
            font-family: Arial, sans-serif;
            resize: both;
            overflow-y: auto;
            display: flex;
            flex-direction: column;
            gap: 10px;
        }
        .grok-response-header, .chatgpt-response-header {
            display: flex;
            justify-content: space-between;
            align-items: center;
            background: #3a3a3a;
            padding: 5px 10px;
            border-radius: 5px;
            cursor: move;
            flex-shrink: 0;
            margin-bottom: 0;
        }
        .grok-response-title, .chatgpt-response-title {
            font-size: 16px;
            font-weight: bold;
        }
        .grok-response-close, .chatgpt-response-close {
            cursor: pointer;
            padding: 5px;
        }
        .grok-response, .chatgpt-response {
            padding: 10px;
            background: #1c1c1c;
            border-radius: 5px;
            border: 1px solid #444;
            color: white;
            white-space: pre-wrap;
            flex-grow: 1;
        }
        .grok-response p, .chatgpt-response p {
            margin: 5px 0;
            padding: 5px;
            background: #333;
            border-radius: 3px;
        }
    `;

    // AI助手配置
    const AIs = [
        { id: 'gemini', name: 'Gemini', url: 'https://gemini.google.com/app', inputSelector: 'rich-textarea.text-input-field_textarea', color: '#8e44ad' },
        { id: 'grok', name: 'Grok', url: 'https://grok.com/', color: '#e74c3c' },
        { id: 'chatgpt', name: 'ChatGPT', url: 'https://chatgpt.com/', color: '#27ae60' },
        { id: 'perplexity', name: 'Perplexity', url: 'https://www.perplexity.ai/', color: '#3498db' }
    ];

    // 添加樣式
    GM_addStyle(styles);

    // 拖動功能
    function makeDraggable(element, handle, saveKeyPrefix) {
        let pos1 = 0, pos2 = 0, pos3 = 0, pos4 = 0;
        let isDragging = false;
        let moved = false;

        const dragHandle = handle || element;

        dragHandle.addEventListener('mousedown', dragStart);

        function dragStart(e) {
            const rect = element.getBoundingClientRect();
            const isBottomRight = e.clientX > rect.right - 20 && e.clientY > rect.bottom - 20;
            if (isBottomRight && element.style.resize === 'both') {
                return;
            }

            e.preventDefault();
            pos3 = e.clientX;
            pos4 = e.clientY;
            isDragging = true;
            moved = false;

            document.addEventListener('mousemove', dragMove);
            document.addEventListener('mouseup', dragEnd);
        }

        function dragMove(e) {
            if (!isDragging) return;

            e.preventDefault();
            pos1 = pos3 - e.clientX;
            pos2 = pos4 - e.clientY;
            pos3 = e.clientX;
            pos4 = e.clientY;

            let newTop = element.offsetTop - pos2;
            let newLeft = element.offsetLeft - pos1;

            newTop = Math.max(0, Math.min(newTop, window.innerHeight - element.offsetHeight));
            newLeft = Math.max(0, Math.min(newLeft, window.innerWidth - element.offsetWidth));

            element.style.top = `${newTop}px`;
            element.style.left = `${newLeft}px`;

            moved = true;
        }

        function dragEnd(e) {
            if (!isDragging) return;

            document.removeEventListener('mousemove', dragMove);
            document.removeEventListener('mouseup', dragEnd);

            isDragging = false;

            if (saveKeyPrefix && moved) {
                GM_setValue(`${saveKeyPrefix}Top`, element.style.top || `${element.offsetTop}px`);
                GM_setValue(`${saveKeyPrefix}Left`, element.style.left || `${element.offsetLeft}px`);
                console.log(`Saved ${saveKeyPrefix}Top: ${element.style.top}, ${saveKeyPrefix}Left: ${element.style.left}`);
            }
        }
    }

    // 創建UI
    function createUI() {
        const bubble = document.createElement('div');
        bubble.className = 'ai-selector-bubble';
        bubble.textContent = 'AI';
        const savedBubbleTop = GM_getValue('positionTop', '20px');
        const savedBubbleLeft = GM_getValue('positionLeft', 'calc(100% - 60px)');
        bubble.style.top = savedBubbleTop;
        bubble.style.left = savedBubbleLeft;

        const container = document.createElement('div');
        container.className = 'ai-selector-container';
        container.style.display = 'none';

        // 從 GM_getValue 獲取保存的尺寸和位置
        const savedWidth = GM_getValue('containerWidth', 300);  // 純數值,無單位
        const savedHeight = GM_getValue('containerHeight', 300);  // 純數值,無單位
        const savedTop = GM_getValue('containerTop', '50px');
        const savedLeft = GM_getValue('containerLeft', '50px');

        // 應用保存的值,確保最小值並添加單位
        container.style.width = `${Math.max(savedWidth, 200)}px`;
        container.style.height = `${Math.max(savedHeight, 200)}px`;
        container.style.top = savedTop;
        container.style.left = savedLeft;

        console.log(`Loaded container - width: ${container.style.width}, height: ${container.style.height}, top: ${container.style.top}, left: ${container.style.left}`);

        const header = document.createElement('div');
        header.className = 'ai-selector-header';
        const title = document.createElement('div');
        title.className = 'ai-selector-title';
        title.textContent = 'AI助手選擇器 / AI Assistant Selector';
        const minimize = document.createElement('div');
        minimize.className = 'ai-selector-minimize';
        minimize.textContent = '×';
        header.appendChild(title);
        header.appendChild(minimize);

        const content = document.createElement('div');
        content.className = 'ai-selector-content';

        const selectedAIs = GM_getValue('selectedAIs', AIs.map(ai => ai.id));
        AIs.forEach(ai => {
            const option = document.createElement('div');
            option.className = 'ai-option';
            option.dataset.aiId = ai.id;
            option.style.border = `2px solid ${ai.color}`;
            if (selectedAIs.includes(ai.id)) {
                option.classList.add('selected');
            }
            const name = document.createElement('span');
            name.className = 'ai-name';
            name.textContent = ai.name;
            option.appendChild(name);
            content.appendChild(option);
        });

        const questionInput = document.createElement('textarea');
        questionInput.className = 'question-input';
        questionInput.placeholder = '輸入您的問題 / Enter your question';

        const sendButton = document.createElement('button');
        sendButton.className = 'send-button';
        sendButton.textContent = '發送到選中的AI / Send to Selected AI';

        content.appendChild(questionInput);
        content.appendChild(sendButton);

        container.appendChild(header);
        container.appendChild(content);

        // 創建Grok回應UI
        const grokResponseContainer = document.createElement('div');
        grokResponseContainer.className = 'grok-response-container';
        grokResponseContainer.style.display = 'none';

        let grokWidth = GM_getValue('grokResponseWidth', 300);  // 純數值,無單位
        let grokHeight = GM_getValue('grokResponseHeight', 200);  // 純數值,無單位
        const grokTop = GM_getValue('grokResponseTop', '100px');
        const grokLeft = GM_getValue('grokResponseLeft', '100px');

        grokWidth = Math.max(grokWidth, 200);
        grokHeight = Math.max(grokHeight, 200);

        grokResponseContainer.style.top = grokTop;
        grokResponseContainer.style.left = grokLeft;
        grokResponseContainer.style.width = `${grokWidth}px`;
        grokResponseContainer.style.height = `${grokHeight}px`;

        const grokHeader = document.createElement('div');
        grokHeader.className = 'grok-response-header';
        const grokTitle = document.createElement('div');
        grokTitle.className = 'grok-response-title';
        grokTitle.textContent = 'Grok 回應 / Grok Response';
        const grokClose = document.createElement('div');
        grokClose.className = 'grok-response-close';
        grokClose.textContent = '×';
        grokHeader.appendChild(grokTitle);
        grokHeader.appendChild(grokClose);

        const grokResponseDiv = document.createElement('div');
        grokResponseDiv.className = 'grok-response';
        const grokInitialP = document.createElement('p');
        grokInitialP.textContent = '等待 Grok 回應... / Waiting for Grok response...';
        grokResponseDiv.appendChild(grokInitialP);

        const grokClearButton = document.createElement('button');
        grokClearButton.className = 'clear-button';
        grokClearButton.textContent = '清空歷史訊息 / Clear History';

        grokResponseContainer.appendChild(grokHeader);
        grokResponseContainer.appendChild(grokClearButton);
        grokResponseContainer.appendChild(grokResponseDiv);

        // 創建ChatGPT回應UI
        const chatgptResponseContainer = document.createElement('div');
        chatgptResponseContainer.className = 'chatgpt-response-container';
        chatgptResponseContainer.style.display = 'none';

        let chatgptWidth = GM_getValue('chatgptResponseWidth', 300);  // 純數值,無單位
        let chatgptHeight = GM_getValue('chatgptResponseHeight', 200);  // 純數值,無單位
        const chatgptTop = GM_getValue('chatgptResponseTop', '150px');
        const chatgptLeft = GM_getValue('chatgptResponseLeft', '150px');

        chatgptWidth = Math.max(chatgptWidth, 200);
        chatgptHeight = Math.max(chatgptHeight, 200);

        chatgptResponseContainer.style.top = chatgptTop;
        chatgptResponseContainer.style.left = chatgptLeft;
        chatgptResponseContainer.style.width = `${chatgptWidth}px`;
        chatgptResponseContainer.style.height = `${chatgptHeight}px`;

        const chatgptHeader = document.createElement('div');
        chatgptHeader.className = 'chatgpt-response-header';
        const chatgptTitle = document.createElement('div');
        chatgptTitle.className = 'chatgpt-response-title';
        chatgptTitle.textContent = 'ChatGPT 回應 / ChatGPT Response';
        const chatgptClose = document.createElement('div');
        chatgptClose.className = 'chatgpt-response-close';
        chatgptClose.textContent = '×';
        chatgptHeader.appendChild(chatgptTitle);
        chatgptHeader.appendChild(chatgptClose);

        const chatgptResponseDiv = document.createElement('div');
        chatgptResponseDiv.className = 'chatgpt-response';
        const chatgptInitialP = document.createElement('p');
        chatgptInitialP.textContent = '等待 ChatGPT 回應... / Waiting for ChatGPT response...';
        chatgptResponseDiv.appendChild(chatgptInitialP);

        const chatgptClearButton = document.createElement('button');
        chatgptClearButton.className = 'clear-button';
        chatgptClearButton.textContent = '清空歷史訊息 / Clear History';

        chatgptResponseContainer.appendChild(chatgptHeader);
        chatgptResponseContainer.appendChild(chatgptClearButton);
        chatgptResponseContainer.appendChild(chatgptResponseDiv);

        document.body.appendChild(bubble);
        document.body.appendChild(container);
        document.body.appendChild(grokResponseContainer);
        document.body.appendChild(chatgptResponseContainer);

        return { bubble, container, grokResponseContainer, grokResponseDiv, chatgptResponseContainer, chatgptResponseDiv, header, grokHeader, chatgptHeader, grokClearButton, chatgptClearButton };
    }

    // 初始化事件監聽
    function initializeEvents(bubble, container, grokResponseContainer, grokResponseDiv, chatgptResponseContainer, chatgptResponseDiv, header, grokHeader, chatgptHeader, grokClearButton, chatgptClearButton) {
        const aiOptions = container.querySelectorAll('.ai-option');
        const questionInput = container.querySelector('.question-input');
        const sendButton = container.querySelector('.send-button');
        const minimizeButton = container.querySelector('.ai-selector-minimize');
        const content = container.querySelector('.ai-selector-content');
        const grokCloseButton = grokResponseContainer.querySelector('.grok-response-close');
        const chatgptCloseButton = chatgptResponseContainer.querySelector('.chatgpt-response-close');

        aiOptions.forEach(option => {
            option.addEventListener('click', () => {
                option.classList.toggle('selected');
                const selectedAIs = Array.from(aiOptions)
                    .filter(opt => opt.classList.contains('selected'))
                    .map(opt => opt.dataset.aiId);
                GM_setValue('selectedAIs', selectedAIs);
            });
        });

        minimizeButton.addEventListener('click', () => {
            container.style.display = 'none';
            bubble.style.display = 'flex';
        });

        sendButton.addEventListener('click', () => {
            const selectedAIs = Array.from(aiOptions).filter(option => option.classList.contains('selected'));
            const question = questionInput.value.trim();
            if (selectedAIs.length > 0 && question) {
                selectedAIs.forEach(aiOption => {
                    const aiId = aiOption.dataset.aiId;
                    const ai = AIs.find(a => a.id === aiId);
                    if (ai) {
                        openAIInNewTab(ai, question);
                        if (ai.id === 'grok') {
                            grokResponseContainer.style.display = 'block';
                            while (grokResponseDiv.firstChild) {
                                grokResponseDiv.removeChild(grokResponseDiv.firstChild);
                            }
                            const p = document.createElement('p');
                            p.textContent = '等待 Grok 回應... / Waiting for Grok response...';
                            grokResponseDiv.appendChild(p);
                        }
                        if (ai.id === 'chatgpt') {
                            chatgptResponseContainer.style.display = 'block';
                            while (chatgptResponseDiv.firstChild) {
                                chatgptResponseDiv.removeChild(chatgptResponseDiv.firstChild);
                            }
                            const p = document.createElement('p');
                            p.textContent = '等待 ChatGPT 回應... / Waiting for ChatGPT response...';
                            chatgptResponseDiv.appendChild(p);
                        }
                    }
                });
                questionInput.value = '';
            }
        });

        grokClearButton.addEventListener('click', () => {
            while (grokResponseDiv.firstChild) {
                grokResponseDiv.removeChild(grokResponseDiv.firstChild);
            }
            const p = document.createElement('p');
            p.textContent = '等待 Grok 回應... / Waiting for Grok response...';
            grokResponseDiv.appendChild(p);
        });

        chatgptClearButton.addEventListener('click', () => {
            while (chatgptResponseDiv.firstChild) {
                chatgptResponseDiv.removeChild(chatgptResponseDiv.firstChild);
            }
            const p = document.createElement('p');
            p.textContent = '等待 ChatGPT 回應... / Waiting for ChatGPT response...';
            chatgptResponseDiv.appendChild(p);
        });

        grokCloseButton.addEventListener('click', () => {
            grokResponseContainer.style.display = 'none';
        });

        chatgptCloseButton.addEventListener('click', () => {
            chatgptResponseContainer.style.display = 'none';
        });

        bubble.addEventListener('click', showContainer);

        makeDraggable(container, header, 'container');
        makeDraggable(bubble, null, 'position');
        makeDraggable(grokResponseContainer, grokHeader, 'grokResponse');
        makeDraggable(chatgptResponseContainer, chatgptHeader, 'chatgptResponse');

        document.addEventListener('contextmenu', (e) => {
            if (container.style.display === 'none') {
                e.preventDefault();
                showContainer();
            }
        });

        window.addEventListener('resize', () => {
            if (container.style.display !== 'none') {
                const containerWidth = container.offsetWidth;
                const containerHeight = container.offsetHeight;
                let containerTop = parseInt(container.style.top || GM_getValue('containerTop', '50px'));
                let containerLeft = parseInt(container.style.left || GM_getValue('containerLeft', '50px'));
                containerTop = Math.max(0, Math.min(containerTop, window.innerHeight - containerHeight));
                containerLeft = Math.max(0, Math.min(containerLeft, window.innerWidth - containerWidth));
                container.style.top = `${containerTop}px`;
                container.style.left = `${containerLeft}px`;
                GM_setValue('containerTop', `${containerTop}px`);
                GM_setValue('containerLeft', `${containerLeft}px`);
            }
            if (grokResponseContainer.style.display !== 'none') {
                const grokWidth = grokResponseContainer.offsetWidth;
                const grokHeight = grokResponseContainer.offsetHeight;
                let grokTop = parseInt(grokResponseContainer.style.top);
                let grokLeft = parseInt(grokResponseContainer.style.left);
                grokTop = Math.max(0, Math.min(grokTop, window.innerHeight - grokHeight));
                grokLeft = Math.max(0, Math.min(grokLeft, window.innerWidth - grokWidth));
                grokResponseContainer.style.top = `${grokTop}px`;
                grokResponseContainer.style.left = `${grokLeft}px`;
            }
            if (chatgptResponseContainer.style.display !== 'none') {
                const chatgptWidth = chatgptResponseContainer.offsetWidth;
                const chatgptHeight = chatgptResponseContainer.offsetHeight;
                let chatgptTop = parseInt(chatgptResponseContainer.style.top);
                let chatgptLeft = parseInt(chatgptResponseContainer.style.left);
                chatgptTop = Math.max(0, Math.min(chatgptTop, window.innerHeight - chatgptHeight));
                chatgptLeft = Math.max(0, Math.min(chatgptLeft, window.innerWidth - chatgptWidth));
                chatgptResponseContainer.style.top = `${chatgptTop}px`;
                chatgptResponseContainer.style.left = `${chatgptLeft}px`;
            }
        });

        const resizeObserver = new ResizeObserver(entries => {
            for (let entry of entries) {
                const target = entry.target;
                // 只在容器可見時儲存尺寸,避免儲存無效值
                if (target.style.display !== 'none') {
                    if (target === container) {
                        GM_setValue('containerWidth', target.offsetWidth);
                        GM_setValue('containerHeight', target.offsetHeight);
                        console.log(`Saved containerWidth: ${target.offsetWidth}, containerHeight: ${target.offsetHeight}, display: ${target.style.display}`);
                    } else if (target === grokResponseContainer) {
                        GM_setValue('grokResponseWidth', target.offsetWidth);
                        GM_setValue('grokResponseHeight', target.offsetHeight);
                        console.log(`Saved grokResponseWidth: ${target.offsetWidth}, grokResponseHeight: ${target.offsetHeight}, display: ${target.style.display}`);
                    } else if (target === chatgptResponseContainer) {
                        GM_setValue('chatgptResponseWidth', target.offsetWidth);
                        GM_setValue('chatgptResponseHeight', target.offsetHeight);
                        console.log(`Saved chatgptResponseWidth: ${target.offsetWidth}, chatgptResponseHeight: ${target.offsetHeight}, display: ${target.style.display}`);
                    }
                } else {
                    console.log(`Skipped saving for ${target.className} - display: ${target.style.display}, width: ${target.offsetWidth}, height: ${target.offsetHeight}`);
                }
            }
        });

        resizeObserver.observe(container);
        resizeObserver.observe(grokResponseContainer);
        resizeObserver.observe(chatgptResponseContainer);

        function showContainer() {
            bubble.style.display = 'none';
            container.style.display = 'block';
            content.style.display = 'flex';

            // 每次顯示時,重新應用保存的尺寸和位置
            const savedWidth = GM_getValue('containerWidth', 300);  // 純數值,無單位
            const savedHeight = GM_getValue('containerHeight', 300);  // 純數值,無單位
            const savedTop = GM_getValue('containerTop', '50px');
            const savedLeft = GM_getValue('containerLeft', '50px');

            container.style.width = `${Math.max(savedWidth, 200)}px`;
            container.style.height = `${Math.max(savedHeight, 200)}px`;
            container.style.top = savedTop;
            container.style.left = savedLeft;

            // 確保位置不超出視窗
            const containerWidth = container.offsetWidth;
            const containerHeight = container.offsetHeight;
            let containerTop = parseInt(savedTop);
            let containerLeft = parseInt(savedLeft);
            containerTop = Math.max(0, Math.min(containerTop, window.innerHeight - containerHeight));
            containerLeft = Math.max(0, Math.min(containerLeft, window.innerWidth - containerWidth));
            container.style.top = `${containerTop}px`;
            container.style.left = `${containerLeft}px`;

            console.log(`Show container - width: ${container.style.width}, height: ${container.style.height}, top: ${container.style.top}, left: ${container.style.left}`);
        }
    }

    // 在新標籤頁中打開AI
    function openAIInNewTab(ai, question) {
        const url = `${ai.url}${ai.id === 'gemini' ? '?q=' : '?q='}${encodeURIComponent(question)}`;
        GM_openInTab(url, {
            active: false,
            insert: true,
            setParent: true
        });
    }

    // 處理 Gemini 頁面
    function handleGeminiPage() {
        if (window.location.hostname === 'gemini.google.com' && window.location.search.includes('q=')) {
            const query = new URLSearchParams(window.location.search).get('q');
            if (query) {
                function setTextAndSendAfterDelay(string) {
                    const richTextarea = document.querySelector('rich-textarea.text-input-field_textarea');
                    if (!richTextarea) return false;
                    const firstDiv = richTextarea.querySelector('div');
                    if (!firstDiv) return false;
                    firstDiv.innerText = string;
                    const event = new Event('input', { bubbles: true });
                    firstDiv.dispatchEvent(event);
                    setTimeout(() => {
                        const sendButton = document.querySelector('.send-button');
                        if (sendButton) sendButton.click();
                    }, 1000);
                    return true;
                }

                const waitForElement = (selector, maxAttempts = 30) => {
                    return new Promise((resolve) => {
                        let attempts = 0;
                        const interval = setInterval(() => {
                            const element = document.querySelector(selector);
                            attempts++;
                            if (element || attempts >= maxAttempts) {
                                clearInterval(interval);
                                resolve(element);
                            }
                        }, 500);
                    });
                };

                const trySetQuestion = async () => {
                    await waitForElement('rich-textarea.text-input-field_textarea');
                    setTextAndSendAfterDelay(decodeURIComponent(query));
                };

                trySetQuestion();
                window.history.replaceState({}, document.title, '/app');
            }
        }
    }

    // 處理 Grok 頁面
    function handleGrokPage() {
        if (window.location.hostname === 'grok.com') {
            let lastContent = '';

            setInterval(() => {
                const messageBubbles = document.querySelectorAll('div.message-bubble:not(.processed)');
                let currentContent = '';

                messageBubbles.forEach(div => {
                    const content = div.innerHTML.trim();
                    if (content) {
                        const nextSibling = div.nextElementSibling;
                        let buttonCount = 0;
                        if (nextSibling) {
                            buttonCount = nextSibling.querySelectorAll('button').length;
                        }
                        if (buttonCount > 2) {
                            currentContent += content + '\n';
                            div.classList.add('processed');
                        }
                    }
                });

                if (currentContent && currentContent !== lastContent) {
                    lastContent = currentContent.trim();
                    console.log('儲存 Grok 回應:', lastContent);
                    GM_setValue('grokResponse', lastContent);
                }
            }, 500);
        }
    }

    // 處理 ChatGPT 頁面
    function handleChatGPTPage() {
        if (window.location.hostname === 'chatgpt.com') {
            let lastContent = '';

            setInterval(() => {
                const assistantMessages = document.querySelectorAll('div[data-message-author-role="assistant"]:not(.processed)');
                let currentContent = '';

                assistantMessages.forEach(div => {
                    const innerDiv = div.querySelector('div.markdown.prose.w-full.break-words');
                    const content = div.innerHTML.trim();
                    if (content && innerDiv && !innerDiv.classList.contains('result-streaming') && !innerDiv.classList.contains('result-thinking')) {
                        currentContent += content + '\n';
                        div.classList.add('processed');
                    }
                });

                if (currentContent && currentContent !== lastContent) {
                    lastContent = currentContent.trim();
                    console.log('儲存 ChatGPT 回應:', lastContent);
                    GM_setValue('chatgptResponse', lastContent);
                }
            }, 500);
        }
    }

    // 檢查並顯示 Grok 回應
    function checkGrokResponse(grokResponseContainer, grokResponseDiv) {
        if (!grokResponseDiv) return;

        setInterval(() => {
            const response = GM_getValue('grokResponse', '');
            if (response && grokResponseContainer.style.display === 'block') {
                console.log('更新 Grok UI:', response);
                const newResponse = document.createElement('p');
                newResponse.innerHTML = response;
                while (grokResponseDiv.firstChild) {
                    grokResponseDiv.removeChild(grokResponseDiv.firstChild);
                }
                grokResponseDiv.appendChild(newResponse);
                GM_setValue('grokResponse', '');
            }
        }, 1000);
    }

    // 檢查並顯示 ChatGPT 回應
    function checkChatGPTResponse(chatgptResponseContainer, chatgptResponseDiv) {
        if (!chatgptResponseDiv) return;

        setInterval(() => {
            const response = GM_getValue('chatgptResponse', '');
            if (response && chatgptResponseContainer.style.display === 'block') {
                console.log('更新 ChatGPT UI:', response);
                const newResponse = document.createElement('p');
                newResponse.innerHTML = response;
                while (chatgptResponseDiv.firstChild) {
                    chatgptResponseDiv.removeChild(chatgptResponseDiv.firstChild);
                }
                chatgptResponseDiv.appendChild(newResponse);
                GM_setValue('chatgptResponse', '');
            }
        }, 1000);
    }

    // 啟動腳本
    function initialize() {
        const { bubble, container, grokResponseContainer, grokResponseDiv, chatgptResponseContainer, chatgptResponseDiv, header, grokHeader, chatgptHeader, grokClearButton, chatgptClearButton } = createUI();
        initializeEvents(bubble, container, grokResponseContainer, grokResponseDiv, chatgptResponseContainer, chatgptResponseDiv, header, grokHeader, chatgptHeader, grokClearButton, chatgptClearButton);
        handleGeminiPage();
        handleGrokPage();
        handleChatGPTPage();
        checkGrokResponse(grokResponseContainer, grokResponseDiv);
        checkChatGPTResponse(chatgptResponseContainer, chatgptResponseDiv);
    }

    if (document.readyState === 'loading') {
        document.addEventListener('DOMContentLoaded', initialize);
    } else {
        initialize();
    }
})();