您需要先安装一个扩展,例如 篡改猴、Greasemonkey 或 暴力猴,之后才能安装此脚本。
您需要先安装一个扩展,例如 篡改猴 或 暴力猴,之后才能安装此脚本。
您需要先安装一个扩展,例如 篡改猴 或 暴力猴,之后才能安装此脚本。
您需要先安装一个扩展,例如 篡改猴 或 Userscripts ,之后才能安装此脚本。
您需要先安装一款用户脚本管理器扩展,例如 Tampermonkey,才能安装此脚本。
您需要先安装用户脚本管理器扩展后才能安装此脚本。
框选网页内容,自动用 DeepSeek 生成总结,可自定义 Prompt 和 API Key!支持自动检测正文和Markdown渲染
// ==UserScript== // @name DeepSeek 网页总结助手 // @namespace https://chat.openai.com/ // @version 1.4 // @description 框选网页内容,自动用 DeepSeek 生成总结,可自定义 Prompt 和 API Key!支持自动检测正文和Markdown渲染 // @author You // @match *://*/* // @grant GM_xmlhttpRequest // @grant GM_setValue // @grant GM_getValue // @grant GM_registerMenuCommand // @connect api.deepseek.com // @license MIT // ==/UserScript== (function () { 'use strict'; // 缓存变量 let lastSummaryResult = null; let buttonsVisible = GM_getValue('deepseek_buttons_visible', true); // 注册TamperMonkey菜单 GM_registerMenuCommand('⚙️ 设置 API Key 和 Prompt', () => { createSettingsPanel(); }); GM_registerMenuCommand(buttonsVisible ? '👁️ 隐藏DeepSeek按钮' : '👁️ 显示DeepSeek按钮', () => { toggleButtons(); }); GM_registerMenuCommand('🐛 切换调试模式', () => { const currentDebugMode = GM_getValue('deepseek_debug_mode', false); GM_setValue('deepseek_debug_mode', !currentDebugMode); // 显示通知 const newStatus = !currentDebugMode ? '已开启' : '已关闭'; alert(`🐛 调试模式${newStatus}\n${!currentDebugMode ? '总结结果将显示详细的调试信息' : '总结结果将不再显示调试信息'}`); }); // 切换按钮显示状态 function toggleButtons() { buttonsVisible = !buttonsVisible; GM_setValue('deepseek_buttons_visible', buttonsVisible); if (buttonsVisible) { autoSummaryButton.style.display = 'flex'; } else { autoSummaryButton.style.display = 'none'; } // 重新注册菜单命令 // location.reload(); } // 简单的Markdown渲染器 function renderMarkdown(text) { return text // 标题 .replace(/^### (.*$)/gim, '<h3>$1</h3>') .replace(/^## (.*$)/gim, '<h2>$1</h2>') .replace(/^# (.*$)/gim, '<h1>$1</h1>') // 粗体 .replace(/\*\*(.*?)\*\*/g, '<strong>$1</strong>') .replace(/__(.*?)__/g, '<strong>$1</strong>') // 斜体 .replace(/\*(.*?)\*/g, '<em>$1</em>') .replace(/_(.*?)_/g, '<em>$1</em>') // 代码块 .replace(/```([\s\S]*?)```/g, '<pre><code>$1</code></pre>') // 行内代码 .replace(/`(.*?)`/g, '<code>$1</code>') // 链接 .replace(/\[([^\]]+)\]\(([^)]+)\)/g, '<a href="$2" target="_blank">$1</a>') // 列表 .replace(/^\* (.*$)/gim, '<li>$1</li>') .replace(/^- (.*$)/gim, '<li>$1</li>') .replace(/^\d+\. (.*$)/gim, '<li>$1</li>') // 换行 .replace(/\n\n/g, '</p><p>') .replace(/\n/g, '<br>'); // 包装列表项 let result = '<p>' + result + '</p>'; result = result.replace(/(<li>.*<\/li>)/gs, '<ul>$1</ul>'); result = result.replace(/<\/ul>\s*<ul>/g, ''); return result; } // 自动检测网页正文内容 function extractMainContent() { // 常见的正文选择器,按优先级排序 const contentSelectors = [ // 语义化标签 'main', 'article', '[role="main"]', '.main-content', '.content', '.post-content', '.article-content', '.entry-content', '.page-content', // 常见的内容类名 '.content-body', '.article-body', '.post-body', '.main-text', '.text-content', '.article-text', // 中文网站常见类名 '.article', '.post', '.content-wrap', '.main-wrap', '.container .content', // 特定网站适配 '.markdown-body', // GitHub '.Post-RichText', // 知乎 '.content_area', // CSDN '.article-content', // 简书 '.rich_media_content', // 微信公众号 '.content', // 通用 ]; let bestContent = null; let maxScore = 0; for (const selector of contentSelectors) { const elements = document.querySelectorAll(selector); for (const element of elements) { const text = element.innerText || element.textContent || ''; const textLength = text.trim().length; // 跳过太短的内容 if (textLength < 100) continue; // 计算内容质量分数 let score = textLength; // 加分项 if (element.tagName === 'MAIN' || element.tagName === 'ARTICLE') score += 1000; if (element.querySelector('p')) score += 500; // 包含段落 if (element.querySelector('h1, h2, h3, h4, h5, h6')) score += 300; // 包含标题 // 减分项 if (element.querySelector('nav')) score -= 200; // 包含导航 if (element.querySelector('.sidebar')) score -= 200; // 包含侧边栏 if (element.querySelector('.footer')) score -= 200; // 包含页脚 if (element.querySelector('.header')) score -= 200; // 包含页头 if (element.querySelector('.ad, .advertisement')) score -= 300; // 包含广告 // 检查是否包含太多链接(可能是导航区域) const links = element.querySelectorAll('a'); const linkRatio = links.length / (textLength / 100); if (linkRatio > 5) score -= 400; if (score > maxScore) { maxScore = score; bestContent = element; } } } // 如果没找到合适的内容,尝试body但排除明显的非内容区域 if (!bestContent) { const bodyText = document.body.innerText || document.body.textContent || ''; if (bodyText.trim().length > 200) { // 创建一个临时元素来清理内容 const tempDiv = document.createElement('div'); tempDiv.innerHTML = document.body.innerHTML; // 移除明显的非内容元素 const removeSelectors = [ 'nav', 'header', 'footer', 'aside', '.nav', '.header', '.footer', '.sidebar', '.menu', '.advertisement', '.ad', '.ads', '.banner', 'script', 'style', 'noscript' ]; removeSelectors.forEach(selector => { const elements = tempDiv.querySelectorAll(selector); elements.forEach(el => el.remove()); }); bestContent = tempDiv; } } if (bestContent) { let text = bestContent.innerText || bestContent.textContent || ''; // 清理文本 text = text .replace(/\s+/g, ' ') // 合并多个空白字符 .replace(/\n\s*\n/g, '\n') // 合并多个换行 .trim(); // 限制长度,避免内容过长 if (text.length > 16000) { // 智能截断:尝试在句号、换行或段落处截断 let cutPoint = 16000; const sentenceEnd = text.lastIndexOf('。', cutPoint); const paragraphEnd = text.lastIndexOf('\n\n', cutPoint); const lineEnd = text.lastIndexOf('\n', cutPoint); // 选择最佳截断点 if (sentenceEnd > cutPoint - 500) { cutPoint = sentenceEnd + 1; } else if (paragraphEnd > cutPoint - 1000) { cutPoint = paragraphEnd; } else if (lineEnd > cutPoint - 200) { cutPoint = lineEnd; } text = text.substring(0, cutPoint) + '\n\n[内容过长,已智能截断...]'; } return text; } return null; } // 创建模态背景 function createModalBackground() { const overlay = document.createElement('div'); overlay.className = 'deepseek-modal-overlay'; overlay.style.cssText = ` position: fixed; top: 0; left: 0; width: 100%; height: 100%; background: rgba(0, 0, 0, 0.5); z-index: 9998; opacity: 0; transition: opacity 0.3s ease; `; document.body.appendChild(overlay); // 淡入效果 setTimeout(() => overlay.style.opacity = '1', 10); return overlay; } // 创建设置面板 function createSettingsPanel() { // 检查是否已存在设置面板 const existingPanel = document.getElementById('deepseek-settings-panel'); if (existingPanel) { existingPanel.remove(); const existingOverlay = document.querySelector('.deepseek-modal-overlay'); if (existingOverlay) existingOverlay.remove(); return; } const overlay = createModalBackground(); const panel = document.createElement('div'); panel.id = 'deepseek-settings-panel'; panel.style.cssText = ` position: fixed; top: 50%; left: 50%; transform: translate(-50%, -50%) scale(0.9); background: #fff; border: none; padding: 25px; border-radius: 15px; box-shadow: 0 10px 30px rgba(0,0,0,0.3); z-index: 9999; max-width: 400px; width: 90%; font-family: -apple-system, BlinkMacSystemFont, 'Segoe UI', Roboto, sans-serif; opacity: 0; transition: all 0.3s ease; `; panel.innerHTML = ` <div style="display: flex; justify-content: space-between; align-items: center; margin-bottom: 20px;"> <h3 style="margin: 0; color: #333; font-size: 18px;">🛠️ DeepSeek 设置</h3> <button id="deepseek-close" style=" background: none; border: none; font-size: 24px; cursor: pointer; color: #666; padding: 0; width: 30px; height: 30px; border-radius: 50%; display: flex; align-items: center; justify-content: center; transition: background-color 0.2s; " title="关闭">×</button> </div> <div style="margin-bottom: 20px;"> <label style="display: block; margin-bottom: 8px; font-weight: 500; color: #333;">API Key:</label> <input id="deepseek-key" type="password" style=" width: 100%; padding: 12px; border: 2px solid #e1e5e9; border-radius: 8px; font-size: 14px; transition: border-color 0.2s; box-sizing: border-box; " placeholder="sk-xxx" /> </div> <div style="margin-bottom: 25px;"> <label style="display: block; margin-bottom: 8px; font-weight: 500; color: #333;">Prompt 模板:</label> <textarea id="deepseek-prompt" style=" width: 100%; height: 100px; padding: 12px; border: 2px solid #e1e5e9; border-radius: 8px; font-size: 14px; resize: vertical; font-family: inherit; transition: border-color 0.2s; box-sizing: border-box; " placeholder="请总结以下内容:{content}"></textarea> <small style="color: #666; font-size: 12px;">使用 {content} 作为内容占位符</small> </div> <div style="margin-bottom: 25px;"> <label style="display: flex; align-items: center; cursor: pointer; font-weight: 500; color: #333;"> <input type="checkbox" id="deepseek-debug" style="margin-right: 8px; transform: scale(1.2);"> 显示调试信息(输入/输出长度等) </label> <small style="color: #666; font-size: 12px; margin-left: 24px;">开启后会在总结结果中显示详细的调试信息</small> </div> <div style="display: flex; gap: 10px;"> <button id="deepseek-save" style=" flex: 1; padding: 12px 20px; background: #007bff; color: white; border: none; border-radius: 8px; font-size: 14px; font-weight: 500; cursor: pointer; transition: background-color 0.2s; ">💾 保存配置</button> <button id="deepseek-cancel" style=" padding: 12px 20px; background: #6c757d; color: white; border: none; border-radius: 8px; font-size: 14px; font-weight: 500; cursor: pointer; transition: background-color 0.2s; ">取消</button> </div> `; document.body.appendChild(panel); // 添加样式效果 setTimeout(() => { panel.style.opacity = '1'; panel.style.transform = 'translate(-50%, -50%) scale(1)'; }, 10); // 加载保存的配置 const savedKey = GM_getValue('deepseek_api_key', ''); const savedPrompt = localStorage.getItem('deepseek_prompt_template') || "告诉我这篇文章最有价值的信息是什么:==={content}===,用中文回复"; const savedDebug = GM_getValue('deepseek_debug_mode', false); if (savedKey) document.getElementById('deepseek-key').value = savedKey; document.getElementById('deepseek-prompt').value = savedPrompt; document.getElementById('deepseek-debug').checked = savedDebug; // 关闭面板函数 function closePanel() { panel.style.opacity = '0'; panel.style.transform = 'translate(-50%, -50%) scale(0.9)'; overlay.style.opacity = '0'; setTimeout(() => { panel.remove(); overlay.remove(); }, 300); } // 事件监听 document.getElementById('deepseek-save').onclick = () => { const key = document.getElementById('deepseek-key').value.trim(); const prompt = document.getElementById('deepseek-prompt').value.trim(); const debugMode = document.getElementById('deepseek-debug').checked; if (!key) { alert('⚠️ 请输入 API Key'); return; } GM_setValue('deepseek_api_key', key); localStorage.setItem('deepseek_prompt_template', prompt); GM_setValue('deepseek_debug_mode', debugMode); // 显示成功提示 const saveBtn = document.getElementById('deepseek-save'); const originalText = saveBtn.innerHTML; saveBtn.innerHTML = '✅ 已保存'; saveBtn.style.background = '#28a745'; setTimeout(() => { saveBtn.innerHTML = originalText; saveBtn.style.background = '#007bff'; closePanel(); }, 1000); }; document.getElementById('deepseek-close').onclick = closePanel; document.getElementById('deepseek-cancel').onclick = closePanel; // 点击背景关闭 overlay.onclick = closePanel; // ESC键关闭 const escHandler = (e) => { if (e.key === 'Escape') { closePanel(); document.removeEventListener('keydown', escHandler); } }; document.addEventListener('keydown', escHandler); // 输入框焦点样式 const inputs = panel.querySelectorAll('input, textarea'); inputs.forEach(input => { input.addEventListener('focus', () => { input.style.borderColor = '#007bff'; }); input.addEventListener('blur', () => { input.style.borderColor = '#e1e5e9'; }); }); // 按钮悬停效果 document.getElementById('deepseek-save').addEventListener('mouseenter', function() { this.style.background = '#0056b3'; }); document.getElementById('deepseek-save').addEventListener('mouseleave', function() { this.style.background = '#007bff'; }); document.getElementById('deepseek-cancel').addEventListener('mouseenter', function() { this.style.background = '#545b62'; }); document.getElementById('deepseek-cancel').addEventListener('mouseleave', function() { this.style.background = '#6c757d'; }); document.getElementById('deepseek-close').addEventListener('mouseenter', function() { this.style.backgroundColor = '#f8f9fa'; }); document.getElementById('deepseek-close').addEventListener('mouseleave', function() { this.style.backgroundColor = 'transparent'; }); } // 显示总结结果弹窗 function showResultPopup(summary, duration, isMarkdown = true, originalContent = '') { // 移除已存在的结果弹窗 const existingPopup = document.getElementById('deepseek-result-popup'); if (existingPopup) existingPopup.remove(); const overlay = createModalBackground(); const popup = document.createElement('div'); popup.id = 'deepseek-result-popup'; popup.style.cssText = ` position: fixed; top: 50%; left: 50%; transform: translate(-50%, -50%) scale(0.9); background: #fff; border: none; padding: 0; border-radius: 15px; box-shadow: 0 10px 30px rgba(0,0,0,0.3); z-index: 9999; max-width: 800px; width: 90%; max-height: 85vh; font-family: -apple-system, BlinkMacSystemFont, 'Segoe UI', Roboto, sans-serif; opacity: 0; transition: all 0.3s ease; display: flex; flex-direction: column; `; const durationText = duration ? ` (${duration}ms)` : ''; const currentUrl = window.location.href; const domain = window.location.hostname; popup.innerHTML = ` <div style=" display: flex; justify-content: space-between; align-items: center; padding: 20px 25px; border-bottom: 1px solid #e1e5e9; background: #f8f9fa; border-radius: 15px 15px 0 0; "> <h3 style="margin: 0; color: #333; font-size: 18px;">📝 总结结果${durationText}</h3> <div style="display: flex; gap: 8px; align-items: center;"> <button id="save-result" style=" background: #17a2b8; color: white; border: none; padding: 6px 12px; border-radius: 6px; font-size: 12px; cursor: pointer; transition: background-color 0.2s; " title="保存为文件">💾 保存</button> <button id="copy-result" style=" background: #28a745; color: white; border: none; padding: 6px 12px; border-radius: 6px; font-size: 12px; cursor: pointer; transition: background-color 0.2s; " title="复制到剪贴板">📋 复制</button> <button id="close-result" style=" background: none; border: none; font-size: 24px; cursor: pointer; color: #666; padding: 0; width: 30px; height: 30px; border-radius: 50%; display: flex; align-items: center; justify-content: center; transition: background-color 0.2s; " title="关闭">×</button> </div> </div> <div style=" padding: 25px; overflow-y: auto; flex: 1; line-height: 1.6; color: #333; font-size: 14px; " id="result-content"></div> `; document.body.appendChild(popup); // 设置内容 - 支持Markdown渲染 const contentDiv = document.getElementById('result-content'); if (isMarkdown && (summary.includes('**') || summary.includes('##') || summary.includes('*'))) { contentDiv.innerHTML = renderMarkdown(summary); // 为渲染后的内容添加样式 contentDiv.style.cssText += ` h1, h2, h3 { color: #2c3e50; margin: 20px 0 10px 0; } h1 { font-size: 24px; border-bottom: 2px solid #3498db; padding-bottom: 10px; } h2 { font-size: 20px; border-bottom: 1px solid #bdc3c7; padding-bottom: 5px; } h3 { font-size: 18px; } strong { color: #e74c3c; font-weight: 600; } em { color: #8e44ad; font-style: italic; } code { background: #f8f9fa; padding: 2px 6px; border-radius: 4px; font-family: 'Courier New', monospace; color: #e83e8c; } pre { background: #f8f9fa; padding: 15px; border-radius: 8px; overflow-x: auto; border-left: 4px solid #007bff; } pre code { background: none; padding: 0; color: #333; } ul, ol { margin: 10px 0; padding-left: 20px; } li { margin: 5px 0; } a { color: #007bff; text-decoration: none; } a:hover { text-decoration: underline; } p { margin: 10px 0; } `; } else { contentDiv.textContent = summary; } // 添加动画效果 setTimeout(() => { popup.style.opacity = '1'; popup.style.transform = 'translate(-50%, -50%) scale(1)'; }, 10); // 关闭函数 function closePopup() { popup.style.opacity = '0'; popup.style.transform = 'translate(-50%, -50%) scale(0.9)'; overlay.style.opacity = '0'; setTimeout(() => { popup.remove(); overlay.remove(); }, 300); } // 生成文件名 function generateFileName() { const now = new Date(); const timestamp = now.getFullYear() + String(now.getMonth() + 1).padStart(2, '0') + String(now.getDate()).padStart(2, '0') + '_' + String(now.getHours()).padStart(2, '0') + String(now.getMinutes()).padStart(2, '0') + String(now.getSeconds()).padStart(2, '0'); return `${domain}_${timestamp}.md`; } // 生成保存内容 function generateSaveContent() { const now = new Date(); const dateStr = now.toLocaleString('zh-CN'); return `# 网页总结 ## 基本信息 - **网站**: ${domain} - **URL**: ${currentUrl} - **总结时间**: ${dateStr} - **处理耗时**: ${duration}ms ## 总结内容 ${summary} ## 完整原始内容 \`\`\` ${originalContent} \`\`\` --- *由 DeepSeek 网页总结助手生成*`; } // 保存功能 document.getElementById('save-result').onclick = () => { try { const content = generateSaveContent(); const fileName = generateFileName(); // 创建下载链接 const blob = new Blob([content], { type: 'text/markdown;charset=utf-8' }); const url = URL.createObjectURL(blob); const link = document.createElement('a'); link.href = url; link.download = fileName; link.style.display = 'none'; document.body.appendChild(link); link.click(); document.body.removeChild(link); URL.revokeObjectURL(url); // 显示成功提示 const saveBtn = document.getElementById('save-result'); const originalText = saveBtn.innerHTML; saveBtn.innerHTML = '✅ 已保存'; saveBtn.style.background = '#28a745'; setTimeout(() => { saveBtn.innerHTML = originalText; saveBtn.style.background = '#17a2b8'; }, 2000); } catch (error) { alert('保存失败:' + error.message); } }; // 复制功能 - 复制原始文本而不是HTML document.getElementById('copy-result').onclick = async () => { try { await navigator.clipboard.writeText(summary); const copyBtn = document.getElementById('copy-result'); const originalText = copyBtn.innerHTML; copyBtn.innerHTML = '✅ 已复制'; copyBtn.style.background = '#20c997'; setTimeout(() => { copyBtn.innerHTML = originalText; copyBtn.style.background = '#28a745'; }, 1500); } catch (err) { // 降级方案 const textArea = document.createElement('textarea'); textArea.value = summary; document.body.appendChild(textArea); textArea.select(); document.execCommand('copy'); document.body.removeChild(textArea); alert('📋 内容已复制到剪贴板'); } }; // 事件监听 document.getElementById('close-result').onclick = closePopup; overlay.onclick = closePopup; // ESC键关闭 const escHandler = (e) => { if (e.key === 'Escape') { closePopup(); document.removeEventListener('keydown', escHandler); } }; document.addEventListener('keydown', escHandler); // 按钮悬停效果 const buttons = [ { id: 'save-result', normalColor: '#17a2b8', hoverColor: '#138496' }, { id: 'copy-result', normalColor: '#28a745', hoverColor: '#1e7e34' }, ]; buttons.forEach(btn => { const element = document.getElementById(btn.id); element.addEventListener('mouseenter', function() { if (!this.innerHTML.includes('✅')) { this.style.background = btn.hoverColor; } }); element.addEventListener('mouseleave', function() { if (!this.innerHTML.includes('✅')) { this.style.background = btn.normalColor; } }); }); document.getElementById('close-result').addEventListener('mouseenter', function() { this.style.backgroundColor = '#f8f9fa'; }); document.getElementById('close-result').addEventListener('mouseleave', function() { this.style.backgroundColor = 'transparent'; }); } // API 调用函数 function getSummary(userText) { return new Promise((resolve, reject) => { const startTime = performance.now(); const key = GM_getValue('deepseek_api_key', ''); const promptTemplate = localStorage.getItem('deepseek_prompt_template') || "告诉我这篇文章最有价值的信息是什么:==={content}===,用中文回复"; if (!key) { resolve({ summary: "❌ 未设置 API Key,请在油猴菜单中进行配置", duration: Math.round(performance.now() - startTime), originalContent: userText }); return; } const prompt = promptTemplate.replace('{content}', userText); const body = JSON.stringify({ model: "deepseek-chat", messages: [{ role: "user", content: prompt }], temperature: 0.3, max_tokens: 4000, // 增加最大输出token数,确保总结不被截断 top_p: 0.95, // 添加top_p参数提高输出质量 frequency_penalty: 0.1, // 减少重复内容 presence_penalty: 0.1 // 鼓励多样化表达 }); GM_xmlhttpRequest({ method: "POST", url: "https://api.deepseek.com/v1/chat/completions", headers: { "Content-Type": "application/json", "Authorization": "Bearer " + key }, data: body, onload: function (res) { const duration = Math.round(performance.now() - startTime); try { const json = JSON.parse(res.responseText); if (json.error) { resolve({ summary: "❌ API 错误:" + (json.error.message || json.error), duration: duration, originalContent: userText }); return; } const result = json.choices?.[0]?.message?.content?.trim(); // 添加调试信息 const inputLength = userText.length; const outputLength = result ? result.length : 0; const finishReason = json.choices?.[0]?.finish_reason; let debugInfo = ''; const debugMode = GM_getValue('deepseek_debug_mode', false); if (debugMode) { debugInfo = `\n\n---\n📊 调试信息:\n`; debugInfo += `• 输入长度:${inputLength} 字符\n`; debugInfo += `• 输出长度:${outputLength} 字符\n`; debugInfo += `• 完成原因:${finishReason || '未知'}\n`; debugInfo += `• 处理时间:${duration}ms`; if (finishReason === 'length') { debugInfo += `\n⚠️ 输出因长度限制被截断,建议缩短输入内容或调整max_tokens参数`; } } resolve({ summary: (result || "❌ 无法生成总结") + debugInfo, duration: duration, originalContent: userText }); } catch (e) { resolve({ summary: "❌ 响应解析失败:" + e.message, duration: duration, originalContent: userText }); } }, onerror: function (err) { const duration = Math.round(performance.now() - startTime); resolve({ summary: "❌ 请求失败,请检查网络连接", duration: duration, originalContent: userText }); } }); }); } // 创建自动总结按钮 const autoSummaryButton = document.createElement('button'); autoSummaryButton.innerHTML = "🤖 智能总结"; autoSummaryButton.style.cssText = ` position: fixed; bottom: 20px; right: 20px; background: linear-gradient(135deg, #ff6b6b 0%, #ee5a24 100%); color: white; border: none; padding: 12px 20px; border-radius: 25px; cursor: pointer; font-size: 14px; font-weight: 500; z-index: 9996; box-shadow: 0 4px 12px rgba(0,0,0,0.15); transition: all 0.2s ease; font-family: -apple-system, BlinkMacSystemFont, 'Segoe UI', Roboto, sans-serif; user-select: none; display: ${buttonsVisible ? 'flex' : 'none'}; align-items: center; justify-content: center; `; autoSummaryButton.title = "自动检测并总结网页正文"; document.body.appendChild(autoSummaryButton); // 自动总结按钮悬停效果 autoSummaryButton.addEventListener('mouseenter', () => { autoSummaryButton.style.transform = 'translateY(-2px)'; autoSummaryButton.style.boxShadow = '0 6px 16px rgba(0,0,0,0.2)'; }); autoSummaryButton.addEventListener('mouseleave', () => { autoSummaryButton.style.transform = 'translateY(0)'; autoSummaryButton.style.boxShadow = '0 4px 12px rgba(0,0,0,0.15)'; }); // 自动总结按钮点击事件 autoSummaryButton.addEventListener('click', async () => { // 检查是否已配置API Key const apiKey = GM_getValue('deepseek_api_key', ''); if (!apiKey) { // 如果没有配置API Key,先弹出设置页面 createSettingsPanel(); return; } // 如果有缓存的结果,直接显示 if (lastSummaryResult) { showResultPopup( lastSummaryResult.summary, lastSummaryResult.duration, true, lastSummaryResult.originalContent ); return; } autoSummaryButton.innerHTML = "🔍 检测中..."; autoSummaryButton.style.cursor = "wait"; try { const mainContent = extractMainContent(); if (!mainContent) { showResultPopup("❌ 未能检测到网页正文内容,请尝试手动选择文本进行总结", 0, false, ''); return; } if (mainContent.length < 50) { showResultPopup("❌ 检测到的内容过短,请尝试手动选择文本进行总结", 0, false, mainContent); return; } autoSummaryButton.innerHTML = "⏳ 总结中..."; const result = await getSummary(mainContent); // 缓存成功的结果 if (!result.summary.startsWith('❌')) { lastSummaryResult = result; } showResultPopup(result.summary, result.duration, true, result.originalContent); } catch (error) { showResultPopup("❌ 自动总结时发生错误:" + error.message, 0, false, ''); } finally { autoSummaryButton.innerHTML = "🤖 智能总结"; autoSummaryButton.style.cursor = "pointer"; } }); // 添加全局样式,防止与网站样式冲突 const style = document.createElement('style'); style.textContent = ` #deepseek-settings-panel *, #deepseek-result-popup * { box-sizing: border-box !important; } `; document.head.appendChild(style); })();