您需要先安装一个扩展,例如 篡改猴、Greasemonkey 或 暴力猴,之后才能安装此脚本。
您需要先安装一个扩展,例如 篡改猴 或 暴力猴,之后才能安装此脚本。
您需要先安装一个扩展,例如 篡改猴 或 暴力猴,之后才能安装此脚本。
您需要先安装一个扩展,例如 篡改猴 或 Userscripts ,之后才能安装此脚本。
您需要先安装一款用户脚本管理器扩展,例如 Tampermonkey,才能安装此脚本。
您需要先安装用户脚本管理器扩展后才能安装此脚本。
Japanese dictionary, pop-up, using Jisho
// ==UserScript== // @name NipponDict // @namespace http://tampermonkey.net/ // @version 1.0 // @description Japanese dictionary, pop-up, using Jisho // @author iljann // @match *://*/* // @grant GM_xmlhttpRequest // @grant GM_addStyle // ==/UserScript== (function() { 'use strict'; GM_addStyle(` #jp-popup { position: fixed; z-index: 999999; display: none; background: #FEFEFE; border: 1px solid #E8E8E8; border-radius: 12px; box-shadow: 0 8px 32px rgba(0,0,0,0.08); font-family: system-ui, sans-serif; max-width: 400px; min-width: 280px; padding: 0; } #jp-header { background: linear-gradient(135deg, #FFF8F0, #F0F8FF); color: #6B7280; padding: 12px 16px; border-radius: 12px 12px 0 0; font-size: 13px; font-weight: 500; position: relative; } #jp-header::before { content: "あ"; color: #F59E0B; font-weight: 600; margin-right: 8px; } #jp-close { position: absolute; top: 8px; right: 12px; background: none; border: none; color: #9CA3AF; cursor: pointer; font-size: 16px; } #jp-content { padding: 16px; background: #FEFEFE; color: #374151; max-height: 350px; overflow-y: auto; border-radius: 0 0 12px 12px; } .jp-word { font-size: 20px; color: #1F2937; font-weight: 600; text-align: center; padding: 10px; background: linear-gradient(135deg, #FFF7ED, #FEF3C7); border-radius: 6px; margin-bottom: 12px; border-left: 3px solid #F59E0B; } .jp-reading { font-size: 16px; color: #D97706; text-align: center; margin-bottom: 12px; font-style: italic; } .jp-source { background: #FEF3C7; color: #D97706; padding: 2px 6px; font-size: 9px; border-radius: 3px; margin-right: 6px; text-transform: uppercase; font-weight: 500; } .jp-def { font-size: 14px; line-height: 1.5; color: #374151; padding: 12px; background: #FEFEFE; border: 1px solid #F3F4F6; border-radius: 6px; margin-bottom: 10px; } .jp-pos { background: #F1F5F9; color: #64748B; padding: 2px 6px; font-size: 9px; border-radius: 3px; margin-right: 6px; text-transform: uppercase; font-weight: 500; } .jp-tags { margin-top: 6px; } .jp-tag { background: #E0E7FF; color: #3730A3; padding: 1px 4px; font-size: 8px; border-radius: 2px; margin-right: 4px; text-transform: lowercase; } .jp-loading { text-align: center; padding: 30px; color: #6B7280; animation: pulse 1.5s ease-in-out infinite; } @keyframes pulse { 0%, 100% { opacity: 1; } 50% { opacity: 0.5; } } .jp-error { text-align: center; padding: 20px; color: #6B7280; font-size: 13px; } `); const popup = document.createElement('div'); popup.id = 'jp-popup'; popup.innerHTML = ` <div id="jp-header">NipponDict <button id="jp-close">×</button></div> <div id="jp-content"></div> `; document.body.appendChild(popup); const content = document.getElementById('jp-content'); const closeBtn = document.getElementById('jp-close'); const japaneseRegex = /[\u3040-\u309F\u30A0-\u30FF\u4E00-\u9FAF]/; let isVisible = false; let timeout; function close() { popup.style.display = 'none'; isVisible = false; } function position(x, y) { let left = x + 15; let top = y + 15; if (left + 400 > window.innerWidth) left = x - 415; if (top + 250 > window.innerHeight) top = y - 265; popup.style.left = Math.max(10, left) + 'px'; popup.style.top = Math.max(10, top) + 'px'; } function getJapaneseText(element) { const selection = window.getSelection().toString().trim(); if (selection && japaneseRegex.test(selection)) return selection; if (!element) return null; const text = element.textContent || element.innerText || ''; const match = text.match(/[\u3040-\u309F\u30A0-\u30FF\u4E00-\u9FAF]+/); return match ? match[0] : null; } // Query Jisho API function queryJisho(word) { return new Promise((resolve) => { GM_xmlhttpRequest({ method: 'GET', url: `https://jisho.org/api/v1/search/words?keyword=${encodeURIComponent(word)}`, headers: { 'User-Agent': 'Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36' }, onload: function(response) { try { if (response.status === 200) { const data = JSON.parse(response.responseText); if (data.data && data.data.length > 0) { const results = data.data.slice(0, 4).map(entry => { const japanese = entry.japanese[0] || {}; const senses = entry.senses || []; return { word: japanese.word || japanese.reading || word, reading: japanese.reading || '', senses: senses.slice(0, 3).map(sense => ({ pos: sense.parts_of_speech || [], definitions: sense.english_definitions || [], tags: sense.tags || [] })) }; }); resolve(results); } else { resolve([]); } } else { console.log('Jisho request failed:', response.status); resolve([]); } } catch (e) { console.log('Jisho parse error:', e); resolve([]); } }, onerror: function(error) { console.log('Jisho request error:', error); resolve([]); } }); }); } function displayResults(word, results) { if (results.length === 0) { content.innerHTML = `<div class="jp-error">No definition found for "${word}" on Jisho</div>`; return; } let html = ''; results.forEach((result, index) => { html += `<div class="jp-word">${result.word}</div>`; if (result.reading && result.reading !== result.word) { html += `<div class="jp-reading">${result.reading}</div>`; } result.senses.forEach((sense, senseIndex) => { html += `<div class="jp-def">`; html += `<span class="jp-source">jisho</span>`; // Parts of speech if (sense.pos && sense.pos.length > 0) { sense.pos.forEach(pos => { html += `<span class="jp-pos">${pos}</span>`; }); } // Definitions html += `<div style="margin-top: 6px;">${sense.definitions.join('; ')}</div>`; // Tags if (sense.tags && sense.tags.length > 0) { html += `<div class="jp-tags">`; sense.tags.forEach(tag => { html += `<span class="jp-tag">${tag}</span>`; }); html += `</div>`; } html += `</div>`; }); }); content.innerHTML = html; } async function show(x, y, word) { content.innerHTML = '<div class="jp-loading">Searching...</div>'; popup.style.display = 'block'; position(x, y); isVisible = true; try { const results = await queryJisho(word); displayResults(word, results); } catch (error) { console.log('Dictionary query error:', error); displayResults(word, []); } } closeBtn.onclick = close; document.addEventListener('mousemove', e => { if (isVisible) return; clearTimeout(timeout); timeout = setTimeout(() => { const word = getJapaneseText(e.target); if (word) show(e.clientX, e.clientY, word); }, 500); }); document.addEventListener('dblclick', e => { const word = getJapaneseText(e.target); if (word) { close(); setTimeout(() => show(e.clientX, e.clientY, word), 100); } }); document.addEventListener('click', e => { if (!popup.contains(e.target)) close(); }); document.addEventListener('keydown', e => { if (e.ctrlKey && e.shiftKey && e.key === 'J') { e.preventDefault(); const word = window.getSelection().toString().trim(); if (word && japaneseRegex.test(word)) { isVisible ? close() : show(100, 100, word); } } }); console.log('Online'); })();