// ==UserScript==
// @name DeepSeek防撤回助手
// @namespace ds+@Byzod.user.js
// @version 0.14
// @description 自动缓存并恢复被撤回的AI回答内容
// @author Deepseek & Byozd
// @license MIT
// @match https://chat.deepseek.com/*
// @grant GM_addStyle
// @downloadURL https://update.greasyfork.icu/scripts/533125/DeepSeek%E9%98%B2%E6%92%A4%E5%9B%9E%E5%8A%A9%E6%89%8B.user.js
// @updateURL https://update.greasyfork.icu/scripts/533125/DeepSeek%E9%98%B2%E6%92%A4%E5%9B%9E%E5%8A%A9%E6%89%8B.meta.js
// ==/UserScript==
GM_addStyle(`
#ai-rescue-button {
position: fixed !important;
top: 20px !important;
right: 20px !important;
z-index: 99999 !important;
transition: all 0.3s ease;
box-shadow: 0 2px 10px rgba(0,0,0,0.2);
}
#ai-rescue-button.ds-button--alert {
box-shadow: 0 0 15px rgba(0,255,0,0.7) !important;
animation: pulse 1s infinite;
}
@keyframes pulse {
0% { opacity: 0.95; }
50% { opacity: 1; }
100% { opacity: 0.95; }
}
/* 新增弹出层样式 */
#ai-rescue-popup {
position: fixed;
top: 50%;
left: 50%;
transform: translate(-30%, -50%);
background: #fff;
border-radius: 8px;
box-shadow: 0 0 20px rgba(0,0,0,0.3);
z-index: 100000;
min-width: 800px;
max-width: 80%;
padding: 20px;
}
#ai-rescue-popup h3 {
margin: 0 0 15px 0;
color: #2c3e50;
font-size: 18px;
}
#ai-rescue-popup textarea {
width: 100%;
height: 300px;
padding: 5px;
border: 1px solid #ddd;
border-radius: 4px;
resize: vertical;
font-family: monospace;
}
#ai-rescue-popup button {
margin-top: 15px;
padding: 8px 15px;
background: #3498db;
color: white;
border: none;
border-radius: 4px;
cursor: pointer;
}
/* 新增渲染模式样式 */
.content-viewer {
display: flex;
gap: 15px;
min-height: 300px;
max-height: 80vh;
}
.render-pane, .source-pane {
flex: 1;
border: 1px solid #ddd;
border-radius: 4px;
padding: 10px;
overflow: auto;
}
.render-pane {
background: #f9f9f9;
white-space: pre-wrap;
}
.render-pane img {
max-width: 100%;
}
.source-pane {
font-family: monospace;
display: none;
}
.source-pane.active {
display: block;
}
.view-switcher {
display: flex;
justify-content: space-between;
align-items: center;
margin-bottom: 15px;
}
.toggle-source {
background: none;
border: 1px solid #3498db;
color: #3498db;
padding: 5px 10px;
border-radius: 4px;
cursor: pointer;
}
`);
(function() {
'use strict';
const config = {
CONTAINER_SELECTOR: 'div:has(>div>div.ds-markdown--block)', // 对话列表容器
TARGET_PATTERN: 'div:not(:has(+ div)) > div.ds-markdown--block', // 最新内容框选择器
TARGET_THOUGHT_PATTERN: 'div:not(:has(+ div)) > div:has(+div.ds-markdown--block)', // 最新内容框选择器
BUTTON_CONTAINER: 'div:has(>#chat-input)+div', // 原按钮选择器
CHECK_INTERVAL: 2000, // 容器检查间隔
MIN_LENGTH: 20
};
let state = {
currentTarget: null,
currentThought: null,
containerObserver: null,
contentObserver: null,
lastContent: '',
lastThought: '',
pendingContent: null,
pendingThought: null,
controlButton: null
};
function createControlButton() {
const btn = document.createElement('button');
btn.id = 'ai-rescue-button'; // 添加唯一ID
btn.className = 'ds-button ds-button--m';
btn.textContent = '缓存';
// 移除原有的marginLeft样式
btn.style.cssText = `
min-width: 80px;
cursor: pointer;
font-weight: bold;
`;
// 点击事件保持原有逻辑
btn.addEventListener('click', handleButtonClick);
// 直接添加到body避免被覆盖
document.body.appendChild(btn);
return btn;
}
// 在showCachePopup函数中进行修改
function showCachePopup() {
const existingPopup = document.getElementById('ai-rescue-popup');
if (existingPopup) existingPopup.remove();
const popup = document.createElement('div');
popup.id = 'ai-rescue-popup';
popup.innerHTML = `
已缓存内容 (${state.lastThought.length + state.lastContent.length}字符)
`;
// 填充内容
const renderPane = popup.querySelector('.render-pane');
const sourcePane = popup.querySelector('.source-pane');
renderPane.classList.add(...Array.from(findLatestTarget()?.parentElement.classList));
renderPane.innerHTML = state.lastThought + state.lastContent; // 渲染HTML
sourcePane.value = state.lastThought + state.lastContent; // 显示源码
// 添加切换功能
const toggleBtn = popup.querySelector('.toggle-source');
toggleBtn.addEventListener('click', () => {
const isShowingSource = sourcePane.classList.toggle('active');
renderPane.style.display = isShowingSource ? 'none' : 'block';
toggleBtn.textContent = isShowingSource ? '查看渲染' : '查看源码';
// 自动滚动保持位置
if (isShowingSource) {
sourcePane.scrollTop = renderPane.scrollTop;
} else {
renderPane.scrollTop = sourcePane.scrollTop;
}
});
// 保持原有点击外部关闭功能
popup.addEventListener('click', (e) => {
if (e.target === popup) popup.remove();
});
document.body.appendChild(popup);
}
function initControlButton() {
const parent = document.querySelector(config.BUTTON_CONTAINER);
if (!parent || state.controlButton) return;
state.controlButton = createControlButton();
parent.insertAdjacentElement('beforeend', state.controlButton);
}
function updateButtonState(isAlert = false) {
if (!state.controlButton) return;
state.controlButton.classList.toggle('ds-button--alert', isAlert);
state.controlButton.textContent = isAlert ? '不许撤回' : '缓存';
}
// 获取最新内容框
function findLatestTarget() {
return document.querySelector(config.TARGET_PATTERN);
}
// 获取最新内容框
function findLatestThoughtTarget() {
return document.querySelector(config.TARGET_THOUGHT_PATTERN);
}
// 初始化容器观察
function initContainerObserver() {
const container = document.querySelector(config.CONTAINER_SELECTOR) || document.body;
state.containerObserver = new MutationObserver(() => {
checkTargetUpdate();
});
state.containerObserver.observe(container, {
childList: true,
subtree: true,
attributes: false
});
}
// 检查是否需要更新目标
function checkTargetUpdate() {
const newTarget = findLatestTarget();
const newThoughtTarget = findLatestThoughtTarget();
if (!newTarget || newTarget === state.currentTarget) return;
// 切换观察目标
if (state.contentObserver) {
state.contentObserver.disconnect();
}
state.currentTarget = newTarget;
state.lastContent = newTarget.outerHTML;
state.currentThought = newThoughtTarget;
state.lastThought = newThoughtTarget.outerHTML;
state.contentObserver = new MutationObserver(handleContentMutations);
state.contentObserver.observe(newTarget, {
childList: true,
subtree: true,
characterData: true
});
console.log('切换到新内容框:', newTarget);
}
// 处理内容变化
function handleContentMutations(mutations) {
const newContent = state.currentTarget.innerHTML;
const newThoughtContent = state.currentThought.innerHTML;
// 内容增长时更新缓存
if (newContent.length > state.lastContent.length) {
state.lastContent = newContent;
state.lastThought = newThoughtContent;
updateButtonState();
}
// 检测撤回
else if (newContent.length < state.lastContent.length - 10) {
state.pendingContent = state.lastContent;
state.pendingThought = state.lastThought;
updateButtonState(true);
}
}
// 控制按钮相关功能(保持原有实现,增加目标检查)
function handleButtonClick() {
if (!state.currentTarget) {
checkTargetUpdate();
return;
}
if (state.pendingContent) {
restoreContent();
} else {
showCachePopup();
}
}
function restoreContent() {
state.currentTarget.insertAdjacentHTML("beforebegin", state.pendingThought);
state.lastThought = state.pendingThought;
state.pendingThought = null;
state.currentTarget.innerHTML = state.pendingContent;
state.lastContent = state.pendingContent;
state.pendingContent = null;
updateButtonState();
}
// 主初始化
function mainInit() {
// 确保按钮始终存在
if (!state.controlButton || !document.body.contains(state.controlButton)) {
state.controlButton = createControlButton();
}
initContainerObserver();
setInterval(checkTargetUpdate, config.CHECK_INTERVAL);
}
setTimeout(mainInit, 500);
})();