您需要先安装一个扩展,例如 篡改猴、Greasemonkey 或 暴力猴,之后才能安装此脚本。
您需要先安装一个扩展,例如 篡改猴 或 暴力猴,之后才能安装此脚本。
您需要先安装一个扩展,例如 篡改猴 或 暴力猴,之后才能安装此脚本。
您需要先安装一个扩展,例如 篡改猴 或 Userscripts ,之后才能安装此脚本。
您需要先安装一款用户脚本管理器扩展,例如 Tampermonkey,才能安装此脚本。
您需要先安装用户脚本管理器扩展后才能安装此脚本。
【强力拦截版】1. 彻底阻止Enter键直接发送,改为Ctrl+Enter发送。为Qwen增加ID选择器,优先点击按钮发送。2. 支持点击复制数学公式的LaTeX代码(已为Copilot修复v5-final)。
// ==UserScript== // @name AI平台_Web体验优化 // @namespace http://tampermonkey.net/ // @version 6.5.5 // @description 【强力拦截版】1. 彻底阻止Enter键直接发送,改为Ctrl+Enter发送。为Qwen增加ID选择器,优先点击按钮发送。2. 支持点击复制数学公式的LaTeX代码(已为Copilot修复v5-final)。 // @author 0xbbbb & Gemini & GPT (重构 by Gemini, copilot-fix v5 by Gemini) // @match https://chatgpt.com/* // @match https://chat.deepseek.com/* // @match https://chat.qwen.ai/* // @match https://gemini.google.com/* // @match https://claude.ai/* // @match https://grok.com/* // @match https://copilot.microsoft.com/* // @grant GM_addStyle // @grant GM_setClipboard // @run-at document-start // ==/UserScript== (function() { 'use strict'; const SCRIPT_NAME = 'AIChatEnhancer-v6.5.5'; const currentHostname = window.location.hostname; // --- 模块一:公式复制器 (v5 - 终极精准侦测) --- if ( !currentHostname.includes('gemini.google.com') && !currentHostname.includes('claude.ai') ) { (function FormulaCopier() { function initFormulaCopier() { if (!document.body) { setTimeout(initFormulaCopier, 100); return; } console.log(`[${SCRIPT_NAME}] DOM ready, 启动【公式复制器 v5 - 终极精准侦测】。`); const styles = ` .formula-copier-selected { outline: 2px solid #4A90E2 !important; border-radius: 5px; cursor: pointer; box-shadow: 0 0 5px rgba(74, 144, 226, 0.5); } .formula-copier-feedback { position: fixed; background-color: #4A90E2; color: white; padding: 5px 10px; border-radius: 5px; font-size: 14px; font-family: -apple-system, BlinkMacSystemFont, "Segoe UI", Roboto, sans-serif; z-index: 10001; pointer-events: none; opacity: 0; transition: opacity 0.3s ease, transform 0.3s ease; transform: translateY(10px); } .formula-copier-feedback.visible { opacity: 1; transform: translateY(0); } `; if (currentHostname.includes('copilot.microsoft.com')) { const styleEl = document.createElement('style'); styleEl.textContent = styles; document.head.appendChild(styleEl); } else { GM_addStyle(styles); } let selectedElement = null; function showCopyFeedback(element) { const rect = element.getBoundingClientRect(); const feedback = document.createElement('div'); feedback.textContent = 'LaTeX 已复制!'; feedback.classList.add('formula-copier-feedback'); document.body.appendChild(feedback); feedback.style.left = `${rect.left + rect.width / 2 - feedback.offsetWidth / 2}px`; feedback.style.top = `${rect.top - feedback.offsetHeight - 8}px`; setTimeout(() => { feedback.classList.add('visible'); }, 10); setTimeout(() => { feedback.style.opacity = '0'; feedback.style.transform = 'translateY(10px)'; setTimeout(() => feedback.remove(), 300); }, 1500); } function clearSelection() { if (selectedElement) { selectedElement.classList.remove('formula-copier-selected'); selectedElement = null; } } // --- MODIFIED PART START --- // 重写的 getLatexSource 函数,采用多种高精度侦测方案 function getLatexSource(containerElement) { // 方案一:标准 <annotation> 标签 (最高优先级) let annotation = containerElement.querySelector('annotation[encoding="application/x-tex"], annotation[encoding="text/x-latex"]'); if (annotation) { return annotation.textContent.trim().replace(/\s*\\tag\{.*\}/, '').trim(); } // 方案二: Shadow DOM 'raw' 属性 (针对 <cib-math> 宿主) let currentElement = containerElement; while (currentElement) { const root = currentElement.getRootNode(); if (root instanceof ShadowRoot) { const host = root.host; if (host && host.tagName === 'CIB-MATH' && host.hasAttribute('raw')) { return host.getAttribute('raw').trim(); } currentElement = host; } else { currentElement = null; } } // 方案三: <math> 标签的直接子文本节点 (解决“融合怪”问题的最终方案) const mathElement = containerElement.querySelector('math, [role="math"]'); if (mathElement) { // 遍历 <math> 元素的所有直接子节点 for (const child of mathElement.childNodes) { // 我们只关心类型为“文本”的节点 if (child.nodeType === Node.TEXT_NODE) { const latex = child.textContent.trim(); // 通过特征字符简单验证这确实是LaTeX源码 if (latex.length > 1 && (latex.includes('\\') || latex.includes('^') || latex.includes('_') || latex.includes('{'))) { return latex; } } } } // 所有高精度方案都失败 return null; } // --- MODIFIED PART END --- function processFormulaClick(target) { if (target === selectedElement) { const latex = getLatexSource(target); if (latex) { GM_setClipboard(latex); showCopyFeedback(target); } else { console.warn(`[${SCRIPT_NAME}] 未能从元素中提取LaTeX源码:`, target); } clearSelection(); } else { clearSelection(); selectedElement = target; selectedElement.classList.add('formula-copier-selected'); } } document.addEventListener('click', (event) => { const path = event.composedPath(); let formulaElement = null; for (const element of path) { if (element.nodeType === Node.ELEMENT_NODE && element.classList && ( element.classList.contains('katex') || element.classList.contains('katex-display') || element.classList.contains('mjx-container') )) { formulaElement = element; break; } } if (formulaElement) { event.preventDefault(); event.stopPropagation(); processFormulaClick(formulaElement); } else { clearSelection(); } }, true); } if (document.readyState === 'loading') { document.addEventListener('DOMContentLoaded', initFormulaCopier); } else { initFormulaCopier(); } })(); } // --- 模块二:回车/Ctrl+Enter 增强 (强力拦截版 v6.5) --- (function EnterNoSendEnhanced() { console.log(`[${SCRIPT_NAME}] 模块【回车增强-强力拦截版】: 准备加载。`); function createKeyEvent(type, options) { const event = new KeyboardEvent(type, { key: options.key || 'Enter', code: options.code || 'Enter', keyCode: options.keyCode || 13, which: options.which || 13, shiftKey: !!options.shiftKey, ctrlKey: !!options.ctrlKey, altKey: !!options.altKey, metaKey: !!options.metaKey, bubbles: true, cancelable: true }); Object.defineProperty(event, 'isTriggeredByScript', { value: true, writable: false }); return event; } function simulateAdvancedEnter(target) { console.log(`[${SCRIPT_NAME}] 启动高级Enter模拟发送...`); target.focus(); const beforeInput = new InputEvent('beforeinput', { bubbles: true, cancelable: true, inputType: 'insertLineBreak', data: null }); target.dispatchEvent(beforeInput); if (!beforeInput.defaultPrevented) { console.log(`[${SCRIPT_NAME}] 'beforeinput' 未被阻止,继续完整模拟流程。`); target.dispatchEvent(createKeyEvent('keydown', {})); const inputEvent = new InputEvent('input', { bubbles: true, cancelable: true, inputType: 'insertLineBreak' }); target.dispatchEvent(inputEvent); target.dispatchEvent(createKeyEvent('keyup', {})); } else { console.log(`[${SCRIPT_NAME}] 'beforeinput' 已被框架处理,模拟完成。`); } } function handleKeyDown(e) { if (e.isTriggeredByScript) return; if (e.key !== 'Enter' || e.altKey || e.metaKey || e.shiftKey) return; if (e.ctrlKey) { console.log(`[${SCRIPT_NAME}] 检测到 Ctrl+Enter,执行发送。`); e.preventDefault(); e.stopImmediatePropagation(); let sendButton; if (currentHostname.includes('qwen.ai')) { sendButton = document.querySelector('#send-message-button'); if (sendButton && !sendButton.disabled) { console.log(`[${SCRIPT_NAME}] 适配[Qwen]:通过ID找到发送按钮并执行 click()。`, sendButton); sendButton.click(); return; } console.warn(`[${SCRIPT_NAME}] 适配[Qwen]:ID选择器未找到按钮,回退到高级事件模拟。`); simulateAdvancedEnter(e.target); return; } if (currentHostname.includes('chatgpt.com')) { sendButton = document.querySelector('button[data-testid="send-button"]'); } else if (currentHostname.includes('claude.ai')) { sendButton = document.querySelector('button[aria-label="Send Message"]') || document.querySelector('button[aria-label="发送消息"]'); } else if (currentHostname.includes('deepseek.com')) { sendButton = document.querySelector('button.ds-it-btn-send'); } if (sendButton && !sendButton.disabled) { console.log(`[${SCRIPT_NAME}] 适配[${currentHostname}]:找到发送按钮并执行 click()。`, sendButton); sendButton.click(); } else { console.warn(`[${SCRIPT_NAME}] 适配[${currentHostname}]:未找到发送按钮,回退到模拟原生Enter发送。`); e.target.dispatchEvent(createKeyEvent('keydown', {})); } } else { console.log(`[${SCRIPT_NAME}] 检测到单独的 Enter,执行换行。`); e.preventDefault(); e.stopImmediatePropagation(); e.target.dispatchEvent(createKeyEvent('keydown', { key: 'Enter', shiftKey: true })); } } function blockEnterPropagation(e) { if (e.isTriggeredByScript) return; if (e.key === 'Enter' && !e.ctrlKey && !e.shiftKey && !e.altKey && !e.metaKey) { e.preventDefault(); e.stopImmediatePropagation(); } } function attachInterceptor(element) { if (element.dataset.enterInterceptorAttached) return; if (element.offsetParent === null) return; console.log(`[${SCRIPT_NAME}] 发现可见输入框,正在附加强力拦截器...`, element); element.addEventListener('keydown', handleKeyDown, true); element.addEventListener('keypress', blockEnterPropagation, true); element.addEventListener('keyup', blockEnterPropagation, true); element.dataset.enterInterceptorAttached = 'true'; } const observer = new MutationObserver(mutations => { for (const mutation of mutations) { if (mutation.addedNodes.length) { mutation.addedNodes.forEach(node => { if (node.nodeType === Node.ELEMENT_NODE) { const targets = []; if (node.matches('textarea, div[contenteditable="true"]')) { targets.push(node); } targets.push(...node.querySelectorAll('textarea, div[contenteditable="true"]')); targets.forEach(attachInterceptor); } }); } } }); function initEnterInterceptor() { if (!document.body) { setTimeout(initEnterInterceptor, 100); return; } console.log(`[${SCRIPT_NAME}] DOM ready, 启动【回车增强-强力拦截版】。`); document.querySelectorAll('textarea, div[contenteditable="true"]').forEach(attachInterceptor); observer.observe(document.body, { childList: true, subtree: true }); console.log(`[${SCRIPT_NAME}] 强力拦截模块已启动,正在监视输入框...`); } if (document.readyState === 'loading') { document.addEventListener('DOMContentLoaded', initEnterInterceptor); } else { initEnterInterceptor(); } })(); })();