// ==UserScript== // @name 🌟 AI 智能词组 // @namespace http://tampermonkey.net/ // @version 3.1 // @description 利用 NLP 智能提取网页英文词组。彻底解决单词拆解问题,点击高亮处即可查询【整个短语】的精准释义,内置权威词典直达。 // @author Gemini // @match *://*/* // @grant GM_addStyle // @grant GM_xmlhttpRequest // @require https://unpkg.com/@popperjs/core@2 // @require https://unpkg.com/tippy.js@6 // @require https://cdn.jsdelivr.net/npm/compromise@14.14.3/builds/compromise.min.js // @require https://cdnjs.cloudflare.com/ajax/libs/mark.js/8.11.1/mark.min.js // @connect translate.googleapis.com // @run-at document-end // @downloadURL https://update.greasyfork.icu/scripts/572395/%F0%9F%8C%9F%20AI%20%E6%99%BA%E8%83%BD%E8%AF%8D%E7%BB%84.user.js // @updateURL https://update.greasyfork.icu/scripts/572395/%F0%9F%8C%9F%20AI%20%E6%99%BA%E8%83%BD%E8%AF%8D%E7%BB%84.meta.js // ==/UserScript== (function() { 'use strict'; // ========================================== // 1. 全局样式与 UI // ========================================== GM_addStyle(` .tm-phrase { background-color: rgba(76, 175, 80, 0.2); border-bottom: 2px dashed #4CAF50; border-radius: 2px; cursor: pointer; padding: 0 2px; transition: background-color 0.2s; color: inherit !important; position: relative; } /* 屏蔽子元素事件,防止与单词翻译脚本冲突 */ .tm-phrase * { pointer-events: none !important; } .tm-phrase:hover, .tm-phrase[aria-expanded="true"] { background-color: rgba(76, 175, 80, 0.45); color: #1b5e20 !important; } #tm-nlp-fab { position: fixed; bottom: 30px; right: 20px; width: 50px; height: 50px; background-color: #673AB7; color: white; border-radius: 50%; display: flex; align-items: center; justify-content: center; box-shadow: 0 4px 10px rgba(0,0,0,0.3); cursor: pointer; z-index: 2147483647; font-size: 24px; user-select: none; transition: transform 0.3s, background-color 0.3s; } #tm-nlp-fab:hover { transform: scale(1.1); } #tm-nlp-fab.processing { animation: tm-spin 1.5s linear infinite; background-color: #FF9800; } @keyframes tm-spin { 0% { transform: rotate(0deg); } 100% { transform: rotate(360deg); } } #tm-nlp-toast { position: fixed; bottom: 90px; right: 20px; background: rgba(0,0,0,0.8); color: #fff; padding: 10px 15px; border-radius: 8px; font-size: 14px; z-index: 2147483647; opacity: 0; pointer-events: none; transition: opacity 0.3s; } /* 弹窗高级样式 */ .tippy-box[data-theme~='light-border'] { background-color: #fff; color: #333; border: 1px solid #ccc; border-radius: 8px; box-shadow: 0 4px 14px rgba(0,0,0,0.15); } .tippy-content { padding: 8px; } .dict-popup { text-align: left; font-size: 14px; line-height: 1.4; max-width: 320px; min-width: 220px; color: #333; } .dict-head-word { font-weight: bold; color: #673AB7; font-size: 18px; line-height: 1.2; } .tm-badge { background-color: #673AB7; color: white; font-size: 11px; padding: 2px 6px; border-radius: 12px; margin-left: 6px; font-weight: normal; } .dict-speaker-btn { display: inline-flex; cursor: pointer; color: #1976D2; padding: 2px; border-radius: 50%; transition: background 0.2s; margin-left: 4px; } .dict-speaker-btn:hover { background-color: rgba(25, 118, 210, 0.1); } .dict-speaker-btn svg { width: 18px; height: 18px; pointer-events: none; } .dict-speaker-btn.playing { color: #E91E63; animation: tm-pulse 1s infinite; } .dict-basic-trans { font-size: 15px; color: #222; font-weight: 500; margin-top: 6px; } .dict-loading { color: #666; font-style: italic; font-size: 13px; padding: 5px; } @keyframes tm-pulse { 0% { transform: scale(1); } 50% { transform: scale(1.15); } 100% { transform: scale(1); } } `); // ========================================== // 2. TTS 与翻译请求模块 // ========================================== let globalAudioPlayer = null; let currentPlayingBtn = null; function initAudioPlayer() { if (!globalAudioPlayer) { globalAudioPlayer = document.createElement('audio'); globalAudioPlayer.style.display = 'none'; document.body.appendChild(globalAudioPlayer); } } function playTTS(text, btn) { if (currentPlayingBtn && currentPlayingBtn !== btn) currentPlayingBtn.classList.remove('playing'); currentPlayingBtn = btn; btn.classList.add('playing'); const url = `https://translate.google.com/translate_tts?ie=UTF-8&client=tw-ob&tl=en&q=${encodeURIComponent(text)}`; globalAudioPlayer.src = url; globalAudioPlayer.onended = () => { if (currentPlayingBtn) currentPlayingBtn.classList.remove('playing'); }; globalAudioPlayer.onerror = () => { if (currentPlayingBtn) currentPlayingBtn.classList.remove('playing'); }; globalAudioPlayer.play().catch(e => btn.classList.remove('playing')); } function fetchTranslation(text) { return new Promise((resolve, reject) => { GM_xmlhttpRequest({ method: "GET", url: `https://translate.googleapis.com/translate_a/single?client=gtx&sl=en&tl=zh-CN&dt=t&dt=bd&q=${encodeURIComponent(text)}`, onload: (res) => { if (res.status === 200) resolve(JSON.parse(res.responseText)); else reject(new Error('Translate error')); }, onerror: reject }); }); } function showToast(msg) { let toast = document.getElementById('tm-nlp-toast'); if (!toast) { toast = document.createElement('div'); toast.id = 'tm-nlp-toast'; document.body.appendChild(toast); } toast.innerText = msg; toast.style.opacity = '1'; setTimeout(() => toast.style.opacity = '0', 3000); } // ========================================== // 3. NLP 语法结构挖掘与精准绑定引擎 // ========================================== let tippyInstances = []; let markInstance = null; function cleanAndFilterPhrase(phrase) { const cleaned = phrase.replace(/[.,/#!$%^&*;:{}=\-_`~()]/g, "").replace(/\s{2,}/g, " ").trim().toLowerCase(); if (!cleaned.includes(' ') || cleaned.length <= 3 || cleaned.length > 40) return null; const copulasAndModals = ['is', 'am', 'are', 'was', 'were', 'be', 'been', 'being', 'can', 'could', 'shall', 'should', 'will', 'would', 'may', 'might', 'must', 'has', 'have', 'had', 'do', 'does', 'did', 'it', 'there']; if (copulasAndModals.includes(cleaned.split(' ')[0])) return null; return cleaned; } function extractAndHighlight() { if (!window.nlp || !window.Mark) { alert("依赖库未加载完毕,请稍后再试!"); return; } const fab = document.getElementById('tm-nlp-fab'); fab.classList.add('processing'); fab.innerHTML = '⚙️'; setTimeout(() => { const mainContent = document.body.innerText; const doc = nlp(mainContent); const grammarRules = [ '#PhrasalVerb', '#Idiom', '#Verb (up|down|in|out|on|off|over|under|away|back|forward|through|with|about|at|from|into|of|to|for)', '#Verb #Pronoun (up|down|in|out|on|off|over|under|away|back|forward|through)', '#Verb (a|an|the) #Adjective? #Noun', '#Adjective (in|of|with|for|to|about|at|on|from)' ]; let rawPhrases = []; grammarRules.forEach(rule => { rawPhrases = rawPhrases.concat(doc.match(rule).out('array')); }); const validPhrases = rawPhrases.map(cleanAndFilterPhrase).filter(Boolean); const uniquePhrases = [...new Set(validPhrases)]; if (uniquePhrases.length === 0) { fab.classList.remove('processing'); fab.innerHTML = '🪄'; showToast("未检测到合适的英语词组。"); return; } if (!markInstance) markInstance = new Mark(document.body); // 异步分批高亮机制:解决跨元素断层问题 function markNextPhrase(index) { if (index >= uniquePhrases.length) { bindTippyToPhrases(); fab.classList.remove('processing'); fab.innerHTML = '✅'; showToast(`🎯 提取成功!发现了 ${uniquePhrases.length} 个词组搭配。`); setTimeout(() => fab.innerHTML = '🪄', 2000); return; } const phrase = uniquePhrases[index]; markInstance.mark(phrase, { className: "tm-phrase", accuracy: "exactly", separateWordSearch: false, acrossElements: true, // 核心穿透能力 exclude: ["script", "style", "noscript", ".tippy-box", "textarea", "input", "#tm-nlp-toast"], each: function(node) { // 无论被切割成多少块,全都绑定同一个完整的短语! node.setAttribute('data-full-phrase', phrase); }, done: () => { // 避免阻塞主线程卡顿 if (index % 10 === 0) setTimeout(() => markNextPhrase(index + 1), 0); else markNextPhrase(index + 1); } }); } markInstance.unmark({ className: "tm-phrase", done: () => markNextPhrase(0) }); }, 150); } // ========================================== // 4. 短语级专业查词弹窗 // ========================================== function bindTippyToPhrases() { if (tippyInstances.length > 0) { tippyInstances.forEach(t => t.destroy()); tippyInstances = []; } tippyInstances = tippy('.tm-phrase', { trigger: 'click', interactive: true, theme: 'light-border', placement: 'bottom', appendTo: () => document.body, maxWidth: 350, onShow(instance) { // 【核心逻辑】:不读取innerText,直接读取刚才锚定的完整词组 const phrase = instance.reference.getAttribute('data-full-phrase'); if (!phrase) return; const loadingDiv = document.createElement('div'); loadingDiv.className = 'dict-popup'; loadingDiv.innerHTML = '
🔍 提取短语涵义中...
'; ['mousedown', 'touchstart', 'click'].forEach(evt => loadingDiv.addEventListener(evt, e => e.stopPropagation(), { capture: true })); instance.setContent(loadingDiv); fetchTranslation(phrase).then((transData) => { // 确保获取完整的翻译拼接 let basicTrans = ''; if (transData[0] && transData[0].length > 0) { transData[0].forEach(item => { if (item[0]) basicTrans += item[0]; }); } if (!basicTrans) basicTrans = '无翻译结果'; const contentDiv = document.createElement('div'); contentDiv.className = 'dict-popup'; contentDiv.innerHTML = `
${phrase} 短语涵义
💡 ${basicTrans}
`; ['mousedown', 'touchstart', 'click'].forEach(evt => contentDiv.addEventListener(evt, e => e.stopPropagation(), { capture: true })); const btn = contentDiv.querySelector('.dict-speaker-btn'); if (btn) { btn.addEventListener('click', (e) => { e.preventDefault(); e.stopPropagation(); const text = btn.getAttribute('data-phrase'); if (!text || btn.classList.contains('playing')) return; playTTS(text, btn); }); } instance.setContent(contentDiv); }).catch(() => { instance.setContent('
网络错误,无法翻译。
'); }); } }); } // ========================================== // 5. 初始化与 UI 挂载 // ========================================== function createUI() { initAudioPlayer(); const fab = document.createElement('div'); fab.id = 'tm-nlp-fab'; fab.innerHTML = '🪄'; fab.title = "点击智能分析:自动提取页面动词短语与习语"; document.body.appendChild(fab); fab.addEventListener('click', (e) => { e.stopPropagation(); extractAndHighlight(); }); } if (document.readyState === 'complete' || document.readyState === 'interactive') { setTimeout(createUI, 500); } else { window.addEventListener('DOMContentLoaded', () => setTimeout(createUI, 500)); } })();