// ==UserScript== // @name DeepSeek 增强工具集 // @namespace http://tampermonkey.net/ // @version 2.2 // @description 为DeepSeek添加批量删除和侧边栏折叠功能 // @author Assistant // @match https://chat.deepseek.com/* // @grant GM_addStyle // ==/UserScript== (function() { 'use strict'; // ---------- 配置选择器 ---------- const SELECTORS = { // 开启新对话按钮 newChatBtn: '#root > div > div > div.c3ecdb44 > div.dc04ec1d > div > div._5a8ac7a.a084f19e', // 左侧历史对话列表容器 leftList: '#root > div > div > div.c3ecdb44 > div.dc04ec1d > div', // 单个对话项(用于点击跳转) chatItem: 'a', // 历史对话滚动区域(批量删除需要) scrollArea: '.ds-scroll-area' }; // ---------- 全局变量 ---------- let rootObserver = null; // 批量删除相关变量 let btnBatchDelete = null; let btnCancel = null; let btnReverse = null; let btnConfirm = null; let checkboxes = []; let batchContainer = null; let actionContainer = null; let originalNewChatBtn = null; let batchDeleteObserver = null; // ---------- 添加自定义样式 ---------- GM_addStyle(` /* 折叠按钮样式 */ .ds-collapse-btn { display: flex; align-items: center; justify-content: space-between; width: 100%; padding: 8px 12px; margin: 8px 0; background: transparent; border: none; border-radius: 8px; cursor: pointer; font-size: 14px; font-weight: 500; color: var(--dsw-alias-label-secondary, #666); transition: all 0.2s ease; } .ds-collapse-btn:hover { background-color: var(--dsw-alias-fill-hover, rgba(0,0,0,0.05)); color: var(--dsw-alias-label-primary, #1f1f1f); } .ds-collapse-btn svg { width: 16px; height: 16px; fill: currentColor; transition: transform 0.2s ease; } .ds-collapse-btn.collapsed svg { transform: rotate(-90deg); } .ds-history-collapsed ._3098d02 { display: none; } /* 批量删除按钮样式 */ .btn::after { content: "";} .btn-danger { color: var(--dsw-alias-state-error-primary); overflow: hidden} .btn-danger::before { content: ''; position: absolute; top: 0; left: 0; width: 100%; height: 100%; background-color: rgba(0, 0, 0, 0); transition: background-color .2s; pointer-events: none; } .btn-danger:hover::before { background-color: var(--dsw-alias-interactive-bg-hover-danger); } .ds-button-container { display: flex; flex-direction: column; gap: 12px; margin-bottom: 12px; } .ds-button-row { display: flex; gap: 12px; } `); // 添加历史对话折叠按钮(防重复) function addCollapseButton() { const leftList = document.querySelector(SELECTORS.leftList); if (!leftList) return; if (document.querySelector('.ds-collapse-btn')) return; const collapseBtn = document.createElement('button'); collapseBtn.className = 'ds-collapse-btn collapsed'; collapseBtn.innerHTML = ` 历史对话 `; leftList.insertBefore(collapseBtn, leftList.firstChild); leftList.classList.add('ds-history-collapsed'); collapseBtn.addEventListener('click', () => { const isCollapsed = leftList.classList.contains('ds-history-collapsed'); if (isCollapsed) { leftList.classList.remove('ds-history-collapsed'); collapseBtn.classList.remove('collapsed'); } else { leftList.classList.add('ds-history-collapsed'); collapseBtn.classList.add('collapsed'); } }); } // ---------- 折叠与展开辅助函数(批量删除耦合) ---------- function expandHistoryIfCollapsed() { const leftList = document.querySelector(SELECTORS.leftList); if (!leftList) return; if (leftList.classList.contains('ds-history-collapsed')) { leftList.classList.remove('ds-history-collapsed'); const collapseBtn = document.querySelector('.ds-collapse-btn'); if (collapseBtn && collapseBtn.classList.contains('collapsed')) { collapseBtn.classList.remove('collapsed'); } } } // ---------- 批量删除功能(仅登录后启用)---------- function initBatchDelete() { const userTokenRaw = localStorage.getItem("userToken"); if (!userTokenRaw) return; try { const userToken = JSON.parse(userTokenRaw); if (!userToken || !userToken.value) return; } catch(e) { return; } const scrollArea = document.querySelector(SELECTORS.scrollArea); if (!scrollArea) { setTimeout(initBatchDelete, 500); return; } const btnNewChat = document.querySelector(SELECTORS.newChatBtn); if (!btnNewChat) { setTimeout(initBatchDelete, 500); return; } if (document.querySelector('.ds-button-container[data-batch="true"]')) return; originalNewChatBtn = btnNewChat; batchContainer = document.createElement("div"); batchContainer.className = 'ds-button-container'; batchContainer.setAttribute('data-batch', 'true'); const buttonRow = document.createElement('div'); buttonRow.className = 'ds-button-row'; btnBatchDelete = originalNewChatBtn.cloneNode(true); btnBatchDelete.innerHTML = '批量删除'; btnBatchDelete.classList.add('btn-danger'); btnBatchDelete.style.width = "40%"; const newChatButton = originalNewChatBtn.cloneNode(true); newChatButton.innerHTML = '开启新对话'; newChatButton.style.width = "55%"; newChatButton.addEventListener('click', () => { originalNewChatBtn.click(); }); buttonRow.appendChild(btnBatchDelete); buttonRow.appendChild(newChatButton); batchContainer.appendChild(buttonRow); actionContainer = document.createElement("div"); actionContainer.className = 'ds-button-container'; actionContainer.style.visibility = "hidden"; actionContainer.style.opacity = "0"; actionContainer.style.height = "0"; actionContainer.style.overflow = "hidden"; actionContainer.style.transition = "all 0.3s ease"; const actionRow = document.createElement('div'); actionRow.className = 'ds-button-row'; btnCancel = originalNewChatBtn.cloneNode(true); btnCancel.textContent = "取消"; btnCancel.style.width = "30%"; btnReverse = originalNewChatBtn.cloneNode(true); btnReverse.textContent = "反选"; btnReverse.style.width = "30%"; btnConfirm = originalNewChatBtn.cloneNode(true); btnConfirm.textContent = "删除"; btnConfirm.classList.add('btn-danger'); btnConfirm.style.width = "30%"; actionRow.appendChild(btnCancel); actionRow.appendChild(btnReverse); actionRow.appendChild(btnConfirm); actionContainer.appendChild(actionRow); originalNewChatBtn.before(batchContainer); batchContainer.before(actionContainer); originalNewChatBtn.style.display = 'none'; btnBatchDelete.addEventListener("click", function () { expandHistoryIfCollapsed(); batchContainer.style.visibility = "hidden"; batchContainer.style.opacity = "0"; batchContainer.style.height = "0"; batchContainer.style.overflow = "hidden"; actionContainer.style.visibility = "visible"; actionContainer.style.opacity = "1"; actionContainer.style.height = "auto"; actionContainer.style.overflow = "visible"; addCheckbox(); }); btnCancel.addEventListener("click", function () { batchContainer.style.visibility = "visible"; batchContainer.style.opacity = "1"; batchContainer.style.height = "auto"; batchContainer.style.overflow = "visible"; actionContainer.style.visibility = "hidden"; actionContainer.style.opacity = "0"; actionContainer.style.height = "0"; actionContainer.style.overflow = "hidden"; removeCheckbox(); }); btnReverse.addEventListener("click", function () { for (let checkbox of checkboxes) { checkbox.checked = !checkbox.checked; } }); btnConfirm.addEventListener("click", confirmDelete); const innerScrollArea = document.querySelector(".ds-scroll-area")?.querySelector(".ds-scroll-area"); if (innerScrollArea && innerScrollArea.firstChild) { if (batchDeleteObserver) batchDeleteObserver.disconnect(); batchDeleteObserver = new MutationObserver((mutations) => { for (let mutation of mutations) { try { for (let node of mutation.addedNodes) { if (node.tagName === "A") { node.parentElement.style.display = ""; } } } catch(e) {} } }); batchDeleteObserver.observe(innerScrollArea.firstChild, { childList: true, subtree: true }); } } function addCheckbox() { const scrollArea = document.querySelector(".ds-scroll-area")?.querySelector(".ds-scroll-area"); if (!scrollArea) return; const chats = scrollArea.querySelectorAll("a"); for (let a of chats) { if (a.querySelector('input[type="checkbox"]')) continue; a.style.justifyContent = "unset"; let checkbox = document.createElement("input"); checkbox.type = "checkbox"; checkbox.style.cssText = `height:100%; aspect-ratio: 1;`; checkbox.name = a.href.toString().split("/").pop(); checkbox.refer = a; checkbox.onclick = (e) => { e.stopPropagation(); }; a.prepend(checkbox); checkboxes.push(checkbox); } } function removeCheckbox() { for (let checkbox of checkboxes) { checkbox.remove(); } checkboxes = []; } function removeEmptyDateGroup() { const scrollArea = document.querySelector(".ds-scroll-area")?.querySelector(".ds-scroll-area"); if (!scrollArea || !scrollArea.firstChild) return; let dateGroups = scrollArea.firstChild.childNodes; for (let dateGroup of dateGroups) { let flagNoContent = true; for (let a of dateGroup.querySelectorAll("a")) { if (a.style.display !== "none") { flagNoContent = false; } } if (flagNoContent) { dateGroup.style.display = "none"; } } } function confirmDelete() { let userToken; try { userToken = JSON.parse(localStorage.getItem("userToken")).value; } catch(e) { return; } (async () => { const promises = []; for (let checkbox of checkboxes) { if (!checkbox.checked) continue; let sessionID = checkbox.name; promises.push(fetch("https://chat.deepseek.com/api/v0/chat_session/delete", { "credentials": "include", "headers": { "Accept": "*/*", "authorization": `Bearer ${userToken}`, "content-type": "application/json", }, "body": JSON.stringify({chat_session_id: sessionID}), "method": "POST", }).then(r => { if (r.ok) { if (checkbox.name === location.href.toString().split('/').pop()) { const newChatBtn = document.querySelector(SELECTORS.newChatBtn); if (newChatBtn) newChatBtn.click(); } checkbox.refer.style.display = "none"; } })); } await Promise.allSettled(promises); removeEmptyDateGroup(); })(); removeCheckbox(); batchContainer.style.visibility = "visible"; batchContainer.style.opacity = "1"; batchContainer.style.height = "auto"; batchContainer.style.overflow = "visible"; actionContainer.style.visibility = "hidden"; actionContainer.style.opacity = "0"; actionContainer.style.height = "0"; actionContainer.style.overflow = "hidden"; } // ---------- 观察器生命周期管理 ---------- function disconnectAllObservers() { if (rootObserver) { rootObserver.disconnect(); rootObserver = null; } if (batchDeleteObserver) { batchDeleteObserver.disconnect(); batchDeleteObserver = null; } } function observeForReinit() { if (rootObserver) rootObserver.disconnect(); rootObserver = new MutationObserver(() => { const leftList = document.querySelector(SELECTORS.leftList); if (leftList && !document.querySelector('.ds-collapse-btn')) { addCollapseButton(); } const userTokenRaw = localStorage.getItem("userToken"); if (userTokenRaw) { try { const userToken = JSON.parse(userTokenRaw); if (userToken && userToken.value && !document.querySelector('.ds-button-container[data-batch="true"]')) { initBatchDelete(); } } catch(e) {} } }); rootObserver.observe(document.body, { childList: true, subtree: true }); } window.addEventListener('beforeunload', () => { disconnectAllObservers(); }); // ---------- 启动脚本 ---------- function start() { addCollapseButton(); initBatchDelete(); observeForReinit(); } if (document.readyState === 'loading') { document.addEventListener('DOMContentLoaded', start); } else { start(); } })();