您需要先安装一个扩展,例如 篡改猴、Greasemonkey 或 暴力猴,之后才能安装此脚本。
您需要先安装一个扩展,例如 篡改猴 或 暴力猴,之后才能安装此脚本。
您需要先安装一个扩展,例如 篡改猴 或 暴力猴,之后才能安装此脚本。
您需要先安装一个扩展,例如 篡改猴 或 Userscripts ,之后才能安装此脚本。
您需要先安装一款用户脚本管理器扩展,例如 Tampermonkey,才能安装此脚本。
您需要先安装用户脚本管理器扩展后才能安装此脚本。
Highlight a word/phrase and click the popup to see its definition, with fallback and retry.
// ==UserScript== // @name Quick Dictionary Lookup // @namespace https://example.com/ // @version 1.5 // @description Highlight a word/phrase and click the popup to see its definition, with fallback and retry. // @author ChatGPT // @match *://*/* // @license MIT // @grant GM_addStyle // ==/UserScript== (function () { 'use strict'; let badge = null; let tooltip = null; let lastSelection = ''; // Styles GM_addStyle(` .dict-badge { position: absolute; background: #1f5aff; color: white; padding: 4px 8px; border-radius: 4px; cursor: pointer; font-size: 12px; z-index: 999999; box-shadow: 0 2px 8px rgba(0,0,0,0.3); user-select: none; } .dict-tooltip { position: absolute; background: white; color: #222; padding: 12px; border: 1px solid #ccc; border-radius: 6px; max-width: 320px; font-size: 13px; z-index: 999999; box-shadow: 0 4px 16px rgba(0,0,0,0.2); line-height: 1.3; font-family: system-ui,-apple-system,BlinkMacSystemFont,sans-serif; } .dict-term { font-weight: bold; margin-bottom: 6px; display: block; } .dict-error { color: #b33; font-style: italic; } .dict-close { position: absolute; top: 4px; right: 6px; cursor: pointer; font-weight: bold; } .dict-link { margin-top: 6px; display: inline-block; font-size: 12px; } `); // Utility: clean up term function normalizeTerm(raw) { if (!raw) return ''; let t = raw.trim().replace(/\s+/g, ' '); // strip surrounding punctuation except internal (like "word," -> "word") t = t.replace(/^[^\p{L}\p{N}]+|[^\p{L}\p{N}]+$/gu, ''); return t.toLowerCase(); } // Remove existing badge function removeBadge() { if (badge) { badge.remove(); badge = null; } } // Remove tooltip function removeTooltip() { if (tooltip) { tooltip.remove(); tooltip = null; } } // Fetch definition from dictionaryapi.dev with structured result async function fetchDefinition(term) { try { const resp = await fetch(`https://api.dictionaryapi.dev/api/v2/entries/en/${encodeURIComponent(term)}`, { cache: 'no-store', }); const text = await resp.text(); let data; try { data = JSON.parse(text); } catch (e) { console.warn('Dictionary API: failed to parse JSON for term:', term, 'raw response:', text); throw new Error('Invalid JSON from dictionary API'); } console.debug('Dictionary API response for', term, data); if (!resp.ok || !Array.isArray(data) || !data[0].meanings) { return { found: false, raw: data }; } return { found: true, data }; } catch (err) { console.warn('Dictionary API fetch error for term:', term, err); return { found: false, error: err }; } } // Create badge near selection function createBadge(x, y, rawText) { removeBadge(); removeTooltip(); badge = document.createElement('div'); badge.className = 'dict-badge'; badge.textContent = '📘 Dictionary'; document.body.appendChild(badge); // adjust if offscreen const offsetX = 4; const offsetY = 4; badge.style.left = `${Math.min(x + offsetX, window.scrollX + window.innerWidth - 120)}px`; badge.style.top = `${Math.max(y + offsetY, window.scrollY + 4)}px`; badge.addEventListener('click', () => { showDefinition(rawText, parseInt(badge.style.left, 10), parseInt(badge.style.top, 10) + badge.offsetHeight + 4); }); } // Show tooltip with definition, fallback, and retry function showDefinition(rawTerm, x, y) { const term = normalizeTerm(rawTerm) || rawTerm; removeTooltip(); tooltip = document.createElement('div'); tooltip.className = 'dict-tooltip'; tooltip.style.left = `${x}px`; tooltip.style.top = `${y}px`; // Close button const closeBtn = document.createElement('span'); closeBtn.className = 'dict-close'; closeBtn.textContent = '×'; closeBtn.title = 'Close'; closeBtn.onclick = () => removeTooltip(); tooltip.appendChild(closeBtn); // Title const title = document.createElement('div'); title.className = 'dict-term'; title.textContent = term; tooltip.appendChild(title); // Content placeholder const content = document.createElement('div'); content.textContent = 'Loading...'; tooltip.appendChild(content); document.body.appendChild(tooltip); // Fetch definition fetchDefinition(term).then((result) => { content.innerHTML = ''; if (result.found) { const meanings = result.data[0].meanings.slice(0, 2); meanings.forEach((meaning) => { const part = document.createElement('div'); part.style.marginBottom = '6px'; const partTitle = document.createElement('div'); partTitle.textContent = meaning.partOfSpeech; partTitle.style.fontStyle = 'italic'; part.appendChild(partTitle); meaning.definitions.slice(0, 2).forEach((def) => { const d = document.createElement('div'); d.textContent = `• ${def.definition}`; part.appendChild(d); }); content.appendChild(part); }); } else { let msg = 'Definition not found.'; if (result.raw && result.raw.title) { msg += ` (${result.raw.title})`; } else if (result.error) { msg += ' (API error)'; } const errorDiv = document.createElement('div'); errorDiv.className = 'dict-error'; errorDiv.innerHTML = `${msg} <a href="https://www.google.com/search?q=define+${encodeURIComponent(term)}" target="_blank" rel="noreferrer">Search web</a>`; content.appendChild(errorDiv); } // Retry link const retry = document.createElement('div'); retry.className = 'dict-link'; retry.innerHTML = `<a href="#" title="Try fetching again">Retry</a>`; retry.querySelector('a').addEventListener('click', (e) => { e.preventDefault(); content.textContent = 'Retrying...'; showDefinition(rawTerm, x, y); // re-open (simpler than partial update) }); content.appendChild(retry); // Full search link const more = document.createElement('div'); more.className = 'dict-link'; more.innerHTML = `<a href="https://www.google.com/search?q=define+${encodeURIComponent(term)}" target="_blank" rel="noreferrer">Full search</a>`; content.appendChild(more); }); } // Event: selection finished document.addEventListener('mouseup', (e) => { setTimeout(() => { const selObj = window.getSelection(); const selection = selObj.toString().trim(); if (selection && selection !== lastSelection) { lastSelection = selection; let x = e.pageX; let y = e.pageY; try { const range = selObj.getRangeAt(0); const rect = range.getBoundingClientRect(); x = rect.right + window.scrollX; y = rect.top + window.scrollY; } catch (err) { // ignore, fallback to mouse coords } createBadge(x, y, selection); } else if (!selection) { removeBadge(); removeTooltip(); } }, 10); }); // Close tooltip when clicking outside document.addEventListener('mousedown', (e) => { if (tooltip && !tooltip.contains(e.target) && badge && !badge.contains(e.target)) { removeTooltip(); } }); // Escape closes everything document.addEventListener('keydown', (e) => { if (e.key === 'Escape') { removeBadge(); removeTooltip(); } }); })();// ==UserScript== // @name Quick Dictionary Lookup (Debug & Robust) // @namespace https://example.com/ // @version 0.4 // @description Highlight a word/phrase and click the popup to see its definition, with detailed error reporting, retry, and fallback. // @author ChatGPT // @match *://*/* // @grant GM_addStyle // ==/UserScript== (function () { 'use strict'; let badge = null; let tooltip = null; let lastSelection = ''; GM_addStyle(` .dict-badge { position: absolute; background: #1f5aff; color: white; padding: 4px 8px; border-radius: 4px; cursor: pointer; font-size: 12px; z-index: 999999; box-shadow: 0 2px 8px rgba(0,0,0,0.3); user-select: none; } .dict-tooltip { position: absolute; background: white; color: #222; padding: 12px; border: 1px solid #ccc; border-radius: 6px; max-width: 360px; font-size: 13px; z-index: 999999; box-shadow: 0 4px 16px rgba(0,0,0,0.2); line-height: 1.3; font-family: system-ui,-apple-system,BlinkMacSystemFont,sans-serif; } .dict-term { font-weight: bold; margin-bottom: 6px; display: block; } .dict-error { color: #b33; font-style: italic; } .dict-close { position: absolute; top: 4px; right: 6px; cursor: pointer; font-weight: bold; } .dict-link { margin-top: 6px; display: inline-block; font-size: 12px; } .dict-meta { margin-top: 4px; font-size: 11px; color: #555; } `); function normalizeTerm(raw) { if (!raw) return ''; let t = raw.trim().replace(/\s+/g, ' '); t = t.replace(/^[^\p{L}\p{N}]+|[^\p{L}\p{N}]+$/gu, ''); return t.toLowerCase(); } function removeBadge() { if (badge) { badge.remove(); badge = null; } } function removeTooltip() { if (tooltip) { tooltip.remove(); tooltip = null; } } async function fetchDefinition(term) { try { const resp = await fetch(`https://api.dictionaryapi.dev/api/v2/entries/en/${encodeURIComponent(term)}`, { cache: 'no-store', }); const text = await resp.text(); let data; try { data = JSON.parse(text); } catch (e) { console.warn('Dictionary API: JSON parse failure for term:', term, 'raw:', text); return { found: false, error: new Error('Invalid JSON'), rawText: text, status: resp.status }; } console.debug('Dictionary API response for', term, data); if (!resp.ok || !Array.isArray(data) || !data[0].meanings) { return { found: false, raw: data, status: resp.status }; } return { found: true, data, status: resp.status }; } catch (err) { console.warn('Dictionary API fetch error for term:', term, err); return { found: false, error: err }; } } function createBadge(x, y, rawText) { removeBadge(); removeTooltip(); badge = document.createElement('div'); badge.className = 'dict-badge'; badge.textContent = '📘 Dictionary'; document.body.appendChild(badge); const offsetX = 4; const offsetY = 4; badge.style.left = `${Math.min(x + offsetX, window.scrollX + window.innerWidth - 140)}px`; badge.style.top = `${Math.max(y + offsetY, window.scrollY + 4)}px`; badge.addEventListener('click', () => { showDefinition(rawText, parseInt(badge.style.left, 10), parseInt(badge.style.top, 10) + badge.offsetHeight + 4); }); } function showDefinition(rawTerm, x, y) { const term = normalizeTerm(rawTerm) || rawTerm; removeTooltip(); tooltip = document.createElement('div'); tooltip.className = 'dict-tooltip'; tooltip.style.left = `${x}px`; tooltip.style.top = `${y}px`; const closeBtn = document.createElement('span'); closeBtn.className = 'dict-close'; closeBtn.textContent = '×'; closeBtn.title = 'Close'; closeBtn.onclick = () => removeTooltip(); tooltip.appendChild(closeBtn); const title = document.createElement('div'); title.className = 'dict-term'; title.textContent = term; tooltip.appendChild(title); const content = document.createElement('div'); content.textContent = 'Loading...'; tooltip.appendChild(content); document.body.appendChild(tooltip); fetchDefinition(term).then((result) => { content.innerHTML = ''; if (result.found) { const meanings = result.data[0].meanings.slice(0, 2); meanings.forEach((meaning) => { const part = document.createElement('div'); part.style.marginBottom = '6px'; const partTitle = document.createElement('div'); partTitle.textContent = meaning.partOfSpeech; partTitle.style.fontStyle = 'italic'; part.appendChild(partTitle); meaning.definitions.slice(0, 2).forEach((def) => { const d = document.createElement('div'); d.textContent = `• ${def.definition}`; part.appendChild(d); }); content.appendChild(part); }); } else { let msg = 'Definition not found.'; if (result.status) msg += ` (HTTP ${result.status})`; if (result.raw && result.raw.title) { msg += ` — ${result.raw.title}`; } else if (result.error) { msg += ` — ${result.error.message || 'Network/other error'}`; } const errorDiv = document.createElement('div'); errorDiv.className = 'dict-error'; errorDiv.innerHTML = `${msg} <br><a href="https://www.google.com/search?q=define+${encodeURIComponent(term)}" target="_blank" rel="noreferrer">Search web</a>`; content.appendChild(errorDiv); } const meta = document.createElement('div'); meta.className = 'dict-meta'; const parts = []; if (result.status !== undefined) parts.push(`Status: ${result.status}`); if (result.error) parts.push(`Error: ${result.error.message}`); if (parts.length) meta.textContent = parts.join(' | '); if (parts.length) content.appendChild(meta); const retry = document.createElement('div'); retry.className = 'dict-link'; retry.innerHTML = `<a href="#" title="Retry lookup">Retry</a>`; retry.querySelector('a').addEventListener('click', (e) => { e.preventDefault(); content.textContent = 'Retrying...'; showDefinition(rawTerm, x, y); }); content.appendChild(retry); const more = document.createElement('div'); more.className = 'dict-link'; more.innerHTML = `<a href="https://www.google.com/search?q=define+${encodeURIComponent(term)}" target="_blank" rel="noreferrer">Full search</a>`; content.appendChild(more); }); } document.addEventListener('mouseup', (e) => { setTimeout(() => { const selObj = window.getSelection(); const selection = selObj.toString().trim(); if (selection && selection !== lastSelection) { lastSelection = selection; let x = e.pageX; let y = e.pageY; try { const range = selObj.getRangeAt(0); const rect = range.getBoundingClientRect(); x = rect.right + window.scrollX; y = rect.top + window.scrollY; } catch (_) {} createBadge(x, y, selection); } else if (!selection) { removeBadge(); removeTooltip(); } }, 10); }); document.addEventListener('mousedown', (e) => { if (tooltip && !tooltip.contains(e.target) && badge && !badge.contains(e.target)) { removeTooltip(); } }); document.addEventListener('keydown', (e) => { if (e.key === 'Escape') { removeBadge(); removeTooltip(); } }); })();