// ==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 ``;
});
// 处理列表
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}${tag}>`;
});
// 处理代码块
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);
});
}
})();