(function() { let playerContainer = null; function createPlayer() { if (playerContainer) return; playerContainer = document.createElement('div'); playerContainer.id = 'readvox-player'; const playerIframe = document.createElement('iframe'); playerIframe.src = chrome.runtime.getURL('popup.html'); playerIframe.className = 'player-iframe'; playerContainer.appendChild(playerIframe); document.body.appendChild(playerContainer); makeDraggable(playerContainer); } function removePlayer() { if (playerContainer) { playerContainer.remove(); playerContainer = null; } } function makeDraggable(element) { let pos1 = 0, pos2 = 0, pos3 = 0, pos4 = 0; const dragMouseDown = (e) => { e = e || window.event; pos3 = e.clientX; pos4 = e.clientY; document.onmouseup = closeDragElement; document.onmousemove = elementDrag; }; const elementDrag = (e) => { e = e || window.event; e.preventDefault(); pos1 = pos3 - e.clientX; pos2 = pos4 - e.clientY; pos3 = e.clientX; pos4 = e.clientY; element.style.top = (element.offsetTop - pos2) + "px"; element.style.left = (element.offsetLeft - pos1) + "px"; }; const closeDragElement = () => { document.onmouseup = null; document.onmousemove = null; }; element.addEventListener('mousedown', dragMouseDown); } // ---------- Grammar Highlight Logic ---------- const PARAGRAPH_SELECTORS = 'p, article p, section p, div'; function callBackend(payload) { return new Promise((resolve, reject) => { chrome.runtime.sendMessage( { type: 'GRAMMAR_API_REQUEST', ...payload }, (response) => { if (chrome.runtime.lastError) { reject(new Error(chrome.runtime.lastError.message)); return; } if (!response) { reject(new Error('后台未返回数据。')); return; } if (response.error) { reject(new Error(response.error)); return; } resolve(response); } ); }); } function isParagraphElement(node) { return ( node && node.nodeType === Node.ELEMENT_NODE && node.matches && node.matches(PARAGRAPH_SELECTORS) ); } function collectParagraphNodes(container) { if (!container || container.nodeType !== Node.ELEMENT_NODE) return []; const paragraphs = Array.from(container.querySelectorAll('p')).filter( (el) => el.innerText.trim() ); if (paragraphs.length > 0) return paragraphs; return [container]; } function buildParagraphTarget(nodes) { const usableNodes = nodes.filter( (node) => node && node.innerText && node.innerText.trim() ); if (usableNodes.length === 0) { throw new Error('未找到可用段落文本。'); } const originals = usableNodes.map((node) => node.innerHTML); const paragraphs = usableNodes.map((node) => node.innerText.trim()); return { payload: { paragraphs }, apply(htmlList) { const list = Array.isArray(htmlList) ? htmlList : []; usableNodes.forEach((node, idx) => { const html = list[idx]; if (typeof html === 'string' && html.trim()) { node.innerHTML = html; } }); }, restore() { usableNodes.forEach((node, idx) => { node.innerHTML = originals[idx]; }); } }; } function getSelectionTarget() { const selection = window.getSelection(); if (!selection || selection.rangeCount === 0) { throw new Error('请先在页面中选择一段文本。'); } const range = selection.getRangeAt(0); if (range.collapsed) { throw new Error('所选文本为空。'); } const root = range.commonAncestorContainer.nodeType === Node.ELEMENT_NODE ? range.commonAncestorContainer : range.commonAncestorContainer.parentElement || document.body; const walker = document.createTreeWalker(root, NodeFilter.SHOW_ELEMENT, { acceptNode(node) { if (!(node instanceof HTMLElement)) return NodeFilter.FILTER_SKIP; return isParagraphElement(node) && range.intersectsNode(node) ? NodeFilter.FILTER_ACCEPT : NodeFilter.FILTER_SKIP; } }); const nodes = []; let current = walker.nextNode(); while (current) { nodes.push(current); current = walker.nextNode(); } if (nodes.length === 0) { const fallback = (range.commonAncestorContainer.nodeType === Node.ELEMENT_NODE ? range.commonAncestorContainer : range.commonAncestorContainer.parentElement) || document.body; nodes.push(fallback.closest(PARAGRAPH_SELECTORS) || fallback); } return buildParagraphTarget(nodes); } function getParagraphTarget() { const selection = window.getSelection(); let node = selection && selection.rangeCount > 0 ? selection.getRangeAt(0).commonAncestorContainer : document.activeElement; if (!node) { node = document.body; } if (node.nodeType === Node.TEXT_NODE) { node = node.parentElement; } const paragraph = node.closest(PARAGRAPH_SELECTORS) || document.querySelector('p') || document.body; return buildParagraphTarget([paragraph]); } function getArticleTarget() { const container = document.querySelector('article') || document.querySelector('main') || document.body; const nodes = collectParagraphNodes(container).filter( (node) => node.innerText.trim() ); return buildParagraphTarget(nodes); } async function handleAnalyze(mode) { let targetGetter; switch (mode) { case 'selection': targetGetter = getSelectionTarget; break; case 'paragraph': targetGetter = getParagraphTarget; break; case 'page': case 'article': targetGetter = getArticleTarget; break; default: throw new Error('未知的分析模式。'); } const target = targetGetter(); try { const response = await callBackend(target.payload); const htmlList = response.highlightedHtmls || (response.highlightedHtml ? [response.highlightedHtml] : []); if (!htmlList || htmlList.length === 0) { throw new Error('后台未返回数据。'); } target.apply(htmlList); return {}; } catch (error) { target.restore?.(); throw error; } } // ---------- Message Handler ---------- chrome.runtime.onMessage.addListener((request, sender, sendResponse) => { if (request.action === "activatePlayer") { createPlayer(); } else if (request.action === "deactivatePlayer") { removePlayer(); } else if (request.type === 'EXECUTE_GRAMMAR') { handleAnalyze(request.mode) .then(() => sendResponse({ success: true })) .catch((error) => { sendResponse({ error: error.message || '未知错误' }); }); return true; } }); })();