LunaLens

通过HTTP API连接LunaTranslator实现浏览器上的原文的分词、翻译、朗读和查词功能

// ==UserScript==
// @name         LunaLens
// @namespace    http://tampermonkey.net/
// @license      MIT
// @version      0.2.7
// @description  通过HTTP API连接LunaTranslator实现浏览器上的原文的分词、翻译、朗读和查词功能
// @description:en Split, translate, read and query words on the browser through the HTTP API of LunaTranslator
// @description:ja LunaTranslatorのHTTP APIを通じてブラウザ上で分詞、翻訳、読み上げ、辞書検索機能を実現
// @author       Raindrop213
// @match        *://*/*
// @grant        GM_xmlhttpRequest
// @connect      *
// ==/UserScript==

(function() {
    'use strict';

    // 默认配置
    const DEFAULT_CONFIG = {
        // ※重点:填写你的服务器地址,同步设置@connect如: @connect 192.168.6.229
        API_URL: 'http://127.0.0.1:2333',

        // 分句设置
        // 分句:断句的符号基准
        SENTENCE_DELIMITERS: '。..!?!?…',
        // 分句:句子字数阈值
        SENTENCE_LENGTH: 50, 
        // 最小内容长度
        MIN_CONTENT_LENGTH: 2,
        // 最大内容长度
        MAX_CONTENT_LENGTH: 1000,
        // 是否移除注音

        // 选择器设置
        // 选择的标签
        INCLUDE_TAGS: 'p, h1, h2, h3, h4, h5, h6',
        // 排除的标签
        EXCLUDE_TAGS: '',
        // 包含的class id
        INCLUDE_CLASS_IDS: '',
        // 排除的class id
        EXCLUDE_CLASS_IDS: '',
        // 停止容器
        STOP_CONTAINERS: 'article, main, section, div.content, div.main-content',

        // 顶栏常用设置
        // 是否使用句子模式
        DISPLAY_SENTENCE_MODE: false,
        // 是否打开翻译
        TRANSLATION_ENABLED: false,
        // 是否激活标签就自动朗读
        TTS_AUTO: false,
        // 是否打开设置栏
        SETTING_DISPLAY: false,
        BUTTON_TEXT: {
            TTS: {true: 'TTS:auto', false: 'TTS'},
            TRANSLATION: {true: '翻译:auto', false: '翻译'},
            DISPLAY: {true: '句子', false: '段落'}
        },

        // 其他设置
        // API请求等待的最大时限
        TIMEOUT: 5000
    };
    
    // 当前配置,初始化为默认配置的副本
    const CONFIG = JSON.parse(JSON.stringify(DEFAULT_CONFIG));

    // 面板样式
    const STYLE =  `
        .lunalens-highlighted {
            outline: 2px solid rgba(59, 122, 83, 0.52) !important;
        }
        .lunalens-word {
            display: inline-block;
            position: relative;
            margin: 1px;
            cursor: pointer;
        }
        .lunalens-word.flash {
            animation: word-flash 0.3s ease-out;
        }
        @keyframes word-flash {
            0% { background-color: rgba(255, 206, 30, 0.5); }
            100% { background-color: transparent; }
        }
        /* 仅在词典窗内使用的高亮样式 */
        .lunalens-dict-context .lunalens-word.active-word {
            background-color:rgba(255, 206, 30, 0.5);
        }
        .lunalens-word rt {
            font-size: 0.7em;
            color: #b91a1a;
        }
        /* LunaLens面板 */
        .lunalens-panel {
            position: fixed;
            bottom: 0;
            left: 0;
            right: 0;
            height: 92%;
            background-color: #fff;
            border-top: 1px solid #ccc;
            box-shadow: 0 -2px 10px rgba(0,0,0,0.1);
            z-index: 9999;
            display: flex;
            flex-direction: column;
            font-family: sans-serif;
            transition: transform 0.2s ease;
            transform: translateY(100%);
            writing-mode: horizontal-tb !important;
            -webkit-writing-mode: horizontal-tb !important;
        }
        .lunalens-panel.visible {
            transform: translateY(0);
        }
        .lunalens-panel input::placeholder {
            color: #aaa;
            opacity: 0.8;
        }
        .lunalens-header {
            display: flex;
            justify-content: space-between;
            background: #ffffff;
            border-bottom: 1px solid #ddd;
            height: 40px;
        }
        .lunalens-title {
            font-size: 16px;
            font-weight: bold;
            padding: 8px 10px;
            margin: 0;
            line-height: 20px;
            cursor: pointer;
            transition: color 0.2s ease;
        }
        .lunalens-title:hover {
            color: #007acc;
        }
        .lunalens-header-buttons {
            display: flex;
            align-items: stretch;
            height: 100%;
        }
        .lunalens-header-button {
            cursor: pointer;
            color: #666;
            font-size: 14px;
            padding: 0 12px;
            margin: 0;
            border: none;
            border-radius: 0;
            border-left: 1px solid #ddd;
            transition: all 0.2s ease;
            display: flex;
            align-items: center;
            justify-content: center;
        }
        .lunalens-square-button {
            width: 40px;
            color: white;
            border: none;
            border-radius: 0;
            cursor: pointer;
            display: flex;
            align-items: center;
            justify-content: center;
            font-size: 20px;
            margin: 0;
        }
        .lunalens-setting-toggle {
            background-color: #929292;
        }
        .lunalens-setting-toggle.active {
            background-color: #1288ab;
        }
        .lunalens-close {
            background-color: #000000;
        }
        .lunalens-dict-context-wrapper {
            position: relative;
            background: #f9f9f9;
            border-bottom: 1px solid #eee;
            max-height: 20vh;
            overflow: hidden;
        }
        .lunalens-context-buttons {
            position: absolute;
            bottom: 4px;
            right: 4px;
            display: flex;
            gap: 4px;
            z-index: 10;
        }
        .lunalens-dict-context {
            padding: 8px 10px 30px 10px;
            font-size: 15px;
            line-height: 1.5;
            overflow-y: auto;
            height: 100%;
            box-sizing: border-box;
        }
        .lunalens-dict-query-box {
            display: flex;
            padding: 0;
            border-bottom: 1px solid #eee;
            height: 40px;
        }
        .lunalens-dict-query-input {
            flex: 1;
            padding: 0 10px;
            border: 1px solid transparent;
            border-right: 1px solid #ddd;
            border-radius: 0;
            margin: 0;
            height: 100%;
            outline: none;
            box-sizing: border-box;
        }
        .lunalens-dict-query-input:focus {
            border: 1px solid #000000;
        }
        .lunalens-dict-query-button {
            background: #23ab12;
        }
        .lunalens-tts-button {
            background: #a51dd1;
        }
        .lunalens-context-copy-button {
            width: 32px;
            height: 32px;
            color: #4a90e2;
            background: rgba(74, 144, 226, 0.1);
            border: 1px solid rgba(74, 144, 226, 0.3);
            cursor: pointer;
            display: flex;
            align-items: center;
            justify-content: center;
            font-size: 16px;
            transition: all 0.2s ease;
        }
        .lunalens-context-translate-button {
            width: 32px;
            height: 32px;
            color: #50c878;
            background: rgba(80, 200, 120, 0.1);
            border: 1px solid rgba(80, 200, 120, 0.3);
            cursor: pointer;
            display: flex;
            align-items: center;
            justify-content: center;
            font-size: 14px;
            font-weight: bold;
            transition: all 0.2s ease;
        }
        .lunalens-context-tts-button {
            width: 32px;
            height: 32px;
            color: #a51dd1;
            background:rgba(165, 29, 209, 0.1);
            border: 1px solid rgba(165, 29, 209, 0.3);
            cursor: pointer;
            display: flex;
            align-items: center;
            justify-content: center;
            font-size: 18px;
            transition: all 0.2s ease;
        }
        .lunalens-dict-content {
            flex: 1;
            display: flex;
            flex-direction: column;
            overflow: hidden;
        }
        .lunalens-dict-tabs {
            display: flex;
            flex-direction: row;
            background: #f9f9f9;
            overflow-x: auto;
            white-space: nowrap;
        }
        .lunalens-dict-tab {
            padding: 8px 15px;
            cursor: pointer;
            border-right: 1px solid #eee;
            font-size: 13px;
        }
        .lunalens-dict-tab.active {
            background: #fff;
            font-weight: bold;
            border-top: 3px solid #b91a1a;
            border-left: none;
        }
        .lunalens-dict-entries {
            flex: 1;
            overflow-y: auto;
            padding-top: 10px;
        }
        .lunalens-dict-entry {
            display: none;
        }
        .lunalens-dict-entry.active {
            display: block;
        }
        .lunalens-dict-loading {
            text-align: center;
            padding: 20px;
            color: #666;
        }
        /* 翻译区域样式 */
        .lunalens-translation {
            padding: 8px 10px;
            border-bottom: 1px solid #e0e6f5;
            font-size: 13px;
            line-height: 1.5;
            max-height: 15vh;
            overflow-y: auto;
        }
        .lunalens-translator-item {
            position: relative;
            padding-right: 40px;
        }
        .lunalens-translator-name {
            position: absolute;
            right: 0;
            top: 0;
            font-size: 10px;
            color: #bbb;
            font-style: italic;
        }
        .lunalens-hr {
            border: none;
            border-top: 1px dashed #c1c1c17d;
            margin: 5px 0;
        }
        /* 设置窗口样式 */
        .lunalens-setting-window {
            display: none;
            flex-direction: column;
            width: 100%;
            height: 100%;
            overflow: hidden;
            padding: 10px;
            box-sizing: border-box;
            overflow-y: auto;
        }
        .lunalens-setting-window.visible {
            display: flex;
        }
        .lunalens-dict-window {
            display: none;
            flex-direction: column;
            width: 100%;
            height: 100%;
            overflow: hidden;
        }
        .lunalens-dict-window.visible {
            display: flex;
        }
        .lunalens-setting-api-url {
            padding: 8px 10px;
            border-bottom: 1px solid #eee;
        }
        .lunalens-setting-api-url label {
            display: block;
            margin-bottom: 5px;
            font-weight: bold;
            font-size: 13px;
            color: #333;
        }
        .lunalens-setting-api-url input {
            width: 100%;
            padding: 6px 10px;
            border: 1px solid #ddd;
            border-radius: 0;
            font-size: 14px;
            outline: none;
            box-sizing: border-box;
        }
        .lunalens-setting-api-url input:focus {
            border: 1px solid #000000;
        }
        .lunalens-setting-tabs {
            display: flex;
            flex-direction: row;
            background: #f9f9f9;
            overflow-x: auto;
            white-space: nowrap;
        }
        .lunalens-setting-tab {
            padding: 8px 15px;
            cursor: pointer;
            border-right: 1px solid #eee;
            font-size: 13px;
        }
        .lunalens-setting-tab.active {
            background: #fff;
            font-weight: bold;
            border-top: 3px solid #b91a1a;
            border-bottom: none;
            border-left: none;
        }
        .lunalens-setting-contents {
            flex: 1;
            overflow-y: auto;
        }
        .lunalens-setting-content {
            display: none;
            padding: 10px;
            border-top: none;
        }
        .lunalens-setting-content.active {
            display: block;
        }
        .lunalens-setting-item {
            margin-bottom: 12px;
            padding-bottom: 8px;
        }
        .lunalens-setting-item:last-child {
            border-bottom: none;
        }
        .lunalens-setting-item label {
            display: block;
            margin-bottom: 5px;
            font-weight: bold;
            font-size: 13px;
        }
        .lunalens-setting-item input[type="text"] {
            width: 100%;
            padding: 6px 10px;
            border: 1px solid #ddd;
            border-radius: 0;
            outline: none;
            box-sizing: border-box;
        }
        .lunalens-setting-item input[type="text"]:focus {
            border: 1px solid #000000;
        }
        .lunalens-setting-item input[type="checkbox"] {
            margin-right: 8px;
            vertical-align: middle;
        }
        .lunalens-setting-description {
            font-size: 12px;
            color: #666;
            margin-top: 5px;
        }
        .lunalens-setting-description kbd {
            background-color:rgba(223, 223, 223, 0.5);
            padding: 0px 3px;
            border-radius: 3px;
        }
        .lunalens-setting-button {
            background-color: #23ab12;
            color: white;
            border: none;
            padding: 8px 16px;
            cursor: pointer;
            border-radius: 0;
            margin-top: 10px;
            font-size: 14px;
            display: flex;
            align-items: center;
            justify-content: center;
            align-self: flex-start;
            margin-right: 10px;
        }
        .lunalens-reset-settings {
            background-color: #cc3333;
        }
        .lunalens-setting-buttons {
            display: flex;
            flex-direction: row;
        }
        .lunalens-toast {
            position: fixed;
            top: 20px;
            left: 50%;
            transform: translateX(-50%);
            background-color: rgba(0, 0, 0, 0.8);
            color: white;
            padding: 10px 15px;
            border-radius: 4px;
            z-index: 10001;
            display: none;
            animation: fadeInOut 2s ease-in-out;
            box-shadow: 0 2px 8px rgba(0,0,0,0.2);
            font-size: 14px;
            max-width: 80%;
            text-align: center;
            writing-mode: horizontal-tb;
        }
        @keyframes fadeInOut {
            0% { opacity: 0; transform: translate(-50%, -10px); }
            10% { opacity: 1; transform: translate(-50%, 0); }
            90% { opacity: 1; transform: translate(-50%, 0); }
            100% { opacity: 0; transform: translate(-50%, -10px); }
        }
    `

    // 全局变量
    let activeElement = null;
    let originalContent = null;
    let dictionaryPanel = null;
    let currentWord = '';
    let activeWordElements = []; // 改为数组存储所有激活的单词元素
    let contextSentence = null;
    let translationArea = null; // 翻译区域
    let lastTranslatedText = ''; // 上次翻译的文本,用于避免重复翻译
    let currentAudio = null; // 当前播放的音频对象
    let pendingTTSRequests = []; // 待处理的TTS请求数组

    // 添加样式到文档
    function addStyle(doc) {
        if (!doc.querySelector('#anon-simple-style')) {
            const style = doc.createElement('style');
            style.id = 'anon-simple-style';
            style.textContent = STYLE;
            doc.head.appendChild(style);
        }
    }

    // 复制文本到剪贴板
    function copyToClipboard(text) {
        navigator.clipboard.writeText(text)
            .then(() => console.log(`Copy: ${text}`))
            .catch(err => console.log('Failed to copy text: ', err));
    }

    // 获取纯文本(移除rp、rt)
    function getPlainText(html) {
        const div = document.createElement('div');
        div.innerHTML = html;
        
        // 先找出所有ruby元素
        div.querySelectorAll('ruby').forEach(ruby => {
            // 创建文本节点,只包含主要内容(不包含rt中的注音)
            const mainText = Array.from(ruby.childNodes)
                .filter(node => node.nodeName !== 'RT' && node.nodeName !== 'RP')
                .map(node => node.textContent)
                .join('');
                
            // 替换整个ruby标签为纯文本
            const textNode = document.createTextNode(mainText);
            ruby.parentNode.replaceChild(textNode, ruby);
        });
        
        return div.textContent || '';
    }

    // 辅助函数:判断片假名和平假名是否等价
    function isKanaEquivalent(word, kana) {
        if (!word || !kana) return false;
        // 简单转换为片假名进行比较
        const toKatakana = str => {
            const hiraRange = [0x3041, 0x3096];
            return str.split('')
                .map(char => {
                    const code = char.charCodeAt(0);
                    if (code >= hiraRange[0] && code <= hiraRange[1]) {
                        return String.fromCharCode(code + 0x60);
                    }
                    return char;
                })
                .join('');
        };
        return toKatakana(word) === toKatakana(kana);
    }
    
    // 片假名转平假名
    function katakanaToHiragana(text) {
        if (!text) return '';
        
        // 片假名的Unicode范围是:U+30A0 to U+30FF
        return text.replace(/[\u30A0-\u30FF]/g, function(match) {
            const code = match.charCodeAt(0) - 0x60;
            return String.fromCharCode(code);
        });
    }

    // 处理分词API请求
    function processMecabAPI(text, callback) {
        GM_xmlhttpRequest({
            method: 'GET',
            url: `${CONFIG.API_URL}/api/mecab?text=${encodeURIComponent(text)}`,
            onload: function(response) {
                if (response.status === 200) {
                    try {
                        const data = JSON.parse(response.responseText);
                        callback(data);
                    } catch (e) {
                        console.error('解析API响应失败:', e);
                        showToast('分词API请求失败,请检查API连接', true);
                        callback(null);
                    }
                } else {
                    console.error('API请求失败:', response.status);
                    showToast('API请求失败,HTTP状态码: ' + response.status, true);
                    callback(null);
                }
            },
            onerror: function(error) {
                console.error('API请求错误:', error);
                showToast('分词API请求失败,请检查API连接', true);
                callback(null);
            },
            ontimeout: function() {
                console.error('API请求超时');
                showToast('分词API请求超时', true);
                callback(null);
            },
            timeout: CONFIG.TIMEOUT
        });
    }

    // 显示设置面板的辅助函数
    function showSettingsPanel() {
        // 先显示词典面板
        showPanel();
        
        // 然后切换到设置页面
        if (dictionaryPanel) {
            dictionaryPanel.querySelector('.lunalens-dict-window').classList.remove('visible');
            dictionaryPanel.querySelector('.lunalens-setting-window').classList.add('visible');
            dictionaryPanel.querySelector('.lunalens-setting-toggle').classList.add('active');
        }
    }

    // 生成带注音的HTML标记,添加唯一索引
    function generateRubyHTML(word, index) {
        const indexAttr = `data-original-index="${index}"`;
        
        if (word.isdeli || !word.kana || isKanaEquivalent(word.word, word.kana)) {
            return `<span class="lunalens-word" data-word="${word.word}" ${indexAttr}>${word.word}</span>`;
        } else {
            // 将片假名转换为平假名
            const hiragana = katakanaToHiragana(word.kana);
            return `<span class="lunalens-word" data-word="${word.word}" ${indexAttr}><ruby>${word.word}<rt>${hiragana}</rt></ruby></span>`;
        }
    }

    // 处理文本并生成带注音的分句HTML
    function processTextWithTokenization(html, callback) {
        // 1. 获取纯文本
        const plainText = getPlainText(html);
        
        // 2. 一次性请求API进行分词
        processMecabAPI(plainText, function(words) {
            if (!words) {
                callback(html); // 如果API失败,返回原HTML
                showSettingsPanel();
                return;
            }
            
            // 3. 按句子分隔符构建句子数组
            const sentences = [];
            let currentSentence = [];
            let currentSentenceLength = 0;
            let wordIndex = 0;
            
            // 遍历所有词汇,构建句子
            words.forEach(word => {
                // 生成当前词的HTML,添加唯一索引
                const wordHTML = generateRubyHTML(word, wordIndex++);
                currentSentence.push(wordHTML);
                currentSentenceLength += word.word.length;
                
                // 如果当前词是句子分隔符,检查是否应结束当前句子
                if (CONFIG.SENTENCE_DELIMITERS.indexOf(word.word) !== -1 && currentSentenceLength >= CONFIG.SENTENCE_LENGTH) {
                    sentences.push(`<span class="lunalens-sentence">${currentSentence.join('')}</span>`);
                    currentSentence = [];
                    currentSentenceLength = 0;
                }
            });
            
            // 处理剩余的词
            if (currentSentence.length > 0) {
                sentences.push(`<span class="lunalens-sentence">${currentSentence.join('')}</span>`);
            }
            
            callback(sentences.join(''));
        });
    }

    // 查询按钮点击事件(添加下一个单词)
    function setupQueryButton(queryButton, queryInput) {
        queryButton.addEventListener('click', function() {
            if (activeWordElements.length > 0) {
                // 获取最后一个激活的单词元素的索引
                const lastActiveIndex = activeWordElements[activeWordElements.length - 1];
                let lastActiveElement;
                
                // 在主页和词典上下文中查找元素
                const allWords = document.querySelectorAll('.lunalens-word');
                for (const word of allWords) {
                    if (word.dataset.originalIndex === lastActiveIndex || 
                        (typeof lastActiveIndex === 'object' && 
                         lastActiveIndex.dataset && 
                         lastActiveIndex.dataset.originalIndex === word.dataset.originalIndex)) {
                        lastActiveElement = word;
                        // 一旦在主页上找到匹配的元素,优先使用
                        if (activeElement.contains(word)) {
                            break;
                        }
                    }
                }
                
                if (!lastActiveElement) return;
                
                // 寻找下一个单词元素
                let nextWord = null;
                if (activeElement.contains(lastActiveElement)) {
                    // 如果最后激活的单词在主页上,直接找下一个相邻元素
                    nextWord = lastActiveElement.nextElementSibling;
                    while (nextWord && !nextWord.classList.contains('lunalens-word')) {
                        nextWord = nextWord.nextElementSibling;
                    }
                } else {
                    // 如果最后激活的单词在词典上下文中,需要找到主页上对应的单词,再找下一个
                    const mainPageWords = activeElement.querySelectorAll('.lunalens-word');
                    for (let i = 0; i < mainPageWords.length; i++) {
                        if (mainPageWords[i].dataset.originalIndex === lastActiveIndex) {
                            if (i < mainPageWords.length - 1) {
                                nextWord = mainPageWords[i + 1];
                            }
                            break;
                        }
                    }
                }
                
                if (nextWord && nextWord.classList.contains('lunalens-word')) {
                    
                    // 记录下一个单词,保留之前的记录
                    activeWordElements.push(nextWord.dataset.originalIndex || nextWord);
                    
                    // 获取当前查询框内容
                    const currentQuery = queryInput.value.trim();
                    
                    // 将下一个单词添加到查询框
                    queryInput.value = currentQuery + nextWord.dataset.word;
                    
                    // 更新上下文
                    updateContext();
                    
                    // 触发查询
                    lookupWord(queryInput.value);
                }
            }
        });
    }
    
    // 上下文区域点击事件处理
    function setupContextAreaEvents(contextArea, queryInput) {
        // 使用事件委托,避免在每次更新上下文时都要重新绑定事件
        contextArea.addEventListener('click', function(e) {
            // 阻止事件冒泡到document,防止触发deactivateElement
            e.stopPropagation();
            
            // 寻找被点击的单词元素,包括临时容器中的元素
            let targetWord = null;
            if (e.target.classList && e.target.classList.contains('lunalens-word')) {
                targetWord = e.target;
            } else {
                targetWord = e.target.closest('.lunalens-word');
            }
            
            if (targetWord) {
                // 清除所有已激活的高亮样式
                contextArea.querySelectorAll('.lunalens-word.active-word').forEach(word => {
                    word.classList.remove('active-word');
                });
                
                // 清空激活单词数组
                activeWordElements = [];
                
                // 直接给当前单词添加高亮类
                targetWord.classList.add('active-word');
                
                // 记录当前激活的单词
                activeWordElements.push(targetWord.dataset.originalIndex || targetWord);
                
                // 更新查询框内容
                queryInput.value = targetWord.dataset.word;
                
                // 触发查询
                lookupWord(targetWord.dataset.word);
                
                // 找到主页上对应的单词并添加临时闪烁效果
                const originalIndex = targetWord.dataset.originalIndex;
                if (originalIndex && activeElement) {
                    const mainPageWord = activeElement.querySelector(`.lunalens-word[data-original-index="${originalIndex}"]`);
                    if (mainPageWord) {
                        mainPageWord.classList.add('flash');
                        setTimeout(() => {
                            mainPageWord.classList.remove('flash');
                        }, 1000);
                    }
                }
            }
        });
    }

    // 创建词典面板
    function createDictionaryPanel() {
        // 检查是否已存在
        if (document.querySelector('.lunalens-panel')) {
            return document.querySelector('.lunalens-panel');
        }

        // 创建面板
        const panel = document.createElement('div');
        panel.className = 'lunalens-panel';
        panel.innerHTML = `
            <div class="lunalens-header">
                <div class="lunalens-title">LunaLens</div>
                <div class="lunalens-header-buttons">
                    <span class="lunalens-header-button lunalens-auto-tts-toggle" title="自动朗读开关">
                        ${CONFIG.BUTTON_TEXT.TTS[CONFIG.TTS_AUTO]}
                    </span>
                    <span class="lunalens-header-button lunalens-translation-toggle" title="切换翻译功能">
                        ${CONFIG.BUTTON_TEXT.TRANSLATION[CONFIG.TRANSLATION_ENABLED]}
                    </span>
                    <span class="lunalens-header-button lunalens-context-toggle" title="切换句子/段落">
                        ${CONFIG.BUTTON_TEXT.DISPLAY[CONFIG.DISPLAY_SENTENCE_MODE]}
                    </span>
                    <button class="lunalens-square-button lunalens-setting-toggle" title="设置">⚙</button>
                    <button class="lunalens-square-button lunalens-close" title="关闭面板">×</button>
                </div>
            </div>
            <div class="lunalens-dict-window">
                <div class="lunalens-dict-context-wrapper">
                    <div class="lunalens-dict-context"></div>
                    <div class="lunalens-context-buttons">
                        <button class="lunalens-context-copy-button" title="复制内容">⧉</button>
                        <button class="lunalens-context-translate-button" title="翻译内容">翻</button>
                        <button class="lunalens-context-tts-button" title="朗读上下文">♬</button>
                    </div>
                </div>
                <div class="lunalens-translation"></div>
                <div class="lunalens-dict-query-box">
                    <input type="text" class="lunalens-dict-query-input" placeholder="输入要查询的单词">
                    <button class="lunalens-square-button lunalens-dict-query-button">+</button>
                    <button class="lunalens-square-button lunalens-tts-button" title="朗读单词">♬</button>
                </div>
                <div class="lunalens-dict-content">
                    <div class="lunalens-dict-tabs"></div>
                    <div class="lunalens-dict-entries">
                        <div class="lunalens-dict-loading">请点击任意单词或输入要查询的词</div>
                    </div>
                </div>
            </div>
            <div class="lunalens-setting-window">
                <div class="lunalens-setting-api-url">
                    <label for="lunalens-api-url">API URL:</label>
                    <input type="text" id="lunalens-api-url" value="${CONFIG.API_URL}" placeholder="例如:http://127.0.0.1:2333">
                    <div class="lunalens-setting-description">通常为LunaTranslator的网络服务地址</div>
                </div>
                <div class="lunalens-setting-tabs">
                    <div class="lunalens-setting-tab active" data-tab="sentence-settings">分句设置</div>
                    <div class="lunalens-setting-tab" data-tab="selector-settings">选择器设置</div>
                    <div class="lunalens-setting-tab" data-tab="other-settings">其他设置</div>
                </div>
                <div class="lunalens-setting-contents">
                    <div class="lunalens-setting-content active" id="sentence-settings">
                        <div class="lunalens-setting-item">
                            <label for="sentence-delimiters">分句断句符号</label>
                            <input type="text" id="sentence-delimiters" value="${CONFIG.SENTENCE_DELIMITERS}" placeholder="切分为多个句子单元的符合">
                        </div>
                        <div class="lunalens-setting-item">
                            <label for="sentence-length">句子字数阈值</label>
                            <input type="text" id="sentence-length" value="${CONFIG.SENTENCE_LENGTH}" placeholder="防止句子过短而设置的最小句子长度">
                        </div>
                        <div class="lunalens-setting-item">
                            <label for="min-content-length">最小内容长度</label>
                            <input type="text" id="min-content-length" value="${CONFIG.MIN_CONTENT_LENGTH}" placeholder="过短的文本不会被选中">
                        </div>
                        <div class="lunalens-setting-item">
                            <label for="max-content-length">最大内容长度</label>
                            <input type="text" id="max-content-length" value="${CONFIG.MAX_CONTENT_LENGTH}" placeholder="过长的文本不会被选中">
                        </div>
                        <div class="lunalens-setting-description">※注意:默认去除原句的振假名注音(ruby 中的 rt 和 rp)</div>
                    </div>
                    <div class="lunalens-setting-content" id="selector-settings">
                        <div class="lunalens-setting-item">
                            <label for="include-tags">包含的标签</label>
                            <input type="text" id="include-tags" value="${CONFIG.INCLUDE_TAGS}" placeholder="例如:p, h1, h2, h3, h4, h5, h6, div">
                        </div>
                        <div class="lunalens-setting-item">
                            <label for="exclude-tags">排除的标签</label>
                            <input type="text" id="exclude-tags" value="${CONFIG.EXCLUDE_TAGS}" placeholder="例如: a, img, em, dd, code, button">
                        </div>
                        <div class="lunalens-setting-item">
                            <label for="include-class-ids">包含的class/id</label>
                            <input type="text" id="include-class-ids" value="${CONFIG.INCLUDE_CLASS_IDS}" placeholder="例如:.article-content, #main-content">
                        </div>
                        <div class="lunalens-setting-item">
                            <label for="exclude-class-ids">排除的class/id</label>
                            <input type="text" id="exclude-class-ids" value="${CONFIG.EXCLUDE_CLASS_IDS}" placeholder="例如:.sidebar, #ads, .popup, #comments">
                        </div>
                        <div class="lunalens-setting-item">
                            <label for="stop-containers">停止容器</label>
                            <input type="text" id="stop-containers" value="${CONFIG.STOP_CONTAINERS}" placeholder="例如:article, main, section, div.content, div.main-content">
                        </div>
                        <div class="lunalens-setting-description">※注意class和id的写法:<br><kbd>class</kbd> 前加 <kbd>.</kbd><br><kbd>id</kbd> 前加 <kbd>#</kbd><br>用逗号分隔</div>
                        <div class="lunalens-setting-description">如果你看不懂又选不中文本的时候,请试试在第一栏加多个加上 <kbd>div</kbd> 提高选中率</div>
                    </div>
                    <div class="lunalens-setting-content" id="other-settings">
                        <div class="lunalens-setting-item">
                            <label for="timeout">API请求超时(ms)</label>
                            <input type="text" id="timeout" value="${CONFIG.TIMEOUT}">
                        </div>
                    </div>
                </div>
                <div class="lunalens-setting-buttons">
                    <button class="lunalens-setting-button lunalens-save-settings">保存设置</button>
                    <button class="lunalens-setting-button lunalens-reset-settings">重置设置</button>
                </div>
            </div>
        `;
        document.body.appendChild(panel);

        // 保存翻译区域引用
        translationArea = panel.querySelector('.lunalens-translation');

        // 添加事件处理
        const titleElement = panel.querySelector('.lunalens-title');
        const closeButton = panel.querySelector('.lunalens-close');
        const contextToggle = panel.querySelector('.lunalens-context-toggle');
        const translationToggle = panel.querySelector('.lunalens-translation-toggle');
        const ttsToggle = panel.querySelector('.lunalens-auto-tts-toggle');
        const settingToggle = panel.querySelector('.lunalens-setting-toggle');
        const queryInput = panel.querySelector('.lunalens-dict-query-input');
        const queryButton = panel.querySelector('.lunalens-dict-query-button');
        const contextArea = panel.querySelector('.lunalens-dict-context');
        const ttsButton = panel.querySelector('.lunalens-tts-button');
        const contextTtsButton = panel.querySelector('.lunalens-context-tts-button');
        const contextCopyButton = panel.querySelector('.lunalens-context-copy-button');
        const contextTranslateButton = panel.querySelector('.lunalens-context-translate-button');
        const settingTabs = panel.querySelectorAll('.lunalens-setting-tab');
        const saveButton = panel.querySelector('.lunalens-save-settings');
        const dictWindow = panel.querySelector('.lunalens-dict-window');
        const settingWindow = panel.querySelector('.lunalens-setting-window');
        
        // 切换到设置面板
        settingToggle.addEventListener('click', function() {
            const isSettingVisible = this.classList.contains('active');
            this.classList.toggle('active');
            
            if (isSettingVisible) {
                // 从设置切换到词典
                dictWindow.classList.add('visible');
                settingWindow.classList.remove('visible');
            } else {
                // 从词典切换到设置
                dictWindow.classList.remove('visible');
                settingWindow.classList.add('visible');
                // 加载保存的设置
                loadSettings();
            }
        });
        
        // 设置面板中的标签切换
        settingTabs.forEach(tab => {
            tab.addEventListener('click', function() {
                // 移除所有标签的活动状态
                settingTabs.forEach(t => t.classList.remove('active'));
                // 添加当前标签的活动状态
                this.classList.add('active');
                
                // 隐藏所有内容
                panel.querySelectorAll('.lunalens-setting-content').forEach(content => {
                    content.classList.remove('active');
                });
                
                // 显示当前标签对应的内容
                const tabId = this.getAttribute('data-tab');
                const tabContent = panel.querySelector(`#${tabId}`);
                if (tabContent) {
                    tabContent.classList.add('active');
                }
            });
        });
        
        // 保存设置
        saveButton.addEventListener('click', function() {
            // 收集所有设置输入
            const apiUrl = panel.querySelector('#lunalens-api-url').value.trim();
            const sentenceDelimiters = panel.querySelector('#sentence-delimiters').value;
            const sentenceLength = parseInt(panel.querySelector('#sentence-length').value) || 50;
            const minContentLength = parseInt(panel.querySelector('#min-content-length').value) || 2;
            const maxContentLength = parseInt(panel.querySelector('#max-content-length').value) || 1000;
            const includeSelectors = panel.querySelector('#include-tags').value;
            const excludeSelectors = panel.querySelector('#exclude-tags').value;
            const includeClassIds = panel.querySelector('#include-class-ids').value;
            const excludeClassIds = panel.querySelector('#exclude-class-ids').value;
            const stopContainers = panel.querySelector('#stop-containers').value;
            const timeout = parseInt(panel.querySelector('#timeout').value) || 5000;
            
            // 更新配置
            CONFIG.API_URL = apiUrl;
            CONFIG.SENTENCE_DELIMITERS = sentenceDelimiters;
            CONFIG.SENTENCE_LENGTH = sentenceLength;
            CONFIG.MIN_CONTENT_LENGTH = minContentLength;
            CONFIG.MAX_CONTENT_LENGTH = maxContentLength;
            CONFIG.INCLUDE_TAGS = includeSelectors;
            CONFIG.EXCLUDE_TAGS = excludeSelectors;
            CONFIG.INCLUDE_CLASS_IDS = includeClassIds;
            CONFIG.EXCLUDE_CLASS_IDS = excludeClassIds;
            CONFIG.STOP_CONTAINERS = stopContainers;
            CONFIG.TIMEOUT = timeout;
            
            // 保存到本地存储
            saveSettings();
            
            // 显示通知
            showToast('设置已保存', false);
        });

        // 标题点击关闭面板
        titleElement.addEventListener('click', function() {
            // 停止当前播放的语音
            stopReading();
            hidePanel();
        });

        // 关闭词典面板
        closeButton.addEventListener('click', function() {
            // 停止当前播放的语音
            stopReading();
            hidePanel();
        });

        // 切换句子/段落
        contextToggle.addEventListener('click', function() {
            CONFIG.DISPLAY_SENTENCE_MODE = !CONFIG.DISPLAY_SENTENCE_MODE;
            this.textContent = CONFIG.BUTTON_TEXT.DISPLAY[CONFIG.DISPLAY_SENTENCE_MODE];
            
            // 更新上下文
            updateContext();
            
            // 保存设置到本地存储
            saveSettings();
        });

        // 切换翻译功能
        translationToggle.addEventListener('click', function() {
            CONFIG.TRANSLATION_ENABLED = !CONFIG.TRANSLATION_ENABLED;
            this.textContent = CONFIG.BUTTON_TEXT.TRANSLATION[CONFIG.TRANSLATION_ENABLED];
            this.classList.toggle('active', CONFIG.TRANSLATION_ENABLED);
            
            if (CONFIG.TRANSLATION_ENABLED) {
                translationArea.style.display = 'block';
                // 如果有上下文内容且不同于上次翻译的内容,立即翻译
                const currentContextText = getContextText();
                if (currentContextText && currentContextText !== lastTranslatedText) {
                    translateContextText(currentContextText);
                }
            } else {
                translationArea.style.display = 'none';
            }
            
            // 保存设置到本地存储
            saveSettings();
        });
        
        // 切换TTS功能
        ttsToggle.addEventListener('click', function() {
            CONFIG.TTS_AUTO = !CONFIG.TTS_AUTO;
            this.textContent = CONFIG.BUTTON_TEXT.TTS[CONFIG.TTS_AUTO];
            this.classList.toggle('active', CONFIG.TTS_AUTO);
            
            // 保存设置到本地存储
            saveSettings();
        });
        
        // 单词发音按钮
        ttsButton.addEventListener('click', function() {
            const word = queryInput.value.trim();
            if (word) {
                readText(word);
            }
        });
        
        // 上下文朗读按钮
        contextTtsButton.addEventListener('click', function() {
            const contextText = getContextText();
            if (contextText) {
                readText(contextText);
            }
        });
        
        // 上下文复制按钮
        contextCopyButton.addEventListener('click', function() {
            const contextText = getContextText();
            copyToClipboard(contextText);
        });
        
        // 上下文翻译按钮
        contextTranslateButton.addEventListener('click', function() {
            const contextText = getContextText();
            if (contextText) {
                // 临时显示翻译区域
                translationArea.style.display = 'block';
                // 手动触发翻译(即使总开关关闭也能工作)
                translateContextText(contextText, true);
            }
        });

        // 查询框输入事件
        queryInput.addEventListener('input', function() {
            const word = this.value.trim();
            if (word) {
                lookupWord(word);
            }
        });

        // 查询按钮点击事件(添加下一个单词)
        setupQueryButton(queryButton, queryInput);

        // 上下文区域点击事件:点击上下文中的单词
        setupContextAreaEvents(contextArea, queryInput);

        // 初始设置翻译区域显示状态
        translationArea.style.display = CONFIG.TRANSLATION_ENABLED ? 'block' : 'none';
        
        // 添加重置设置按钮事件处理
        const resetButton = panel.querySelector('.lunalens-reset-settings');
        resetButton.addEventListener('click', function() {
            resetSettings();
            showToast('设置已重置为默认值', false);
        });

        // 阻止滚动事件冒泡到主页
        panel.addEventListener('wheel', function(e) {
            e.stopPropagation();
        });
        panel.addEventListener('touchmove', function(e) {
            e.stopPropagation();
        });

        return panel;
    }

    // 显示词典面板
    function showPanel() {
        if (dictionaryPanel) {
            dictionaryPanel.classList.add('visible');
            dictionaryPanel.querySelector('.lunalens-dict-window').classList.add('visible');
            dictionaryPanel.querySelector('.lunalens-setting-window').classList.remove('visible');
            dictionaryPanel.querySelector('.lunalens-setting-toggle').classList.remove('active');
        }
        document.documentElement.style.overflow = 'hidden';
    }

    // 隐藏词典面板
    function hidePanel() {
        if (dictionaryPanel) {
            dictionaryPanel.classList.remove('visible');
        }
        document.documentElement.style.overflow = 'auto';
    }

    // 更新词典面板上下文(句子或段落)
    function updateContext() {
        if (!dictionaryPanel || !activeElement || !contextSentence) return;
        
        const contextArea = dictionaryPanel.querySelector('.lunalens-dict-context');
        
        // 保存当前滚动位置
        const scrollTop = contextArea.scrollTop;
        
        // 清空上下文区域
        contextArea.innerHTML = '';
        
        // 创建临时容器
        const tempContainer = document.createElement('div');
        
        if (!CONFIG.DISPLAY_SENTENCE_MODE) {
            // 段落模式:显示整个段落的内容
            tempContainer.innerHTML = activeElement.innerHTML;
        } else {
            // 句子模式:只显示当前句子的内容
            tempContainer.innerHTML = contextSentence.innerHTML;
        }
        
        // 将内容添加到上下文区域,不带原始事件监听器
        contextArea.appendChild(tempContainer);
        
        // 重新标记激活的单词
        if (activeWordElements.length > 0) {
            contextArea.querySelectorAll('.lunalens-word').forEach(word => {
                const isActive = activeWordElements.some(activeWord => 
                    activeWord === word.dataset.originalIndex || // 通过索引匹配
                    (typeof activeWord === 'object' && 
                     activeWord.dataset && 
                     activeWord.dataset.originalIndex === word.dataset.originalIndex)
                );
                
                if (isActive) {
                    word.classList.add('active-word');
                } else {
                    word.classList.remove('active-word');
                }
            });
        }
        
        // 恢复滚动位置
        contextArea.scrollTop = scrollTop;
        
        // 检查上下文内容是否变化,如果变化且翻译功能开启,才翻译上下文内容
        if (CONFIG.TRANSLATION_ENABLED) {
            const currentContextText = getContextText();
            if (currentContextText !== lastTranslatedText) {
                translateContextText(currentContextText);
            }
        }
    }

    // 获取上下文区域的纯文本
    function getContextText() {
        if (!dictionaryPanel) return '';
        
        const contextArea = dictionaryPanel.querySelector('.lunalens-dict-context');
        if (!contextArea) return '';
        
        // 去除振假名
        return getPlainText(contextArea.innerHTML).trim();
    }

    // 翻译上下文内容
    function translateContextText(text, forceTranslate = false) {
        if (!translationArea || (!CONFIG.TRANSLATION_ENABLED && !forceTranslate)) return;
        
        // 如果未提供文本参数,则获取当前上下文文本
        if (!text) {
            text = getContextText();
        }
        
        if (!text) {
            translationArea.innerHTML = '<div class="lunalens-translator-item">没有可翻译的文本</div>';
            return;
        }
        
        // 避免重复翻译相同的文本
        if (text === lastTranslatedText) {
            return;
        }
        
        // 更新上次翻译的文本
        lastTranslatedText = text;
        
        // 清空翻译区域,显示加载信息
        translationArea.innerHTML = '<div class="lunalens-translator-item">正在获取翻译...</div>';
        
        // 获取翻译器列表并发起请求
        GM_xmlhttpRequest({
            method: 'GET',
            url: `${CONFIG.API_URL}/api/list/translator`,
            timeout: CONFIG.TIMEOUT,
            onload: (response) => {
                try {
                    const translators = JSON.parse(response.responseText);
                    if (Array.isArray(translators) && translators.length > 0) {
                        // 清空翻译容器
                        translationArea.innerHTML = '';
                        
                        // 对每个翻译器发起请求
                        translators.forEach((translator, index) => {
                            // 为每个翻译器创建一个项,先显示加载中
                            const translatorItem = document.createElement('div');
                            translatorItem.className = 'lunalens-translator-item';
                            translatorItem.innerHTML = `加载中...<span class="lunalens-translator-name">${translator.name}</span>`;
                            translationArea.appendChild(translatorItem);
                            
                            // 如果不是最后一个翻译器,添加分隔线
                            if (index < translators.length - 1) {
                                const hr = document.createElement('hr');
                                hr.className = 'lunalens-hr';
                                translationArea.appendChild(hr);
                            }
                            
                            // 发起翻译请求
                            GM_xmlhttpRequest({
                                method: 'GET',
                                url: `${CONFIG.API_URL}/api/translate?text=${encodeURIComponent(text)}&id=${encodeURIComponent(translator.id)}`,
                                timeout: CONFIG.TIMEOUT,
                                onload: (response) => {
                                    try {
                                        const data = JSON.parse(response.responseText);
                                        translatorItem.innerHTML = `${data.result || '翻译失败'}<span class="lunalens-translator-name">${translator.name}</span>`;
                                    } catch (error) {
                                        translatorItem.innerHTML = `翻译结果解析失败<span class="lunalens-translator-name">${translator.name}</span>`;
                                    }
                                },
                                onerror: () => {
                                    translatorItem.innerHTML = `翻译请求失败<span class="lunalens-translator-name">${translator.name}</span>`;
                                },
                                ontimeout: () => {
                                    translatorItem.innerHTML = `翻译请求超时<span class="lunalens-translator-name">${translator.name}</span>`;
                                }
                            });
                        });
                    } else {
                        translationArea.innerHTML = '<div class="lunalens-translator-item">未找到可用的翻译器</div>';
                    }
                } catch (error) {
                    translationArea.innerHTML = '<div class="lunalens-translator-item">获取翻译器列表失败</div>';
                }
            },
            onerror: () => {
                translationArea.innerHTML = '<div class="lunalens-translator-item">获取翻译器列表失败</div>';
            },
            ontimeout: () => {
                translationArea.innerHTML = '<div class="lunalens-translator-item">获取翻译器列表超时</div>';
            }
        });
    }

    // 激活元素
    function activateElement(element) {
        // 取消激活当前元素
        deactivateElement();
        
        // 设置新的激活元素
        activeElement = element;
        // 保存原始内容以便后续恢复
        originalContent = element.innerHTML;
        element.classList.add('lunalens-highlighted');
        
        // 重置上次翻译的文本
        lastTranslatedText = '';
        
        // 创建一个取消标志,用于标识当前处理是否仍然有效
        const requestId = Date.now();
        element.dataset.requestId = requestId;
        
        // 如果TTS功能开启,尝试朗读文本
        if (CONFIG.TTS_AUTO) {
            const plainText = getPlainText(originalContent);
            setTimeout(() => {
                // 使用重构后的TTS朗读功能
                readText(plainText);
            }, 50);
        }
        
        // 处理文本并添加分词标记
        processTextWithTokenization(originalContent, html => {
            // 如果元素已经不是当前激活的元素或者请求ID不匹配,则不更新内容
            if (element !== activeElement || element.dataset.requestId != requestId) {
                return;
            }
            
            element.innerHTML = html;
            
            // 为所有单词添加点击事件
            element.querySelectorAll('.lunalens-word').forEach(word => {
                word.addEventListener('click', e => {
                    e.stopPropagation();
                    
                    // 获取单词文本
                    const wordText = word.dataset.word;
                    
                    // 获取所在句子
                    contextSentence = word.closest('.lunalens-sentence');
                    if (!contextSentence) contextSentence = element;
                    
                    // 给单词添加临时闪烁效果
                    word.classList.add('flash');
                    setTimeout(() => {
                        word.classList.remove('flash');
                    }, 1000);
                    
                    // 首先清空之前的激活状态
                    const contextArea = dictionaryPanel.querySelector('.lunalens-dict-context');
                    if (contextArea) {
                        contextArea.querySelectorAll('.lunalens-word.active-word').forEach(w => {
                            w.classList.remove('active-word');
                        });
                    }
                    activeWordElements = [];
                    
                    // 记录当前活跃词
                    activeWordElements.push(word.dataset.originalIndex || word);
                    
                    // 显示词典面板
                    showPanel();
                    
                    // 更新面板上下文
                    updateContext();
                    
                    // 设置查词框内容
                    const queryInput = dictionaryPanel.querySelector('.lunalens-dict-query-input');
                    queryInput.value = wordText;
                    
                    // 查询单词
                    lookupWord(wordText);
                });
            });
        });
    }

    // 取消激活元素
    function deactivateElement() {
        if (activeElement && originalContent) {
            activeElement.innerHTML = originalContent;
            activeElement.classList.remove('lunalens-highlighted');
            activeElement = null;
            originalContent = null;
            
            // 清除激活单词记录
            activeWordElements = [];
            
            // 清除上次翻译的文本记录
            lastTranslatedText = '';
        }
    }

    // 处理点击事件
    function handleClick(e) {
        // 如果点击发生在词典面板内,不处理
        if (dictionaryPanel && dictionaryPanel.contains(e.target)) {
            return;
        }
        
        if (activeElement && activeElement.contains(e.target)) {
            if (e.target.classList.contains('lunalens-sentence') || 
                e.target.classList.contains('lunalens-word') || 
                e.target.closest('.lunalens-word')) {
                e.preventDefault();
                return;
            }
        } else if (isElementSelectable(e.target)) {
            // 检查内容是否为空或只有空白字符
            const textContent = e.target.textContent.trim();
            if (textContent.length < CONFIG.MIN_CONTENT_LENGTH || textContent.length > CONFIG.MAX_CONTENT_LENGTH) {
                return;
            }
            
            e.preventDefault();
            activateElement(e.target);
        } else if (!e.target.closest('.lunalens-panel')) {
            // 只有当点击不在词典面板内时才停用元素
            deactivateElement();
        }
    }
    
    // 判断元素是否符合选择器设置
    function isElementSelectable(element) {
        if (!element) return false;
        
        const tagName = element.tagName.toLowerCase();
        
        // 处理逗号分隔的配置字符串并返回有效的数组
        function parseConfigList(configString) {
            return configString.split(',').map(item => item.trim()).filter(Boolean);
        }
        
        // 1. 检查标签是否在包含列表中
        const includeTags = parseConfigList(CONFIG.INCLUDE_TAGS);
        if (includeTags.length > 0 && !includeTags.includes(tagName)) {
            return false;
        }
        
        // 2. 检查标签是否在排除列表中
        const excludeTags = parseConfigList(CONFIG.EXCLUDE_TAGS);
        if (excludeTags.length > 0 && excludeTags.includes(tagName)) {
            return false;
        }
        
        // 3. 检查class和id是否在包含列表中
        const includeClassIds = parseConfigList(CONFIG.INCLUDE_CLASS_IDS);
        if (includeClassIds.length > 0) {
            let matched = false;
            for (const selector of includeClassIds) {
                if (selector.startsWith('.') && element.classList.contains(selector.substring(1))) {
                    matched = true;
                    break;
                } else if (selector.startsWith('#') && element.id === selector.substring(1)) {
                    matched = true;
                    break;
                }
            }
            if (!matched) return false;
        }
        
        // 4. 检查class和id是否在排除列表中
        const excludeClassIds = parseConfigList(CONFIG.EXCLUDE_CLASS_IDS);
        if (excludeClassIds.length > 0) {
            for (const selector of excludeClassIds) {
                if (selector.startsWith('.') && element.classList.contains(selector.substring(1))) {
                    return false;
                } else if (selector.startsWith('#') && element.id === selector.substring(1)) {
                    return false;
                }
            }
        }
        
        // 5. 检查是否在停止容器内
        const stopContainers = parseConfigList(CONFIG.STOP_CONTAINERS);
        if (stopContainers.length > 0) {
            for (const selector of stopContainers) {
                let closestContainer = null;
                
                try {
                    closestContainer = element.closest(selector);
                } catch (e) {
                    console.error(`无效的选择器: ${selector}`, e);
                }
                
                if (closestContainer) {
                    // 如果元素本身就是停止容器,允许选择
                    if (closestContainer === element) {
                        return true;
                    }
                    
                    // 如果元素在停止容器内,需要确保它不是深层嵌套的
                    const parentContainer = element.parentElement.closest(selector);
                    if (!parentContainer || parentContainer === closestContainer) {
                        return true;
                    }
                    
                    return false;
                }
            }
        }
        
        return true;
    }

    // 处理iframe
    function handleIframe(iframe) {
        try {
            function setupIframe() {
                const iframeDoc = iframe.contentDocument || iframe.contentWindow.document;
                addStyle(iframeDoc);
                iframeDoc.addEventListener('click', handleClick);
            }

            if (iframe.contentDocument && iframe.contentDocument.readyState === 'complete') {
                setupIframe();
            } else {
                iframe.addEventListener('load', setupIframe);
            }
        } catch (e) {
            console.log('无法访问iframe:', e);
        }
    }

    // 处理所有iframe
    function handleAllIframes() {
        document.querySelectorAll('iframe').forEach(handleIframe);
    }

    // 初始化函数
    function init() {
        addStyle(document);
        document.addEventListener('click', handleClick);
        
        // 创建常驻词典面板
        dictionaryPanel = createDictionaryPanel();
        
        // 处理现有iframe
        handleAllIframes();
        
        // 监听新添加的iframe
        new MutationObserver(handleAllIframes).observe(document.body, {
            childList: true,
            subtree: true
        });
        
        // 加载保存的设置
        loadSettings();
    }

    setTimeout(init, 500);

    // 显示通知
    function showToast(message, isError = false) {
        // 创建通知元素
        const toast = document.createElement('div');
        toast.className = 'lunalens-toast';
        toast.textContent = message;
        
        // 错误消息使用红色背景
        if (isError) {
            toast.style.backgroundColor = 'rgba(220, 53, 69, 0.9)';
        } else {
            toast.style.backgroundColor = 'rgba(0, 0, 0, 0.8)';
        }
        
        // 添加到文档中并显示
        document.body.appendChild(toast);
        
        // 强制回流以触发动画
        void toast.offsetWidth;
        toast.style.display = 'block';
        
        // 动画结束后移除元素
        setTimeout(() => {
            toast.style.opacity = '0';
            setTimeout(() => {
                if (toast.parentNode) {
                    toast.parentNode.removeChild(toast);
                }
            }, 300);
        }, 2000);
    }
    
    // 保存设置到本地存储
    function saveSettings() {
        const settings = {
            API_URL: CONFIG.API_URL,
            SENTENCE_DELIMITERS: CONFIG.SENTENCE_DELIMITERS,
            SENTENCE_LENGTH: CONFIG.SENTENCE_LENGTH,
            MIN_CONTENT_LENGTH: CONFIG.MIN_CONTENT_LENGTH,
            MAX_CONTENT_LENGTH: CONFIG.MAX_CONTENT_LENGTH,
            INCLUDE_TAGS: CONFIG.INCLUDE_TAGS,
            EXCLUDE_TAGS: CONFIG.EXCLUDE_TAGS,
            INCLUDE_CLASS_IDS: CONFIG.INCLUDE_CLASS_IDS,
            EXCLUDE_CLASS_IDS: CONFIG.EXCLUDE_CLASS_IDS,
            STOP_CONTAINERS: CONFIG.STOP_CONTAINERS,
            TIMEOUT: CONFIG.TIMEOUT,
            DISPLAY_SENTENCE_MODE: CONFIG.DISPLAY_SENTENCE_MODE,
            TRANSLATION_ENABLED: CONFIG.TRANSLATION_ENABLED,
            TTS_AUTO: CONFIG.TTS_AUTO
        };
        
        try {
            localStorage.setItem('lunalens_settings', JSON.stringify(settings));
        } catch (e) {
            console.error('保存设置失败:', e);
        }
    }
    
    // 从本地存储加载设置
    function loadSettings() {
        try {
            const savedSettings = localStorage.getItem('lunalens_settings');
            if (savedSettings) {
                const settings = JSON.parse(savedSettings);
                
                // 更新CONFIG对象
                Object.keys(settings).forEach(key => {
                    if (typeof CONFIG[key] !== 'undefined') {
                        CONFIG[key] = settings[key];
                    }
                });
                
                // 更新设置表单值
                if (dictionaryPanel) {
                    const panel = dictionaryPanel;
                    
                    // 更新基本设置
                    const updateField = (id, value) => {
                        const field = panel.querySelector(`#${id}`);
                        if (field) field.value = value;
                    };
                    
                    updateField('lunalens-api-url', CONFIG.API_URL);
                    updateField('sentence-delimiters', CONFIG.SENTENCE_DELIMITERS);
                    updateField('sentence-length', CONFIG.SENTENCE_LENGTH);
                    updateField('min-content-length', CONFIG.MIN_CONTENT_LENGTH);
                    updateField('max-content-length', CONFIG.MAX_CONTENT_LENGTH);
                    updateField('include-tags', CONFIG.INCLUDE_TAGS);
                    updateField('exclude-tags', CONFIG.EXCLUDE_TAGS);
                    updateField('include-class-ids', CONFIG.INCLUDE_CLASS_IDS);
                    updateField('exclude-class-ids', CONFIG.EXCLUDE_CLASS_IDS);
                    updateField('stop-containers', CONFIG.STOP_CONTAINERS);
                    updateField('timeout', CONFIG.TIMEOUT);
                    
                    // 更新顶栏按钮状态
                    panel.querySelector('.lunalens-context-toggle').textContent = 
                        CONFIG.BUTTON_TEXT.DISPLAY[CONFIG.DISPLAY_SENTENCE_MODE];
                    
                    panel.querySelector('.lunalens-translation-toggle').textContent = 
                        CONFIG.BUTTON_TEXT.TRANSLATION[CONFIG.TRANSLATION_ENABLED];
                    panel.querySelector('.lunalens-translation-toggle').classList.toggle('active', CONFIG.TRANSLATION_ENABLED);
                    
                    panel.querySelector('.lunalens-auto-tts-toggle').textContent = 
                        CONFIG.BUTTON_TEXT.TTS[CONFIG.TTS_AUTO];
                    panel.querySelector('.lunalens-auto-tts-toggle').classList.toggle('active', CONFIG.TTS_AUTO);
                    
                    // 显示/隐藏翻译区域
                    if (translationArea) {
                        translationArea.style.display = CONFIG.TRANSLATION_ENABLED ? 'block' : 'none';
                    }
                }
            }
        } catch (e) {
            console.error('加载设置失败:', e);
        }
    }
    
    // 重置设置为默认值
    function resetSettings() {
        // 从DEFAULT_CONFIG复制所有属性到CONFIG
        Object.keys(DEFAULT_CONFIG).forEach(key => {
            CONFIG[key] = DEFAULT_CONFIG[key];
        });
        
        // 更新UI上的设置值
        if (dictionaryPanel) {
            const panel = dictionaryPanel;
            
            // 更新基本设置
            const updateField = (id, value) => {
                const field = panel.querySelector(`#${id}`);
                if (field) field.value = value;
            };
            
            updateField('lunalens-api-url', CONFIG.API_URL);
            updateField('sentence-delimiters', CONFIG.SENTENCE_DELIMITERS);
            updateField('sentence-length', CONFIG.SENTENCE_LENGTH);
            updateField('min-content-length', CONFIG.MIN_CONTENT_LENGTH);
            updateField('max-content-length', CONFIG.MAX_CONTENT_LENGTH);
            updateField('include-tags', CONFIG.INCLUDE_TAGS);
            updateField('exclude-tags', CONFIG.EXCLUDE_TAGS);
            updateField('include-class-ids', CONFIG.INCLUDE_CLASS_IDS);
            updateField('exclude-class-ids', CONFIG.EXCLUDE_CLASS_IDS);
            updateField('stop-containers', CONFIG.STOP_CONTAINERS);
            updateField('timeout', CONFIG.TIMEOUT);
            
            // 更新顶栏按钮状态
            panel.querySelector('.lunalens-context-toggle').textContent = 
                CONFIG.BUTTON_TEXT.DISPLAY[CONFIG.DISPLAY_SENTENCE_MODE];
            
            panel.querySelector('.lunalens-translation-toggle').textContent = 
                CONFIG.BUTTON_TEXT.TRANSLATION[CONFIG.TRANSLATION_ENABLED];
            panel.querySelector('.lunalens-translation-toggle').classList.toggle('active', CONFIG.TRANSLATION_ENABLED);
            
            panel.querySelector('.lunalens-auto-tts-toggle').textContent = 
                CONFIG.BUTTON_TEXT.TTS[CONFIG.TTS_AUTO];
            panel.querySelector('.lunalens-auto-tts-toggle').classList.toggle('active', CONFIG.TTS_AUTO);
            
            // 显示/隐藏翻译区域
            if (translationArea) {
                translationArea.style.display = CONFIG.TRANSLATION_ENABLED ? 'block' : 'none';
            }
        }
        
        // 从本地存储中移除保存的设置
        localStorage.removeItem('lunalens_settings');
    }

    // 查词典并显示结果
    function lookupWord(word) {
        if (!word) return;
        if (word === currentWord) return;

        currentWord = word;

        // 获取词典面板
        const panel = document.querySelector('.lunalens-panel');
        if (!panel) return;

        const tabsContainer = panel.querySelector('.lunalens-dict-tabs');
        const entriesContainer = panel.querySelector('.lunalens-dict-entries');
        
        // 清空现有内容
        tabsContainer.innerHTML = '';
        entriesContainer.innerHTML = `<div class="lunalens-dict-loading">正在查询${word}</div>`;
        
        // 使用兼容模式查询
        fetchDictionaryByGM(word, tabsContainer, entriesContainer);
    }

    // 使用GM_xmlhttpRequest并行获取多个词典数据
    function fetchDictionaryByGM(word, tabsContainer, entriesContainer, dictIds = []) {
        // 生成唯一请求ID
        const requestId = Date.now().toString();
        dictionaryPanel.setAttribute('data-request-id', requestId);
        
        // 如果没有提供词典ID列表,则先获取可用词典列表
        if (!dictIds || dictIds.length === 0) {
            GM_xmlhttpRequest({
                method: 'GET',
                url: `${CONFIG.API_URL}/api/list/dictionary`,
                onload: (response) => {
                    // 检查响应是否匹配当前请求
                    if (dictionaryPanel.getAttribute('data-request-id') !== requestId) return;
                    
                    try {
                        const dictList = JSON.parse(response.responseText);
                        if (Array.isArray(dictList) && dictList.length > 0) {
                            // 提取词典ID列表
                            const ids = dictList.map(dict => dict.id);
                            // 递归调用,使用获取的词典ID列表
                            fetchDictionaryByGM(word, tabsContainer, entriesContainer, ids);
                        } else {
                            // 没有找到词典
                            showDictionaryStatus(entriesContainer, '');
                        }
                    } catch (error) {
                        console.error('获取词典列表失败:', error);
                        showDictionaryStatus(entriesContainer, '');
                    }
                },
                onerror: () => {
                    // 检查响应是否匹配当前请求
                    if (dictionaryPanel.getAttribute('data-request-id') !== requestId) return;
                    
                    showDictionaryStatus(entriesContainer, '');
                }
            });
            return;
        }
        
        // 记录未完成的请求数
        let pendingRequests = dictIds.length;
        
        // 添加MDICT内部标签切换函数
        if (entriesContainer.parentNode && !entriesContainer.parentNode.querySelector('script[data-mdict-function]')) {
            const script = document.createElement('script');
            script.setAttribute('data-mdict-function', 'true');
            script.textContent = `
                function onclickbtn_mdict_internal(_id) {
                    tabPanes = document.querySelectorAll('.tab-widget_mdict_internal .tab-pane_mdict_internal');
                    tabButtons = document.querySelectorAll('.tab-widget_mdict_internal .tab-button_mdict_internal');
                    for (i = 0; i < tabButtons.length; i++)
                        tabButtons[i].classList.remove('active');
                    for (i = 0; i < tabPanes.length; i++)
                        tabPanes[i].classList.remove('active');

                    document.getElementById(_id).classList.add('active');

                    tabId = document.getElementById(_id).getAttribute('data-tab');
                    tabPane = document.getElementById(tabId);
                    tabPane.classList.add('active');
                }
            `;
            entriesContainer.parentNode.appendChild(script);
        }
        
        // 并行请求每个词典
        dictIds.forEach(dictId => {
            GM_xmlhttpRequest({
                method: 'GET',
                url: `${CONFIG.API_URL}/api/dictionary?id=${dictId}&word=${encodeURIComponent(word)}`,
                onload: (response) => {
                    // 检查响应是否匹配当前请求
                    if (dictionaryPanel.getAttribute('data-request-id') !== requestId) return;
                    
                    try {
                        const data = JSON.parse(response.responseText);
                        
                        // 隐藏加载提示
                        const loadingIndicator = entriesContainer.querySelector('.lunalens-dict-loading');
                        if (loadingIndicator) loadingIndicator.style.display = 'none';
                        
                        // 添加词典条目
                        addDictionaryEntry(tabsContainer, entriesContainer, data);
                    } catch (error) {
                        console.error(`获取词典 ${dictId} 失败:`, error);
                    }
                    
                    // 减少未完成请求计数
                    pendingRequests--;
                    
                    // 如果所有请求都完成了,但没有词典结果,则显示提示
                    if (pendingRequests === 0 && tabsContainer.children.length === 0) {
                        showDictionaryStatus(entriesContainer, '');
                    }
                },
                onerror: () => {
                    // 检查响应是否匹配当前请求
                    if (dictionaryPanel.getAttribute('data-request-id') !== requestId) return;
                    
                    console.error(`获取词典 ${dictId} 失败`);
                    
                    // 减少未完成请求计数
                    pendingRequests--;
                    
                    // 如果所有请求都完成了,但没有词典结果,则显示提示
                    if (pendingRequests === 0 && tabsContainer.children.length === 0) {
                        showDictionaryStatus(entriesContainer, '');
                    }
                }
            });
        });
    }

    // 显示词典状态信息
    function showDictionaryStatus(container, message) {
        const loadingIndicator = container.querySelector('.lunalens-dict-loading');
        if (loadingIndicator) {
            loadingIndicator.textContent = message;
        } else {
            container.innerHTML = `<div class="lunalens-dict-loading">${message}</div>`;
        }
    }

    // 添加词典条目
    function addDictionaryEntry(tabsContainer, entriesContainer, data, isFirst) {
        // 如果没有词典名称,直接跳过
        if (!data.name) return;
        
        const dictName = data.name;
        const dictId = `dict-${dictName.replace(/\s+/g, '-')}`;
        
        // 检查是否已有该词典结果
        let entryDiv = document.getElementById(dictId);
        
        if (!entryDiv) {
            // 创建新词典条目
            entryDiv = document.createElement('div');
            entryDiv.className = 'lunalens-dict-entry';
            if (isFirst || tabsContainer.children.length === 0) entryDiv.classList.add('active');
            entryDiv.id = dictId;
            entryDiv.setAttribute('data-dict', dictName);
            entryDiv.innerHTML = `<div class="lunalens-dict-content">${data.result || ''}</div>`;
            entriesContainer.appendChild(entryDiv);
            
            // 添加词典标签
            const tab = document.createElement('div');
            tab.className = 'lunalens-dict-tab';
            if (isFirst || tabsContainer.children.length === 0) tab.classList.add('active');
            tab.textContent = dictName;
            tab.setAttribute('data-dict', dictName);
            
            tab.addEventListener('click', function() {
                // 更新标签状态
                document.querySelectorAll('.lunalens-dict-tab').forEach(t => t.classList.remove('active'));
                this.classList.add('active');
                
                // 更新词典显示
                const dictName = this.getAttribute('data-dict');
                document.querySelectorAll('.lunalens-dict-entry').forEach(entry => {
                    if (entry.getAttribute('data-dict') === dictName) {
                        entry.classList.add('active');
                    } else {
                        entry.classList.remove('active');
                    }
                });
            });
            
            tabsContainer.appendChild(tab);
        } else {
            // 更新现有词典内容
            const contentDiv = entryDiv.querySelector('.lunalens-dict-content');
            if (contentDiv) contentDiv.innerHTML = data.result || '';
        }
    }

    // TTS朗读功能
    function readText(text, element = null) {
        if (!text || !text.trim()) return false;

        stopReading();

        // 检测设备类型并使用适当的播放方法
        if (/Android|webOS|iPhone|iPad|iPod|BlackBerry|IEMobile|Opera Mini/i.test(navigator.userAgent)) {
            // 在移动设备上直接使用Audio元素和原始URL
            playWithDirectUrl(text);
        } else {
            // 在桌面设备上使用ArrayBuffer加音频解码
            fetchAndPlayTTS(text);
        }
        
        return true;
    }
    
    // 停止朗读
    function stopReading() {
        // 取消所有待处理的TTS请求
        pendingTTSRequests.forEach(request => {
            if (request && request.abort) {
                try {
                    request.abort();
                } catch(e) {
                    console.log('取消TTS请求失败:', e);
                }
            }
        });
        pendingTTSRequests = [];
        
        // 停止当前播放的音频
        if (currentAudio) {
            try {
                currentAudio.pause();
                if (currentAudio.src) {
                    URL.revokeObjectURL(currentAudio.src);
                }
                
                // 释放资源
                if (currentAudio.source && currentAudio.context) {
                    try {
                        currentAudio.source.stop();
                        currentAudio.context.close();
                    } catch(e) {}
                }
            } catch(e) {
                console.error('停止播放时出错:', e);
            } finally {
                currentAudio = null;
            }
        }
    }
    
    
    // 直接通过URL播放(适用于移动设备)
    function playWithDirectUrl(text) {

        const audio = new Audio();
        audio.src = `${CONFIG.API_URL}/api/tts?text=${encodeURIComponent(text)}`;
        
        currentAudio = {
            element: audio,
            pause: function() {
                try {
                    this.element.pause();
                } catch(e) {
                    console.error('停止播放失败:', e);
                }
            }
        };
        
        audio.onended = () => {
            currentAudio = null;
        };
        
        audio.onerror = (e) => {
            console.error('音频播放失败:', e);
            currentAudio = null;
        };
        
        audio.play().catch(e => fetchAndPlayTTS(text));
    }

    // 获取并播放TTS(适用于桌面设备)
    function fetchAndPlayTTS(text) {
        const request = GM_xmlhttpRequest({
            method: 'GET',
            url: `${CONFIG.API_URL}/api/tts?text=${encodeURIComponent(text)}`,
            responseType: 'arraybuffer',
            timeout: CONFIG.TIMEOUT,
            onload: response => {
                // 从待处理列表中移除该请求
                const index = pendingTTSRequests.indexOf(request);
                if (index > -1) {
                    pendingTTSRequests.splice(index, 1);
                }
                
                if (response.status >= 200 && response.status < 300) {
                    playAudioBlob(response.response);
                } else {
                    console.error('TTS请求失败! 状态:', response.status);
                }
            },
            onerror: error => {
                // 从待处理列表中移除该请求
                const index = pendingTTSRequests.indexOf(request);
                if (index > -1) {
                    pendingTTSRequests.splice(index, 1);
                }
                console.error('TTS请求失败:', error);
            },
            ontimeout: () => {
                // 从待处理列表中移除该请求
                const index = pendingTTSRequests.indexOf(request);
                if (index > -1) {
                    pendingTTSRequests.splice(index, 1);
                }
                console.error('TTS请求超时');
            }
        });
        
        // 将请求添加到待处理列表
        pendingTTSRequests.push(request);
    }
    
    // 播放音频数据(仅用于桌面设备)
    function playAudioBlob(arrayBuffer) {
        // 如果已经有音频在播放或者系统已停止,则不播放新音频
        if (currentAudio) {
            return;
        }
        
        try {
            const audioContext = new (window.AudioContext || window.webkitAudioContext)();
            
            audioContext.decodeAudioData(arrayBuffer, 
                (buffer) => {
                    const source = audioContext.createBufferSource();
                    source.buffer = buffer;
                    source.connect(audioContext.destination);
                    
                    currentAudio = {
                        source: source,
                        context: audioContext,
                        pause: function() {
                            try {
                                this.source.stop();
                                this.context.close();
                            } catch(e) {}
                        }
                    };
                    
                    source.onended = () => {
                        audioContext.close().catch(() => {});
                        currentAudio = null;
                    };
                    
                    source.start(0);
                },
                () => console.error('音频播放失败')
            );
        } catch (e) {
            console.error('音频播放失败:', e);
            playWithDirectUrl(text);
        }
    }
})();