// ==UserScript== // @name Deepseek Chat 增强版Deepseek API对话助手,支持历史记录、参数设置和网页内容检索!帮助你在任意网页学习! // @namespace shy // @version 1.7 // @description 增强版Deepseek API对话助手,支持历史记录、参数设置和网页内容检索! // @author shy // @match *://*/* // @grant GM_getValue // @grant GM_setValue // @grant GM_xmlhttpRequest // @grant GM_registerMenuCommand // @grant GM_addStyle // @grant GM_getResourceText // @connect api.deepseek.com // @license MIT // @downloadURL none // ==/UserScript== (function() { 'use strict'; // 添加CSS样式 GM_addStyle(` .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.2s, box-shadow 0.2s; 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-window { position: fixed; bottom: 20px; right: 20px; width: 350px; max-width: 40vw; 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; transition: opacity 0.3s, transform 0.3s, width 0.3s; opacity: 0; transform: translateY(20px); z-index: 2147483646; backdrop-filter: blur(5px); } .ds-chat-window.active { display: flex; opacity: 1; transform: translateY(0); } .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; } .ds-chat-close { cursor: pointer; font-size: 18px; } .ds-chat-content { flex: 1; padding: 15px; overflow-y: auto; background-color: rgba(255, 255, 255, 0.3); border-bottom: 1px solid #ddd; } .ds-chat-message { margin-bottom: 10px; padding: 8px 12px; border-radius: 8px; line-height: 1.4; word-wrap: break-word; } .ds-user-message { background-color: rgba(227, 242, 253, 0.7); margin-left: auto; text-align: right; } .ds-ai-message { background-color: rgba(241, 241, 241, 0.7); margin-right: 20%; opacity: 0; animation: fadeIn 0.5s ease-in-out forwards; } .ds-chat-input-area { padding: 10px; display: flex; flex-direction: column; backdrop-filter: blur(5px); /* 添加高斯模糊效果 */ background-color: rgba(255, 255, 255, 0.3); /* 添加半透明背景以增强模糊效果 */ border-top: 1px solid rgba(221, 221, 221, 0.5); /* 添加顶部边框以分隔输入区域 */ } .ds-chat-input { width: 100%; padding: 10px; border: 1px solid #ddd; border-radius: 8px; margin-bottom: 8px; outline: none; transition: border-color 0.3s; } .ds-chat-input:focus { border-color: #007bff; } .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: #666; 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; } @keyframes fadeIn { from { opacity: 0; transform: translateY(10px); } to { opacity: 1; transform: translateY(0); } } `); // 初始化配置 let config = { apiKey: GM_getValue('apiKey', ''), model: GM_getValue('model', 'deepseek-chat'), temperature: GM_getValue('temperature', 0.7), maxTokens: GM_getValue('maxTokens', 1024), chatHistory: GM_getValue('chatHistory', []), usePageContext: GM_getValue('usePageContext', true) }; // 创建UI元素 const icon = document.createElement('div'); icon.className = 'ds-chat-icon'; icon.innerText = 'AI'; document.body.appendChild(icon); 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 closeBtn = document.createElement('div'); closeBtn.className = 'ds-chat-close'; closeBtn.innerText = '×'; chatHeader.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 contextSummary = document.createElement('div'); contextSummary.className = 'ds-context-summary'; contextSummary.innerText = '当前网页: ' + document.title; inputArea.appendChild(contextSummary); const inputBox = document.createElement('textarea'); inputBox.className = 'ds-chat-input'; inputBox.placeholder = '输入你的问题...'; inputBox.rows = 3; 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); // 显示历史消息 function displayHistory() { chatContent.innerHTML = ''; config.chatHistory.forEach(msg => { const msgDiv = document.createElement('div'); msgDiv.className = `ds-chat-message ds-${msg.role}-message`; msgDiv.innerText = msg.content; chatContent.appendChild(msgDiv); }); chatContent.scrollTop = chatContent.scrollHeight; } // 初始化显示历史消息 displayHistory(); // 图标点击事件 icon.addEventListener('click', () => { chatWindow.classList.toggle('active'); icon.style.display = 'none'; }); // 关闭按钮事件 closeBtn.addEventListener('click', () => { chatWindow.classList.remove('active'); icon.style.display = 'flex'; }); // 上下文开关事件 contextCheckbox.addEventListener('change', () => { config.usePageContext = contextCheckbox.checked; GM_setValue('usePageContext', config.usePageContext); }); // 设置按钮事件 settingsBtn.addEventListener('click', () => { const newApiKey = prompt('DeepSeek API密钥:', config.apiKey); if (newApiKey !== null) { config.apiKey = newApiKey; GM_setValue('apiKey', config.apiKey); } const newModel = prompt('模型 (deepseek-chat, deepseek-coder等):', config.model); if (newModel !== null) { config.model = newModel; GM_setValue('model', config.model); } const newTemp = parseFloat(prompt('Temperature (0-2):', config.temperature)); if (!isNaN(newTemp) && newTemp >= 0 && newTemp <= 2) { config.temperature = newTemp; GM_setValue('temperature', config.temperature); } const newMaxTokens = parseInt(prompt('最大令牌数:', config.maxTokens)); if (!isNaN(newMaxTokens) && newMaxTokens > 0) { config.maxTokens = newMaxTokens; GM_setValue('maxTokens', config.maxTokens); } }); // 清空历史按钮事件 clearBtn.addEventListener('click', () => { config.chatHistory = []; GM_setValue('chatHistory', config.chatHistory); chatContent.innerHTML = ''; }); // 获取网页主要内容 function getPageContent() { const mainContent = document.querySelector('main, article, .main, .content, #content') || document.body; const clone = mainContent.cloneNode(true); const elementsToRemove = clone.querySelectorAll('script, style, noscript, iframe, nav, footer, header, aside'); elementsToRemove.forEach(el => el.remove()); let text = clone.textContent .replace(/\s+/g, ' ') .trim() .substring(0, 5000); return { url: window.location.href, title: document.title, content: text }; } // 调整对话框宽度 function adjustChatWindowWidth(text) { const chatWindow = document.querySelector('.ds-chat-window'); const maxWidth = window.innerWidth * 0.4; const minWidth = 350; const textLength = text.length; let newWidth = minWidth + (textLength * 5); newWidth = Math.min(newWidth, maxWidth); chatWindow.style.width = `${newWidth}px`; } // 逐行显示AI回答 function typeMessage(message, container) { const lines = message.split('\n'); let index = 0; const interval = setInterval(() => { if (index < lines.length) { const line = document.createElement('div'); line.innerText = lines[index]; container.appendChild(line); chatContent.scrollTop = chatContent.scrollHeight; index++; } else { clearInterval(interval); } }, 200); } // 发送消息函数 function sendMessage(message) { if (!message.trim()) return; if (!config.apiKey) { alert('请先设置API密钥!'); settingsBtn.click(); return; } let finalMessage = message; if (config.usePageContext) { const pageContent = getPageContent(); finalMessage = `[当前网页信息] 标题: ${pageContent.title} URL: ${pageContent.url} 内容摘要: ${pageContent.content} 基于以上网页内容,请回答以下问题: ${message}`; } 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.innerText = message; chatContent.appendChild(userMsgDiv); const thinkingMsgDiv = document.createElement('div'); thinkingMsgDiv.className = 'ds-chat-message ds-thinking'; thinkingMsgDiv.innerText = '思考中...'; chatContent.appendChild(thinkingMsgDiv); chatContent.scrollTop = chatContent.scrollHeight; const requestData = { model: config.model, messages: [...config.chatHistory.map(msg => ({ role: msg.role, content: msg.role === 'user' && config.usePageContext ? finalMessage : msg.content }))], temperature: config.temperature, max_tokens: config.maxTokens }; GM_xmlhttpRequest({ method: 'POST', url: 'https://api.deepseek.com/v1/chat/completions', headers: { 'Content-Type': 'application/json', 'Authorization': `Bearer ${config.apiKey}` }, data: JSON.stringify(requestData), onload: function(response) { try { const data = JSON.parse(response.responseText); if (data.choices && data.choices[0]) { const aiMessage = data.choices[0].message.content; chatContent.removeChild(thinkingMsgDiv); const aiMsg = { role: 'assistant', content: aiMessage }; config.chatHistory.push(aiMsg); GM_setValue('chatHistory', config.chatHistory); const aiMsgDiv = document.createElement('div'); aiMsgDiv.className = 'ds-chat-message ds-ai-message'; typeMessage(aiMessage, aiMsgDiv); chatContent.appendChild(aiMsgDiv); adjustChatWindowWidth(aiMessage); // 调整对话框宽度 } else { throw new Error('无效的API响应'); } } catch (e) { showError(`错误: ${e.message}`); } chatContent.scrollTop = chatContent.scrollHeight; }, onerror: function(error) { showError(`请求失败: ${error.statusText || '网络错误'}`); } }); } // 显示错误消息 function showError(message) { const errorMsgDiv = document.createElement('div'); errorMsgDiv.className = 'ds-chat-message ds-error'; errorMsgDiv.innerText = message; chatContent.appendChild(errorMsgDiv); chatContent.scrollTop = chatContent.scrollHeight; } // 输入框事件 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); }); })();