// ==UserScript== // @name Deepseek Chat 实时网页检索对话工具版 // @namespace Monika_host // @version 3.1.5 // @description 支持流式响应、历史记录、参数设置和全面的网页内容检索,增强Markdown渲染 // @author Monika_host // @match *://*/* // @grant GM_getValue // @grant GM_setValue // @grant GM_xmlhttpRequest // @grant GM_registerMenuCommand // @grant GM_addStyle // @connect * // @license MIT // @resource icon https://img.alicdn.com/imgextra/i2/O1CN01bYc1m81RrcSAyOjMu_!!6000000002165-54-tps-60-60.apng // @grant GM_getResourceURL // @icon https://deepseek.com/favicon.ico // ==/UserScript== (function() { 'use strict'; // 加载Markdown渲染资源 function loadMarkdownResources() { return new Promise((resolve) => { // 加载Highlight.js const hljsScript = document.createElement('script'); hljsScript.src = 'https://cdnjs.cloudflare.com/ajax/libs/highlight.js/11.9.0/highlight.min.js'; hljsScript.onload = () => { // 加载KaTeX const katexScript = document.createElement('script'); katexScript.src = 'https://cdn.jsdelivr.net/npm/katex@0.16.9/dist/katex.min.js'; katexScript.onload = () => { // 加载KaTeX自动渲染扩展 const katexAutoRender = document.createElement('script'); katexAutoRender.src = 'https://cdn.jsdelivr.net/npm/katex@0.16.9/dist/contrib/auto-render.min.js'; katexAutoRender.onload = () => { // 加载KaTeX CSS const katexCSS = document.createElement('link'); katexCSS.rel = 'stylesheet'; katexCSS.href = 'https://cdn.jsdelivr.net/npm/katex@0.16.9/dist/katex.min.css'; document.head.appendChild(katexCSS); // 加载Mermaid const mermaidScript = document.createElement('script'); mermaidScript.src = 'https://cdn.jsdelivr.net/npm/mermaid@10.9.0/dist/mermaid.min.js'; mermaidScript.onload = () => { // 更新Mermaid配置并添加错误处理 try { mermaid.initialize({ startOnLoad: false, theme: 'dark', securityLevel: 'loose', flowchart: { curve: 'basis', useMaxWidth: false }, sequence: { showSequenceNumbers: true }, gantt: { axisFormat: '%Y-%m-%d' } }); } catch (e) { console.error('Mermaid初始化错误:', e); } resolve(); }; document.head.appendChild(mermaidScript); }; document.head.appendChild(katexAutoRender); }; document.head.appendChild(katexScript); }; document.head.appendChild(hljsScript); }); } // 添加Markdown渲染功能 function renderMarkdown(content) { // 增强版Markdown渲染,支持表格、列表、代码块等 let output = content // 处理任务列表 .replace(/^(\s*[-*+]\s+\[ \]\s+.+(\n\s*[-*+]\s+\[ \]\s+.+)*)/gm, list => { const items = list.split('\n').filter(Boolean).map(item => { const text = item.replace(/^\s*[-*+]\s+\[ \]\s+/, ''); return `
  • ${text}
  • `; }).join(''); return ``; }) .replace(/^(\s*[-*+]\s+\[x\]\s+.+(\n\s*[-*+]\s+\[x\]\s+.+)*)/gm, list => { const items = list.split('\n').filter(Boolean).map(item => { const text = item.replace(/^\s*[-*+]\s+\[x\]\s+/, ''); return `
  • ${text}
  • `; }).join(''); return ``; }) // 处理数学公式 .replace(/\$\$(.*?)\$\$/g, (match, formula) => { return `${formula}`; }) .replace(/\$(.*?)\$/g, (match, formula) => { return `${formula}`; }); // 应用其他Markdown转换 output = output .replace(/&/g, '&') .replace(//g, '>') .replace(/\*\*(.*?)\*\*/g, '$1') .replace(/\*(.*?)\*/g, '$1') .replace(/`(.*?)`/g, '$1') .replace(/\[(.*?)\]\((.*?)\)/g, '$1'); // 处理表格 output = output.replace(/^\|(.+)\|$\n^\|(?:-+\|)+$\n((?:^\|.+$\n?)+)/gm, (match, header, rows) => { const headers = header.split('|').map(h => `${h.trim()}`).join(''); const body = rows.split('\n').filter(Boolean).map(row => { const cells = row.split('|').slice(1, -1).map(cell => `${cell.trim()}`).join(''); return `${cells}`; }).join(''); return `${headers}${body}
    `; }); // 处理列表 output = output.replace(/^(\s*[-*+]\s+.+(\n\s*[-*+]\s+.+)*)/gm, list => { const isOrdered = /^\s*\d+\./.test(list); const tag = isOrdered ? 'ol' : 'ul'; const items = list.split('\n').filter(Boolean).map(item => { const text = item.replace(/^\s*[-*+]\s+|\d+\.\s+/, ''); return `
  • ${text}
  • `; }).join(''); return `<${tag} class="ds-markdown-list">${items}`; }); // 处理代码块 output = output.replace(/```([\w-]+)?\n([\s\S]*?)```/g, (match, lang, code) => { if (lang === 'mermaid') { // 对于mermaid图表,不进行转义 return `
    ${code}
    `; } else { return `
    ${escapeHtml(code)}
    `; } }); // 处理块引用 output = output.replace(/^>\s*(.+)$/gm, '
    $1
    '); // 处理水平线 output = output.replace(/^---$/gm, '
    '); // 处理标题 output = output.replace(/^(#{1,6})\s*(.+?)\s*#*\s*$/gm, (match, hashes, text) => { const level = hashes.length; return `${text}`; }); return output; // HTML转义辅助函数 function escapeHtml(unsafe) { return unsafe .replace(/&/g, "&") .replace(//g, ">") .replace(/"/g, '"') .replace(/'/g, ''') } } // 添加CSS样式 GM_addStyle(` /* 数学公式渲染增强 */ .katex-display { overflow-x: auto; overflow-y: hidden; padding: 15px; background: rgba(30, 30, 40, 0.8); border-radius: 8px; border: 1px solid rgba(100, 100, 255, 0.3); box-shadow: 0 2px 8px rgba(0, 0, 0, 0.2); margin: 15px 0; } /* Mermaid图表容器增强 */ .ds-mermaid { background: rgba(25, 25, 35, 0.9); padding: 15px; border-radius: 8px; margin: 15px 0; border: 1px solid rgba(100, 200, 255, 0.2); overflow: auto; max-height: 500px; box-shadow: 0 4px 12px rgba(0, 0, 0, 0.25); display: flex; justify-content: center; align-items: center; } /* 通用图表容器 */ .ds-chart-container { background: rgba(30, 30, 45, 0.9); padding: 20px; border-radius: 10px; border: 1px solid rgba(150, 150, 255, 0.3); margin: 20px 0; box-shadow: 0 5px 15px rgba(0, 0, 0, 0.3); overflow: auto; max-width: 100%; } /* 增强的Markdown渲染支持 */ .ds-message-content strong { font-weight: bold; color: #d63384; text-shadow: 0 0 8px rgba(214, 51, 132, 0.5); } .ds-message-content em { font-style: italic; background: linear-gradient(45deg, #ff9a9e, #fad0c4); -webkit-background-clip: text; background-clip: text; color: transparent; } /* 增强的内联代码样式 - 3D效果 */ .ds-message-content code, .ds-message-content .inline-code { background: linear-gradient(145deg, #1a1a2e, #16213e); color: #66fcf1; padding: 2px 8px; border-radius: 4px; font-family: monospace; border: 1px solid rgba(102, 252, 241, 0.3); box-shadow: 0 3px 6px rgba(0,0,0,0.3), inset 0 1px 1px rgba(255,255,255,0.1); display: inline-block; transform: translateY(-1px); transition: all 0.3s ease; } .ds-message-content code:hover, .ds-message-content .inline-code:hover { transform: translateY(-2px); box-shadow: 0 5px 10px rgba(0,0,0,0.4), inset 0 1px 2px rgba(255,255,255,0.2); color: #45a29e; } .ds-message-content pre { background-color: rgba(0, 0, 0, 0.1); padding: 12px; border-radius: 6px; overflow-x: auto; margin: 10px 0; } .ds-message-content pre code { background: none !important; padding: 0; } .ds-message-content .inline-code { background-color: rgba(0, 0, 0, 0.1); padding: 2px 4px; border-radius: 3px; } .ds-markdown-table { border-collapse: collapse; width: 100%; margin: 12px 0; background-color: rgba(255, 255, 255, 0.1); } .ds-markdown-table th, .ds-markdown-table td { border: 1px solid rgba(0, 0, 0, 0.2); padding: 8px; text-align: left; } .ds-markdown-table th { background-color: rgba(0, 123, 255, 0.2); } .ds-markdown-list { padding-left: 25px; margin: 10px 0; } .ds-markdown-list li { margin-bottom: 5px; } blockquote { border-left: 3px solid rgba(0, 123, 255, 0.5); padding-left: 10px; margin: 10px 0; color: #6c757d; } hr { border: 0; height: 1px; background: rgba(0, 0, 0, 0.2); margin: 15px 0; } /* 标题渐变色效果 */ .ds-markdown-h1 { font-size: 1.8em; margin: 15px 0; background: linear-gradient(90deg, #ff6b6b, #4ecdc4); -webkit-background-clip: text; background-clip: text; color: transparent; } .ds-markdown-h2 { font-size: 1.5em; margin: 13px 0; background: linear-gradient(90deg, #4ecdc4, #1a936f); -webkit-background-clip: text; background-clip: text; color: transparent; } .ds-markdown-h3 { font-size: 1.3em; margin: 11px 0; background: linear-gradient(90deg, #1a936f, #114b5f); -webkit-background-clip: text; background-clip: text; color: transparent; } .ds-markdown-h4 { font-size: 1.1em; margin: 9px 0; background: linear-gradient(90deg, #114b5f, #0d3a4a); -webkit-background-clip: text; background-clip: text; color: transparent; } .ds-markdown-h5 { font-size: 0.9em; margin: 7px 0; background: linear-gradient(90deg, #0d3a4a, #082a35); -webkit-background-clip: text; background-clip: text; color: transparent; } .ds-markdown-h6 { font-size: 0.8em; margin: 5px 0; background: linear-gradient(90deg, #082a35, #041a1f); -webkit-background-clip: text; background-clip: text; color: transparent; } .ds-message-content a { color: #007bff; text-decoration: underline; } /* 任务列表样式 */ .ds-task-list { list-style: none; padding-left: 25px; margin: 10px 0; } .ds-task-list li { margin-bottom: 5px; display: flex; align-items: center; } .ds-task-item { margin-right: 8px; } /* 数学公式样式 - 增强版 */ .ds-math-block { display: block; text-align: center; margin: 15px 0; padding: 15px; background: rgba(30, 30, 40, 0.7); border: 1px solid rgba(100, 100, 255, 0.3); border-radius: 8px; overflow-x: auto; box-shadow: 0 2px 8px rgba(0, 0, 0, 0.2); } .ds-math-inline { display: inline-block; padding: 2px 6px; background: rgba(40, 40, 60, 0.7); border: 1px solid rgba(80, 80, 200, 0.3); border-radius: 4px; margin: 0 2px; } /* 修复多行代码块背景渲染 */ .ds-message-content pre { background: rgba(10, 10, 20, 0.95) !important; /* 确保背景不被覆盖 */ padding: 15px; border-radius: 8px; overflow-x: auto; margin: 15px 0; border: 1px solid rgba(80, 120, 200, 0.4); box-shadow: 0 5px 15px rgba(0, 0, 0, 0.4); position: relative; } /* 增强的代码块标题 - 流光效果 */ .ds-message-content pre::before { content: attr(data-lang); display: block; position: absolute; top: -12px; left: 15px; background: linear-gradient(90deg, #0f2027, #203a43, #2c5364); color: #fff; padding: 5px 15px; border-radius: 5px 5px 0 0; font-family: monospace; font-size: 12px; text-transform: uppercase; z-index: 1; box-shadow: 0 4px 6px rgba(0,0,0,0.3); animation: titleGlow 3s infinite alternate; } @keyframes titleGlow { 0% { box-shadow: 0 4px 6px rgba(44, 83, 100, 0.5); } 100% { box-shadow: 0 4px 12px rgba(44, 83, 100, 0.8), 0 0 15px rgba(32, 58, 67, 0.6); } } /* 内联代码优化 - 修复与代码块的冲突 */ .ds-message-content code:not(pre code), .ds-message-content .inline-code { background-color: rgba(20, 20, 40, 0.8); color: #66d9ef; padding: 2px 4px; border-radius: 4px; font-family: monospace; border: 1px solid rgba(80, 120, 200, 0.3); } /* 代码高亮样式 - 修复背景覆盖问题 */ .ds-message-content pre code.hljs { display: block; overflow-x: auto; padding: 1em; background: transparent !important; /* 移除背景避免覆盖 */ } .ds-message-content code.hljs:not(pre code) { padding: 2px 4px; } .hljs { background: transparent !important; /* 移除背景避免覆盖 */ color: #f8f8f2; } .hljs-keyword, .hljs-selector-tag, .hljs-literal, .hljs-title { color: #f92672; } .hljs-string, .hljs-meta .hljs-meta-string, .hljs-number { color: #a6e22e; } .hljs-comment { color: #75715e; } .hljs-built_in, .hljs-class .hljs-title { color: #66d9ef; } .hljs-function .hljs-title, .hljs-params { color: #fd971f; } /* Mermaid图表样式 */ .ds-mermaid { background: rgba(25, 25, 35, 0.9); padding: 15px; border-radius: 8px; margin: 15px 0; border: 1px solid rgba(100, 200, 255, 0.2); overflow: auto; max-height: 500px; box-shadow: 0 4px 12px rgba(0, 0, 0, 0.25); } /* 原有样式保持不变 */ @keyframes fadeInOut { 0% { opacity: 0; } 100% { opacity: 1; } } @keyframes fadeOut { 0% { opacity: 1; transform: translateY(0); } 100% { opacity: 0; transform: translateY(20px); } } .ds-chat-icon img { width: 30px; height: 30px; border-radius: 50%; transition: all 0.3s ease; animation: breath 2s infinite alternate; } .ds-chat-icon:hover img { transform: scale(1.1); filter: drop-shadow(0 0 8px rgba(0, 123, 255, 0.6)); animation: pulse 0.5s infinite alternate; } @keyframes breath { 0% { opacity: 0.9; } 100% { opacity: 1; } } @keyframes pulse { 0% { transform: scale(1); } 100% { transform: scale(1.15); } } .ds-chat-window { position: fixed; bottom: 20px; right: 20px; width: 340px; max-width: 70vw; max-height: 70vh; background-color: rgba(249, 249, 249, 0.3); border: 1px solid #ddd; border-radius: 15px; box-shadow: 0 4px 10px rgba(0, 0, 0, 0.3); display: none; flex-direction: column; overflow: hidden; opacity: 0; transform: translateY(20px); z-index: 2147483646; backdrop-filter: blur(5px); animation: fadeInOut 0.5s ease-in-out forwards; transition: all 1s ease-in-out; } .ds-chat-window.active { display: flex; opacity: 1; transform: translateY(0); } .ds-chat-window.fullscreen { width: 100% !important; max-width: 100vw !important; max-height: 100vh !important; bottom: 0 !important; right: 0 !important; border-radius: 0 !important; animation: fadeInOut 1.2s ease-in-out forwards; } .ds-chat-icon { position: fixed; bottom: 20px; right: 20px; width: 50px; height: 50px; background-color: rgba(0, 123, 255, 0.5); border-radius: 50%; cursor: pointer; display: flex; align-items: center; justify-content: center; color: #fff; font-size: 24px; box-shadow: 0 4px 6px rgba(0, 0, 0, 0.3); transition: transform 0.3s, box-shadow 0.3s; z-index: 2147483647; backdrop-filter: blur(5px); border: 1px solid rgba(255, 255, 255, 0.4); } .ds-chat-icon:hover { transform: scale(1.05); box-shadow: 0 6px 8px rgba(0, 0, 0, 0.3); background-color: rgba(0, 123, 255, 0.6); } .ds-chat-header { padding: 10px 15px; background-color: rgba(0, 123, 255, 0.3); color: white; display: flex; justify-content: space-between; align-items: center; border-radius: 15px 15px 0 0; } .ds-chat-title { font-weight: bold; color: #2372c3; } .ds-chat-close { cursor: pointer; font-size: 18px; color: #ff6666; } .ds-chat-fullscreen { cursor: pointer; font-size: 18px; margin-right: 10px; } /* HTML预览按钮 */ .ds-preview-html { background-color: rgba(0, 123, 255, 0.3); color: white; border: none; padding: 5px 10px; border-radius: 4px; cursor: pointer; margin-top: 5px; font-size: 12px; } .ds-preview-html:hover { background-color: rgba(0, 123, 255, 0.5); } /* HTML预览窗口 */ .ds-html-preview { position: fixed; top: 50%; left: 50%; transform: translate(-50%, -50%); width: 80%; height: 80%; background: white; border-radius: 10px; box-shadow: 0 10px 30px rgba(0,0,0,0.3); z-index: 2147483647; display: flex; flex-direction: column; overflow: hidden; } .ds-preview-header { padding: 10px 15px; background: #f0f0f0; display: flex; justify-content: space-between; align-items: center; border-bottom: 1px solid #ddd; } .ds-preview-iframe { flex: 1; border: none; } .ds-preview-close { cursor: pointer; font-size: 18px; color: #ff6666; } .ds-preview-overlay { position: fixed; top: 0; left: 0; width: 100%; height: 100%; background: rgba(0,0,0,0.7); z-index: 2147483646; } .ds-chat-content { flex: 1; padding: 0px; overflow-y: auto; background-color: rgba(255, 255, 255, 0.3); border-bottom: 1px solid #ddd; } .ds-chat-message { background-color: rgba(227, 242, 253, 0.1); margin-bottom: 10px; padding: 8px 12px; border-radius: 10px; line-height: 1.5; word-wrap: break-word; color: #2372c3; font-size: 14px; } .ds-user-message { background-color: rgba(227, 242, 253, 0.5); color: #4f856c; margin-left: auto; text-align: right; font-size: 14px; padding: 8px 12px; } .ds-ai-message { background-color: transparent; margin-right: 10%; font-size: 14px; padding: 8px 12px; line-height: 1.5; color: #2372c3; } .ds-chat-input-area { padding: 10px; display: flex; flex-direction: column; backdrop-filter: blur(10px); background-color: rgba(255, 255, 255, 0.3); border-top: 1px solid rgba(221, 221, 221, 0.5); } .ds-chat-input { width: 100%; padding: 8px 10px; border: 1px solid #ddd; border-radius: 8px; margin-bottom: 8px; outline: none; transition: border-color 0.3s; font-size: 15px; color: #3e6854; background-color: rgba(255, 255, 255, 0.8); box-sizing: border-box; } .ds-chat-input:hover { border-color: #90c8f3; box-shadow: 0 0 8px rgba(144, 200, 243, 0.4); } .ds-chat-input:focus { border-color: #5ab1f3; box-shadow: 0 0 10px rgba(90, 177, 243, 0.6); background-color: rgba(255, 255, 255, 0.9); } .ds-chat-settings { display: flex; justify-content: space-between; font-size: 12px; color: #666; } .ds-chat-settings-btn { cursor: pointer; text-decoration: underline; } .ds-thinking { color: #e87be4; font-style: italic; } .ds-error { color: #ff0000; } .ds-context-toggle { margin-bottom: 8px; display: flex; align-items: center; font-size: 12px; } .ds-context-toggle input { margin-right: 5px; } .ds-context-summary { font-size: 11px; color: #666; margin-top: 5px; font-style: italic; } .ds-chat-message { white-space: pre-wrap; word-break: break-word; visibility: visible !important; display: block !important; opacity: 1 !important; } .ds-ai-message { font-size: 14px; line-height: 1.5; padding: 8px 12px; margin: 0px 0px; background-color: rgba(255, 255, 255, 0.1); border-radius: 8px; box-shadow: 0 1px 2px rgba(0, 0, 0, 0.1); color: #2372c3 !important; } .ds-message-content { font-size: 14px !important; line-height: 1.5 !important; color: #2372c3 !important; display: block !important; visibility: visible !important; opacity: 1 !important; min-height: 1em; background: none !important; background-color: transparent !important; background-image: none !important; text-shadow: none !important; } @keyframes blink { 0%, 100% { opacity: 1; } 50% { opacity: 0; } } .ds-message-content::after { content: '|'; position: relative; display: inline; color: transparent !important; animation: blink 1s infinite; margin-left: 2px; } .ds-message-content:not(:empty)::after { display: none; } `); // 初始化配置 let config = { apiKey: GM_getValue('apiKey', ''), apiUrl: GM_getValue('apiUrl', 'https://api.deepseek.com/v1/chat/completions'), model: GM_getValue('model', 'deepseek-chat'), temperature: GM_getValue('temperature', 0.7), maxTokens: GM_getValue('maxTokens', 4000), maxContextTokens: GM_getValue('maxContextTokens', 32000), chatHistory: GM_getValue('chatHistory', []), usePageContext: GM_getValue('usePageContext', true), personalityPrompt: GM_getValue('personalityPrompt', '你是锐锐,一个18岁、热爱数学的可爱女孩。你性格聪明冷静,内心善良,对朋友真诚,伙伴遇困定会援手相助。\n你外貌甜美,皮肤白皙,大眼睛灵动有神。总是身着背带制服,搭配白色腿袜和小皮鞋,乌黑亮丽的高马尾活泼摆动,头上戴着红色蝴蝶结发箍。充满青春活力。\n你的性格特点:聪明、冷静、善良、真诚。\n你的说话风格:逻辑清晰,又温柔贴心。') }; // 检查是否已经存在图标 if (!document.querySelector('.ds-chat-icon')) { // 创建UI元素 const icon = document.createElement('div'); icon.className = 'ds-chat-icon'; icon.innerHTML = ``; document.body.appendChild(icon); // 确保图标位置固定在右下角5px处 icon.style.position = 'fixed'; icon.style.bottom = '5px'; icon.style.right = '5px'; icon.style.zIndex = '2147483647'; icon.style.display = 'flex'; const chatWindow = document.createElement('div'); chatWindow.className = 'ds-chat-window'; document.body.appendChild(chatWindow); const chatHeader = document.createElement('div'); chatHeader.className = 'ds-chat-header'; chatWindow.appendChild(chatHeader); const chatTitle = document.createElement('div'); chatTitle.className = 'ds-chat-title'; chatTitle.innerText = 'Deepseek Chat'; chatHeader.appendChild(chatTitle); const headerButtons = document.createElement('div'); headerButtons.style.display = 'flex'; headerButtons.style.alignItems = 'center'; chatHeader.appendChild(headerButtons); const fullscreenBtn = document.createElement('div'); fullscreenBtn.className = 'ds-chat-fullscreen'; fullscreenBtn.innerText = '🔘'; headerButtons.appendChild(fullscreenBtn); const closeBtn = document.createElement('div'); closeBtn.className = 'ds-chat-close'; closeBtn.innerText = '×'; headerButtons.appendChild(closeBtn); const chatContent = document.createElement('div'); chatContent.className = 'ds-chat-content'; chatWindow.appendChild(chatContent); const inputArea = document.createElement('div'); inputArea.className = 'ds-chat-input-area'; chatWindow.appendChild(inputArea); const contextToggle = document.createElement('div'); contextToggle.className = 'ds-context-toggle'; inputArea.appendChild(contextToggle); const contextCheckbox = document.createElement('input'); contextCheckbox.type = 'checkbox'; contextCheckbox.id = 'ds-context-checkbox'; contextCheckbox.checked = config.usePageContext; contextToggle.appendChild(contextCheckbox); const contextLabel = document.createElement('label'); contextLabel.htmlFor = 'ds-context-checkbox'; contextLabel.innerText = '🌐'; contextToggle.appendChild(contextLabel); const inputBox = document.createElement('textarea'); inputBox.className = 'ds-chat-input'; inputBox.placeholder = '输入你的问题...'; inputBox.rows = 2; inputBox.style.padding = '8px 10px'; inputArea.appendChild(inputBox); const settingsArea = document.createElement('div'); settingsArea.className = 'ds-chat-settings'; inputArea.appendChild(settingsArea); const settingsBtn = document.createElement('span'); settingsBtn.className = 'ds-chat-settings-btn'; settingsBtn.innerText = '⚙️'; settingsArea.appendChild(settingsBtn); const clearBtn = document.createElement('span'); clearBtn.className = 'ds-chat-settings-btn'; clearBtn.innerText = '🗑️'; settingsArea.appendChild(clearBtn); // 显示历史消息(已修改为使用Markdown渲染) function displayHistory() { chatContent.innerHTML = ''; config.chatHistory.forEach(msg => { const msgDiv = document.createElement('div'); msgDiv.className = `ds-chat-message ds-${msg.role}-message`; msgDiv.innerHTML = `
    ${renderMarkdown(msg.content)}
    `; chatContent.appendChild(msgDiv); }); setTimeout(() => { chatContent.scrollTop = chatContent.scrollHeight; // 加载资源并渲染 loadMarkdownResources().then(() => { // 高亮代码块 document.querySelectorAll('.ds-message-content pre code').forEach(block => { hljs.highlightElement(block); }); // 渲染所有数学公式(使用auto-render) renderMathInElement(document.body, { delimiters: [ {left: '$$', right: '$$', display: true}, {left: '$', right: '$', display: false} ], throwOnError: false }); // 增强Mermaid图表渲染 if (typeof mermaid !== 'undefined') { setTimeout(() => { document.querySelectorAll('.ds-mermaid:not([data-rendered="true"])').forEach(el => { try { // 检查是否包含有效的Mermaid代码 if (el.textContent.trim().length > 0) { mermaid.init(undefined, el); el.setAttribute('data-rendered', 'true'); } else { el.innerHTML = `
    空图表
    未检测到有效的Mermaid代码
    `; el.setAttribute('data-rendered', 'error'); } } catch (e) { console.error('Mermaid渲染错误:', e); el.innerHTML = `
    图表渲染失败
    错误: ${e.message}
    建议: 检查Mermaid语法或尝试刷新页面
    `; el.setAttribute('data-rendered', 'error'); } }); }, 300); // 增加延迟确保DOM完全加载 } // 特殊处理:当检测到流程图时自动重试渲染 const mermaidDiagrams = document.querySelectorAll('.ds-mermaid'); if (mermaidDiagrams.length > 0) { setTimeout(() => { if (typeof mermaid !== 'undefined') { mermaidDiagrams.forEach(el => { if (el.getAttribute('data-rendered') !== 'true') { try { mermaid.init(undefined, el); el.setAttribute('data-rendered', 'true'); } catch (e) { console.log('Mermaid重试渲染失败,但已显示错误信息'); } } }); } }, 1000); } }); }, 0); } displayHistory(); // 事件监听 icon.addEventListener('click', () => { chatWindow.classList.toggle('active'); icon.style.display = 'none'; setTimeout(() => { chatContent.scrollTop = chatContent.scrollHeight; }, 0); }); closeBtn.addEventListener('click', () => { // 添加关闭动画 chatWindow.style.animation = 'fadeOut 0.5s ease-in-out forwards'; // 保存当前是否全屏状态 const isFullscreen = chatWindow.classList.contains('fullscreen'); // 动画结束后隐藏窗口并重置样式 const handleAnimationEnd = () => { chatWindow.classList.remove('active'); // 如果是全屏状态,先移除全屏类 if (isFullscreen) { chatWindow.classList.remove('fullscreen'); } chatWindow.style.animation = ''; icon.style.display = 'flex'; chatWindow.removeEventListener('animationend', handleAnimationEnd); }; chatWindow.addEventListener('animationend', handleAnimationEnd); }); fullscreenBtn.addEventListener('click', () => { chatWindow.classList.toggle('fullscreen'); if (chatWindow.classList.contains('fullscreen')) { fullscreenBtn.innerText = '🔘'; } else { fullscreenBtn.innerText = '🔘'; } }); contextCheckbox.addEventListener('change', () => { config.usePageContext = contextCheckbox.checked; GM_setValue('usePageContext', config.usePageContext); }); settingsBtn.addEventListener('click', () => { const newApiUrl = prompt('API地址(默认:https://api.deepseek.com/v1/chat/completions):', config.apiUrl); if (newApiUrl !== null) { config.apiUrl = newApiUrl; GM_setValue('apiUrl', config.apiUrl); } const newApiKey = prompt('API密钥:', config.apiKey); if (newApiKey !== null) { config.apiKey = newApiKey; GM_setValue('apiKey', config.apiKey); } const newModel = prompt('模型默认(deepseek-chat):', config.model); if (newModel !== null) { config.model = newModel; GM_setValue('model', config.model); } const newTemp = parseFloat(prompt('Temperature (0-2建议0.5-0.8)设置越大幻觉越强', config.temperature)); if (!isNaN(newTemp) && newTemp >= 0 && newTemp <= 2) { config.temperature = newTemp; GM_setValue('temperature', config.temperature); } const newMaxTokens = parseInt(prompt('输出Token限制默认4k最大限制受模型所限V3最大8k R1最大64k:', config.maxTokens)); if (!isNaN(newMaxTokens) && newMaxTokens > 0 && newMaxTokens <= 65535) { config.maxTokens = newMaxTokens; GM_setValue('maxTokens', config.maxTokens); } const newMaxContextTokens = parseInt(prompt('最大上下文限制64k默认32k(越大记忆越好):', config.maxContextTokens)); if (!isNaN(newMaxContextTokens) && newMaxContextTokens > 0 && newMaxContextTokens <= 65535) { config.maxContextTokens = newMaxContextTokens; GM_setValue('maxContextTokens', config.maxContextTokens); } const newPersonalityPrompt = prompt('自定义人格提示词:根据个人需求修改(锐锐永远爱你!)', config.personalityPrompt); if (newPersonalityPrompt !== null) { config.personalityPrompt = newPersonalityPrompt; GM_setValue('personalityPrompt', config.personalityPrompt); } }); clearBtn.addEventListener('click', () => { config.chatHistory = []; GM_setValue('chatHistory', config.chatHistory); chatContent.innerHTML = ''; }); /** * 获取网页主要内容 - 全面增强版 * @returns {Object} 包含url、title和content的对象 */ function getPageContent() { // 收集页面元信息 const metaTags = Array.from(document.querySelectorAll('meta')); const metaInfo = metaTags.map(tag => { const name = tag.getAttribute('name') || tag.getAttribute('property') || ''; const content = tag.getAttribute('content') || ''; return { name, content }; }).filter(meta => meta.content); // 收集图片信息 const images = Array.from(document.querySelectorAll('img')); const imageInfo = images.map(img => { return { src: img.src, alt: img.alt, title: img.title, width: img.width, height: img.height, className: img.className, id: img.id }; }); // 收集链接信息 const links = Array.from(document.querySelectorAll('a')); const linkInfo = links.map(link => { return { href: link.href, text: link.innerText.trim(), title: link.title, className: link.className, id: link.id }; }); // 收集样式信息 const styles = Array.from(document.querySelectorAll('style, link[rel="stylesheet"]')); const styleInfo = styles.map(style => { if (style.tagName === 'STYLE') { return { type: 'inline', content: style.innerText.substring(0, 1000) + '...' }; } else { return { type: 'external', href: style.href }; } }); // 收集所有文本内容(包括隐藏元素) const allText = document.body.innerText .replace(/[\n\r\t]+/g, ' ') .replace(/\s{2,}/g, ' ') .trim(); // 智能摘要 const MAX_LENGTH = 20000; let content = ` [网页元信息] 标题: ${document.title} URL: ${window.location.href} 字符集: ${document.characterSet} 语言: ${document.documentElement.lang || '未指定'} [元标签] ${metaInfo.map(meta => `${meta.name}: ${meta.content}`).join('\n')} [主要内容摘要] ${allText.substring(0, MAX_LENGTH / 2)}${allText.length > MAX_LENGTH / 2 ? '...' : ''} [图片信息 (共${images.length}张)] ${imageInfo.slice(0, 20).map((img, i) => `图片${i + 1}: ${img.alt || img.title || '无描述'} [${img.className || '无类名'}]`).join('\n')} ${images.length > 20 ? `...及其他${images.length - 20}张图片` : ''} [链接信息 (共${links.length}个)] ${linkInfo.slice(0, 20).map((link, i) => `链接${i + 1}: ${link.text || '无文本'} → ${link.href}`).join('\n')} ${links.length > 20 ? `...及其他${links.length - 20}个链接` : ''} [样式信息] ${styleInfo.map(style => style.type === 'inline' ? `内联样式: ${style.content}` : `外部样式表: ${style.href}`).join('\n')} [页面结构] 主要标签: ${Array.from(document.body.children).slice(0, 10).map(el => el.tagName).join(', ')}... `; // 确保内容长度不超过限制 if (content.length > MAX_LENGTH) { content = content.substring(0, MAX_LENGTH) + '...'; } return { url: window.location.href, title: document.title, content, charset: document.characterSet, wordCount: content.split(/\s+/).length }; } // 创建HTML预览窗口 function createHtmlPreview(htmlContent) { // 创建遮罩层 const overlay = document.createElement('div'); overlay.className = 'ds-preview-overlay'; // 创建预览窗口 const previewWindow = document.createElement('div'); previewWindow.className = 'ds-html-preview'; // 创建预览头部 const previewHeader = document.createElement('div'); previewHeader.className = 'ds-preview-header'; previewHeader.innerHTML = `
    HTML预览
    ×
    `; // 创建iframe const iframe = document.createElement('iframe'); iframe.className = 'ds-preview-iframe'; iframe.sandbox = 'allow-same-origin allow-scripts allow-popups allow-forms'; // 组装元素 previewWindow.appendChild(previewHeader); previewWindow.appendChild(iframe); overlay.appendChild(previewWindow); document.body.appendChild(overlay); // 写入HTML内容 const iframeDoc = iframe.contentDocument || iframe.contentWindow.document; iframeDoc.open(); iframeDoc.write(` HTML预览 ${htmlContent} `); iframeDoc.close(); // 关闭按钮事件 previewHeader.querySelector('.ds-preview-close').addEventListener('click', () => { document.body.removeChild(overlay); }); // 点击遮罩层关闭 overlay.addEventListener('click', (e) => { if (e.target === overlay) { document.body.removeChild(overlay); } }); } // 流式响应处理(已修改为使用Markdown渲染) function handleStreamResponse(response, aiMsgDiv) { return new Promise((resolve, reject) => { let aiMessage = ''; const thinkingMsg = document.querySelector('.ds-thinking'); if (thinkingMsg && thinkingMsg.parentNode) { thinkingMsg.parentNode.removeChild(thinkingMsg); } aiMsgDiv.innerHTML = ''; const contentDiv = document.createElement('div'); contentDiv.className = 'ds-message-content'; aiMsgDiv.appendChild(contentDiv); // 添加HTML预览按钮检测 const checkForHtmlPreview = () => { setTimeout(() => { contentDiv.querySelectorAll('pre[data-lang="html"]').forEach(pre => { const code = pre.querySelector('code').innerText; const button = document.createElement('button'); button.className = 'ds-preview-html'; button.innerText = '预览HTML'; button.onclick = () => createHtmlPreview(code); pre.parentNode.insertBefore(button, pre.nextSibling); }); }, 500); }; // 记录最后渲染时间用于性能优化 let lastRenderTime = 0; const renderDelay = 200; // 200ms渲染间隔 const decoder = new TextDecoder(); let buffer = ''; const reader = response.response.getReader(); function readStream() { reader.read().then(({done, value}) => { if (done) { // 确保最终内容被渲染 contentDiv.innerHTML = renderMarkdown(aiMessage); chatContent.scrollTop = chatContent.scrollHeight; // 加载资源并渲染 loadMarkdownResources().then(() => { // 高亮代码块 document.querySelectorAll('.ds-message-content pre code').forEach(block => { hljs.highlightElement(block); }); // 渲染所有数学公式(使用auto-render) renderMathInElement(contentDiv, { delimiters: [ {left: '$$', right: '$$', display: true}, {left: '$', right: '$', display: false} ], throwOnError: false }); // 增强Mermaid图表渲染 if (typeof mermaid !== 'undefined') { document.querySelectorAll('.ds-mermaid:not([data-rendered="true"])').forEach(el => { try { // 检查是否包含有效的Mermaid代码 if (el.textContent.trim().length > 0) { mermaid.init(undefined, el); el.setAttribute('data-rendered', 'true'); } else { el.innerHTML = `
    空图表
    未检测到有效的Mermaid代码
    `; el.setAttribute('data-rendered', 'error'); } } catch (e) { console.error('Mermaid渲染错误:', e); el.innerHTML = `
    图表渲染失败
    错误: ${e.message}
    建议: 检查Mermaid语法或尝试刷新页面
    `; el.setAttribute('data-rendered', 'error'); } }); // 特殊处理:当检测到流程图时自动重试渲染 const mermaidDiagrams = document.querySelectorAll('.ds-mermaid'); if (mermaidDiagrams.length > 0) { setTimeout(() => { mermaidDiagrams.forEach(el => { if (el.getAttribute('data-rendered') !== 'true') { try { mermaid.init(undefined, el); el.setAttribute('data-rendered', 'true'); } catch (e) { console.log('Mermaid重试渲染失败,但已显示错误信息'); } } }); }, 1500); } } // 检查并添加HTML预览按钮 checkForHtmlPreview(); }); if (aiMessage.trim()) { config.chatHistory.push({ role: 'assistant', content: aiMessage }); GM_setValue('chatHistory', config.chatHistory); } resolve(); return; } buffer += decoder.decode(value, {stream: true}); const lines = buffer.split('\n'); buffer = lines.pop() || ''; for (const line of lines) { if (!line.trim() || line === 'data: [DONE]') continue; if (line.startsWith('data: ')) { try { const data = JSON.parse(line.slice(6)); if (data.choices?.[0]?.delta?.content) { const newContent = data.choices[0].delta.content; aiMessage += newContent; // 性能优化:限制渲染频率 const now = Date.now(); if (now - lastRenderTime > renderDelay) { contentDiv.innerHTML = renderMarkdown(aiMessage); chatContent.scrollTop = chatContent.scrollHeight; lastRenderTime = now; } } } catch (e) { console.warn('解析响应数据失败:', e); } } } readStream(); }).catch(error => { reject(error); }); } readStream(); }); } // 计算消息的 token 数量(简单估算) function countTokens(text) { return Math.ceil(text.length / 2); } // 检查并截断上下文 function truncateContext(messages, maxContextTokens) { let totalTokens = 0; for (let i = messages.length - 1; i >= 0; i--) { const messageTokens = countTokens(messages[i].content); if (totalTokens + messageTokens > maxContextTokens) { messages.splice(0, i); break; } totalTokens += messageTokens; } return messages; } // 发送消息函数(已修改为使用Markdown渲染) async function sendMessage(message, retryCount = 0) { if (!message.trim()) return; if (!config.apiKey) { alert('请先设置 API 密钥!'); settingsBtn.click(); return; } if (!navigator.onLine) { const errorMsgDiv = document.createElement('div'); errorMsgDiv.className = 'ds-chat-message ds-error'; errorMsgDiv.innerText = '错误: 网络连接已断开,请检查网络后重试'; chatContent.appendChild(errorMsgDiv); chatContent.scrollTop = chatContent.scrollHeight; return; } const userMsg = { role: 'user', content: message }; config.chatHistory.push(userMsg); GM_setValue('chatHistory', config.chatHistory); const userMsgDiv = document.createElement('div'); userMsgDiv.className = 'ds-chat-message ds-user-message'; userMsgDiv.innerHTML = `
    ${renderMarkdown(message)}
    `; chatContent.appendChild(userMsgDiv); const thinkingMsgDiv = document.createElement('div'); thinkingMsgDiv.className = 'ds-chat-message ds-thinking'; thinkingMsgDiv.innerText = '思考中...'; chatContent.appendChild(thinkingMsgDiv); const aiMsgDiv = document.createElement('div'); aiMsgDiv.className = 'ds-chat-message ds-ai-message'; chatContent.appendChild(aiMsgDiv); chatContent.scrollTop = chatContent.scrollHeight; const requestData = { model: config.model, messages: [ { role: 'system', content: config.personalityPrompt }, ...truncateContext(config.chatHistory, config.maxContextTokens) ], temperature: config.temperature, max_tokens: config.maxTokens, stream: true, }; if (config.usePageContext) { const pageContent = getPageContent(); requestData.messages.splice(1, 0, { role: 'system', content: `[当前网页全景信息] ${pageContent.content} 以下6条为此对话的核心条例 1.基于以上全面网页信息,请清晰准确地回答用户问题。若问题与网页内容无关则忽略网页全景信息。 2.你擅长使用 markdown css 渲染重要信息 3.标记重点信息并且加入颜色渲染,标题使用渐变色渲染 4.如果用户的问题不适合使用图表,请用常规方式回答,无需强制使用图表。 5.请记住,图表的目的是让解释更加直观和易懂。在使用图表时,始终以提高回答的清晰度和理解度为目标 6.请使用markdown渲染但不要使用反引号,禁止使用代码块高亮功能 [渲染能力说明] 你拥有强大的Markdown渲染能力,支持以下特性: 1. 数学公式:行内公式用 \`$...$\`,块级公式用 \`$$...$$\` 2. 代码高亮:使用 \`\`\`language\n...\n\`\`\` 语法 3. Mermaid图表:使用 \`\`\`mermaid\n...\n\`\`\` 语法 4. 任务列表:\`- [ ] 未完成\` 和 \`- [x] 已完成\` 5. 表格:标准Markdown表格语法 6. 增强样式: - 标题自动应用渐变色 - 代码块带语言标识和深色背景 - 表格斑马纹+表头高亮 - 内联代码灰色背景 [CSS渲染能力] 你还可以使用内联CSS来增强渲染效果: 1. 文本样式:红色加粗文字 2. 代码样式:代码示例 3. 复杂样式:使用
    元素和内联CSS创建带背景、边框、阴影的区块 4. 表格样式: 使用内联CSS自定义表格样式 5. 文字特效:使用text-shadow属性创建发光效果 6. 渐变文本:使用background-clip实现渐变文字效果 7. 3D代码效果:为内联代码添加3D效果、悬停动画和阴影 8. 动态标题:为代码块标题添加流光动画效果 9. 注意事项: - 所有样式均为内联CSS实现 - 严格遵循Markdown规范 - 未使用任何三反引号代码块 - 保持可读性和可维护性 [使用指南] 1. 优先使用Markdown结构组织内容 2. 复杂概念用图表(mermaid)辅助说明 3. 技术内容用代码块展示(注明语言) 4. 数学公式优先使用块级渲染 5. 任务类内容用任务列表 6. 避免使用三反引号代码块(用指定语言替代) ` }); } try { return new Promise((resolve, reject) => { let timeoutId = setTimeout(() => { reject(new Error('请求超时')); }, 30000); GM_xmlhttpRequest({ method: 'POST', url: config.apiUrl, headers: { 'Content-Type': 'application/json', 'Authorization': `Bearer ${config.apiKey}`, 'Accept': 'text/event-stream' }, responseType: 'stream', data: JSON.stringify(requestData), onloadstart: (response) => { try { handleStreamResponse(response, aiMsgDiv) .then(resolve) .catch(reject); } catch (error) { reject(error); } }, onerror: (error) => { clearTimeout(timeoutId); chatContent.removeChild(thinkingMsgDiv); reject(new Error('请求失败: ' + error.statusText)); }, ontimeout: () => { clearTimeout(timeoutId); chatContent.removeChild(thinkingMsgDiv); reject(new Error('请求超时')); } }); }); } catch (error) { if (thinkingMsgDiv.parentNode) { chatContent.removeChild(thinkingMsgDiv); } let errorMessage = '发生未知错误'; if (error.message.includes('timeout')) { errorMessage = '请求超时,请检查网络连接'; } else if (error.message.includes('Failed to fetch') || error.message.includes('请求失败')) { errorMessage = '无法连接到服务器,请检查:\n1. 网络连接\n2. API地址是否正确\n3. 是否开启了代理/VPN'; } else if (error.message.includes('401')) { errorMessage = 'API密钥无效或已过期,请重新设置'; } else if (error.message.includes('429')) { errorMessage = '请求过于频繁,请稍后再试'; } else { errorMessage = `错误: ${error.message}`; } const errorMsgDiv = document.createElement('div'); errorMsgDiv.className = 'ds-chat-message ds-error'; errorMsgDiv.innerText = errorMessage; chatContent.appendChild(errorMsgDiv); chatContent.scrollTop = chatContent.scrollHeight; if ((error.message.includes('Failed to fetch') || error.message.includes('请求失败') || error.message.includes('timeout')) && retryCount < 3) { const retryMsgDiv = document.createElement('div'); retryMsgDiv.className = 'ds-chat-message ds-thinking'; retryMsgDiv.innerText = `连接失败,正在第${retryCount + 1}次重试...`; chatContent.appendChild(retryMsgDiv); setTimeout(() => { chatContent.removeChild(retryMsgDiv); return sendMessage(message, retryCount + 1); }, 2000); } } } // 输入框事件 inputBox.addEventListener('keydown', (e) => { if (e.key === 'Enter' && !e.shiftKey) { e.preventDefault(); const message = inputBox.value.trim(); if (message) { sendMessage(message); inputBox.value = ''; } } }); // 注册菜单命令 GM_registerMenuCommand("设置DeepSeek API", () => settingsBtn.click()); GM_registerMenuCommand("清空聊天历史", () => clearBtn.click()); GM_registerMenuCommand("切换网页上下文", () => { contextCheckbox.checked = !contextCheckbox.checked; config.usePageContext = contextCheckbox.checked; GM_setValue('usePageContext', config.usePageContext); }); } })();