// ==UserScript==
// @name AI助手選擇器
// @name:en AI Assistant Selector
// @namespace http://tampermonkey.net/
// @version 3.2
// @description 一個 Tampermonkey 腳本,提供浮動介面整合多款 AI 助手,支援右鍵呼叫、清空歷史訊息、可調整尺寸,並記錄 AI 選擇狀態與位置,從Tampermonkey 選單呼叫。
// @description:en A Tampermonkey script that provides a floating interface integrating multiple AI assistants, supporting right-click invocation, clearing history messages, adjustable size, recording AI selection status and position, and callable from the Tampermonkey menu.
// @author 請替換成您的名字或暱稱 / Replace with your name or nickname
// @match *://*/*
// @match *://grok.com/*
// @match *://chatgpt.com/*
// @match *://chat.deepseek.com/*
// @icon data:image/gif;base64,R0lGODlhAQABAAAAACH5BAEKAAEALAAAAAABAAEAAAICTAEAOw==
// @license MIT
// @grant GM_addStyle
// @grant GM_setValue
// @grant GM_getValue
// @grant GM_xmlhttpRequest
// @grant GM_openInTab
// @grant GM_registerMenuCommand
// ==/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-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, .deepseek-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, .deepseek-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, .deepseek-response-title {
font-size: 16px;
font-weight: bold;
}
.grok-response-close, .chatgpt-response-close, .deepseek-response-close {
cursor: pointer;
padding: 5px;
}
.grok-response, .chatgpt-response, .deepseek-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, .deepseek-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' },
{ id: 'deepseek', name: 'DeepSeek', url: 'https://chat.deepseek.com/', color: '#f1c40f' }
];
// 添加樣式
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`);
}
}
}
// 創建UI(移除氣泡)
function createUI() {
const container = document.createElement('div');
container.className = 'ai-selector-container';
container.style.display = 'none';
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 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);
// DeepSeek 回應 UI
const deepseekResponseContainer = document.createElement('div');
deepseekResponseContainer.className = 'deepseek-response-container';
deepseekResponseContainer.style.display = 'none';
let deepseekWidth = GM_getValue('deepseekResponseWidth', 300);
let deepseekHeight = GM_getValue('deepseekResponseHeight', 200);
const deepseekTop = GM_getValue('deepseekResponseTop', '200px');
const deepseekLeft = GM_getValue('deepseekResponseLeft', '200px');
deepseekWidth = Math.max(deepseekWidth, 200);
deepseekHeight = Math.max(deepseekHeight, 200);
deepseekResponseContainer.style.top = deepseekTop;
deepseekResponseContainer.style.left = deepseekLeft;
deepseekResponseContainer.style.width = `${deepseekWidth}px`;
deepseekResponseContainer.style.height = `${deepseekHeight}px`;
const deepseekHeader = document.createElement('div');
deepseekHeader.className = 'deepseek-response-header';
const deepseekTitle = document.createElement('div');
deepseekTitle.className = 'deepseek-response-title';
deepseekTitle.textContent = 'DeepSeek 回應 / DeepSeek Response';
const deepseekClose = document.createElement('div');
deepseekClose.className = 'deepseek-response-close';
deepseekClose.textContent = '×';
deepseekHeader.appendChild(deepseekTitle);
deepseekHeader.appendChild(deepseekClose);
const deepseekResponseDiv = document.createElement('div');
deepseekResponseDiv.className = 'deepseek-response';
const deepseekInitialP = document.createElement('p');
deepseekInitialP.textContent = '等待 DeepSeek 回應... / Waiting for DeepSeek response...';
deepseekResponseDiv.appendChild(deepseekInitialP);
const deepseekClearButton = document.createElement('button');
deepseekClearButton.className = 'clear-button';
deepseekClearButton.textContent = '清空歷史訊息 / Clear History';
deepseekResponseContainer.appendChild(deepseekHeader);
deepseekResponseContainer.appendChild(deepseekClearButton);
deepseekResponseContainer.appendChild(deepseekResponseDiv);
document.body.appendChild(container);
document.body.appendChild(grokResponseContainer);
document.body.appendChild(chatgptResponseContainer);
document.body.appendChild(deepseekResponseContainer);
return { container, grokResponseContainer, grokResponseDiv, chatgptResponseContainer, chatgptResponseDiv, deepseekResponseContainer, deepseekResponseDiv, header, grokHeader, chatgptHeader, deepseekHeader, grokClearButton, chatgptClearButton, deepseekClearButton };
}
// 初始化事件監聽(移除氣泡相關事件,新增選單按鈕)
function initializeEvents(container, grokResponseContainer, grokResponseDiv, chatgptResponseContainer, chatgptResponseDiv, deepseekResponseContainer, deepseekResponseDiv, header, grokHeader, chatgptHeader, deepseekHeader, grokClearButton, chatgptClearButton, deepseekClearButton) {
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');
const deepseekCloseButton = deepseekResponseContainer.querySelector('.deepseek-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';
});
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);
}
});
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);
});
deepseekClearButton.addEventListener('click', () => {
while (deepseekResponseDiv.firstChild) {
deepseekResponseDiv.removeChild(deepseekResponseDiv.firstChild);
}
const p = document.createElement('p');
p.textContent = '等待 DeepSeek 回應... / Waiting for DeepSeek response...';
deepseekResponseDiv.appendChild(p);
});
grokCloseButton.addEventListener('click', () => {
grokResponseContainer.style.display = 'none';
});
chatgptCloseButton.addEventListener('click', () => {
chatgptResponseContainer.style.display = 'none';
});
deepseekCloseButton.addEventListener('click', () => {
deepseekResponseContainer.style.display = 'none';
});
makeDraggable(container, header, 'container');
makeDraggable(grokResponseContainer, grokHeader, 'grokResponse');
makeDraggable(chatgptResponseContainer, chatgptHeader, 'chatgptResponse');
makeDraggable(deepseekResponseContainer, deepseekHeader, 'deepseekResponse');
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`;
}
if (deepseekResponseContainer.style.display !== 'none') {
const deepseekWidth = deepseekResponseContainer.offsetWidth;
const deepseekHeight = deepseekResponseContainer.offsetHeight;
let deepseekTop = parseInt(deepseekResponseContainer.style.top);
let deepseekLeft = parseInt(deepseekResponseContainer.style.left);
deepseekTop = Math.max(0, Math.min(deepseekTop, window.innerHeight - deepseekHeight));
deepseekLeft = Math.max(0, Math.min(deepseekLeft, window.innerWidth - deepseekWidth));
deepseekResponseContainer.style.top = `${deepseekTop}px`;
deepseekResponseContainer.style.left = `${deepseekLeft}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);
} else if (target === grokResponseContainer) {
GM_setValue('grokResponseWidth', target.offsetWidth);
GM_setValue('grokResponseHeight', target.offsetHeight);
} else if (target === chatgptResponseContainer) {
GM_setValue('chatgptResponseWidth', target.offsetWidth);
GM_setValue('chatgptResponseHeight', target.offsetHeight);
} else if (target === deepseekResponseContainer) {
GM_setValue('deepseekResponseWidth', target.offsetWidth);
GM_setValue('deepseekResponseHeight', target.offsetHeight);
}
}
}
});
resizeObserver.observe(container);
resizeObserver.observe(grokResponseContainer);
resizeObserver.observe(chatgptResponseContainer);
resizeObserver.observe(deepseekResponseContainer);
// 顯示容器函數
function showContainer() {
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`;
}
// 註冊 Tampermonkey 選單按鈕
GM_registerMenuCommand('開啟 AI 助手選擇器 / Open AI Assistant Selector', showContainer);
}
// 在新標籤頁中打開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();
setTimeout(() => {
const finalCheck = document.querySelectorAll('div.message-bubble.processed');
let finalContent = '';
finalCheck.forEach(div => {
finalContent += div.innerHTML.trim() + '\n';
});
finalContent = finalContent.trim();
if (finalContent === lastContent) {
GM_setValue('grokResponse', lastContent);
}
}, 1000);
}
}, 500);
}
}
// 處理 ChatGPT 頁面
function handleChatGPTPage() {
if (window.location.hostname === 'chatgpt.com') {
let lastContent = '';
setInterval(() => {
const stopButton = document.querySelector('[data-testid="stop-button"]');
if (stopButton) {
return;
}
const messageBubbles = document.querySelectorAll('div[data-message-author-role="assistant"]:not(.processed)');
let currentContent = '';
messageBubbles.forEach(div => {
const content = div.innerHTML.trim();
if (content) {
const grandParent = div.parentElement.parentElement;
if (grandParent.children.length === 3) {
currentContent += content + '\n';
div.classList.add('processed');
}
}
});
if (currentContent && currentContent !== lastContent) {
lastContent = currentContent.trim();
setTimeout(() => {
GM_setValue('chatgptResponse', lastContent);
}, 1000);
}
}, 500);
}
}
// 處理 DeepSeek 頁面
function handleDeepSeekPage() {
if (window.location.hostname === 'chat.deepseek.com' && window.location.search.includes('q=')) {
const query = new URLSearchParams(window.location.search).get('q');
if (query) {
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 () => {
const chatInput = await waitForElement('#chat-input');
if (chatInput) {
chatInput.value = decodeURIComponent(query);
const inputEvent = new Event('input', { bubbles: true });
chatInput.dispatchEvent(inputEvent);
setTimeout(() => {
const enterEvent = new KeyboardEvent('keydown', {
bubbles: true,
cancelable: true,
keyCode: 13
});
chatInput.dispatchEvent(enterEvent);
}, 1000);
}
};
trySetQuestion();
window.history.replaceState({}, document.title, '/');
}
}
let lastContent = '';
setInterval(() => {
const responseElements = document.querySelectorAll('.chat-message[data-from="assistant"]:not(.processed)');
let currentContent = '';
responseElements.forEach(element => {
const content = element.innerHTML.trim();
if (content) {
currentContent += content + '\n';
element.classList.add('processed');
}
});
if (currentContent && currentContent !== lastContent) {
lastContent = currentContent.trim();
setTimeout(() => {
GM_setValue('deepseekResponse', lastContent);
}, 1000);
}
}, 500);
}
// 檢查並顯示 Grok 回應
function checkGrokResponse(grokResponseContainer, grokResponseDiv) {
if (!grokResponseDiv) return;
setInterval(() => {
const response = GM_getValue('grokResponse', '');
if (response && grokResponseContainer.style.display === 'block') {
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') {
const newResponse = document.createElement('p');
newResponse.innerHTML = response;
while (chatgptResponseDiv.firstChild) {
chatgptResponseDiv.removeChild(chatgptResponseDiv.firstChild);
}
chatgptResponseDiv.appendChild(newResponse);
GM_setValue('chatgptResponse', '');
}
}, 1000);
}
// 檢查並顯示 DeepSeek 回應
function checkDeepSeekResponse(deepseekResponseContainer, deepseekResponseDiv) {
if (!deepseekResponseDiv) return;
setInterval(() => {
const response = GM_getValue('deepseekResponse', '');
if (response && deepseekResponseContainer.style.display === 'block') {
const newResponse = document.createElement('p');
newResponse.innerHTML = response;
while (deepseekResponseDiv.firstChild) {
deepseekResponseDiv.removeChild(deepseekResponseDiv.firstChild);
}
deepseekResponseDiv.appendChild(newResponse);
GM_setValue('deepseekResponse', '');
}
}, 1000);
}
// 啟動腳本
function initialize() {
const { container, grokResponseContainer, grokResponseDiv, chatgptResponseContainer, chatgptResponseDiv, deepseekResponseContainer, deepseekResponseDiv, header, grokHeader, chatgptHeader, deepseekHeader, grokClearButton, chatgptClearButton, deepseekClearButton } = createUI();
initializeEvents(container, grokResponseContainer, grokResponseDiv, chatgptResponseContainer, chatgptResponseDiv, deepseekResponseContainer, deepseekResponseDiv, header, grokHeader, chatgptHeader, deepseekHeader, grokClearButton, chatgptClearButton, deepseekClearButton);
handleGeminiPage();
handleGrokPage();
handleChatGPTPage();
handleDeepSeekPage();
}
if (document.readyState === 'loading') {
document.addEventListener('DOMContentLoaded', initialize);
} else {
initialize();
}
})();