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