DeepSeek快捷键

为DeepSeek提供快捷键支持(Mac & Windows & Linux)

目前为 2025-04-26 提交的版本,查看 最新版本

  1. // ==UserScript==
  2. // @name DeepSeek ShortCuts
  3. // @name:zh-CN DeepSeek快捷键
  4. // @name:zh-TW DeepSeek快捷鍵
  5. // @description Keyboard Shortcuts For DeepSeek (Mac & Windows & Linux)
  6. // @description:zh-CN 为DeepSeek提供快捷键支持(Mac & Windows & Linux)
  7. // @description:zh-TW 為DeepSeek提供快捷鍵支持(Mac & Windows & Linux)
  8. // @version 1.4.0
  9. // @icon https://raw.githubusercontent.com/MiPoNianYou/UserScripts/refs/heads/main/Icons/DeepSeekShortcutsIcon.svg
  10. // @author 念柚
  11. // @namespace https://github.com/MiPoNianYou/UserScripts
  12. // @supportURL https://github.com/MiPoNianYou/UserScripts/issues
  13. // @license GPL-3.0
  14. // @match https://chat.deepseek.com/*
  15. // @grant GM_addStyle
  16. // ==/UserScript==
  17.  
  18. (function () {
  19. "use strict";
  20.  
  21. const helpPanelStyles = `
  22. .ShortcutsHelpPanel {
  23. --panel-bg-color: rgba(44, 44, 46, 0.85);
  24. --panel-text-color: rgba(255, 255, 255, 0.9);
  25. --panel-secondary-text-color: rgba(235, 235, 245, 0.6);
  26. --panel-border-color: rgba(84, 84, 88, 0.65);
  27. --panel-padding: 24px;
  28. --panel-radius: 12px;
  29. --key-bg-color: rgba(118, 118, 128, 0.24);
  30. --warning-bg-color: rgba(118, 118, 128, 0.24);
  31. --warning-border-color: rgba(84, 84, 88, 0.65);
  32. --warning-text-color: rgb(255, 159, 10);
  33. --font-stack: system-ui, sans-serif;
  34. --closebtn-color: #FF5F57;
  35. --closebtn-hover-color: #E0443E;
  36. --closebtn-symbol-color: #4D0000;
  37.  
  38. position: fixed;
  39. top: 50%;
  40. left: 50%;
  41. transform: translate(-50%, -48%) scale(0.95);
  42. z-index: 9999;
  43. pointer-events: none;
  44. visibility: hidden;
  45. min-width: 280px;
  46. max-width: 450px;
  47. padding: var(--panel-padding);
  48. border: 0.5px solid var(--panel-border-color);
  49. border-radius: var(--panel-radius);
  50. background: var(--panel-bg-color);
  51. box-shadow: 0 12px 28px rgba(0, 0, 0, 0.2), 0 2px 4px rgba(0, 0, 0, 0.1);
  52. backdrop-filter: blur(20px) saturate(180%);
  53. color: var(--panel-text-color);
  54. font-family: var(--font-stack);
  55. font-size: 14px;
  56. font-weight: 500;
  57. line-height: 1.5;
  58. opacity: 0;
  59. transition: opacity 0.3s ease-in-out, transform 0.3s ease-in-out, visibility 0s linear 0.3s;
  60. display: flex;
  61. flex-direction: column;
  62. }
  63. .ShortcutsHelpPanel.visible {
  64. opacity: 1;
  65. transform: translate(-50%, -50%) scale(1);
  66. pointer-events: auto;
  67. visibility: visible;
  68. transition-delay: 0s;
  69. }
  70. .ShortcutsHelpCloseButton {
  71. position: absolute;
  72. top: 14px;
  73. left: 14px;
  74. width: 12px;
  75. height: 12px;
  76. padding: 0;
  77. border: 0.5px solid rgba(0, 0, 0, 0.2);
  78. border-radius: 50%;
  79. background-color: var(--closebtn-color);
  80. cursor: pointer;
  81. display: flex;
  82. align-items: center;
  83. justify-content: center;
  84. transition: background-color 0.15s ease;
  85. appearance: none;
  86. -webkit-appearance: none;
  87. }
  88. .ShortcutsHelpCloseButton::before {
  89. content: '×';
  90. display: block;
  91. color: transparent;
  92. font-size: 11px;
  93. font-weight: bold;
  94. line-height: 12px;
  95. text-align: center;
  96. transition: color 0.15s ease;
  97. }
  98. .ShortcutsHelpCloseButton:hover {
  99. background-color: var(--closebtn-hover-color);
  100. }
  101. .ShortcutsHelpCloseButton:hover::before {
  102. color: var(--closebtn-symbol-color);
  103. }
  104. .ShortcutsHelpCloseButton:active {
  105. background-color: var(--closebtn-hover-color);
  106. filter: brightness(0.9);
  107. }
  108. .ShortcutsHelpTitle {
  109. margin: 0 0 15px 0;
  110. width: 100%;
  111. color: var(--panel-text-color);
  112. font-size: 16px;
  113. font-weight: 600;
  114. text-align: center;
  115. padding-top: 5px;
  116. flex-shrink: 0;
  117. }
  118. .ShortcutsHelpContent {
  119. flex-grow: 1;
  120. overflow-y: auto;
  121. max-height: 65vh;
  122. margin-right: -10px;
  123. padding-right: 10px;
  124. scrollbar-width: thin;
  125. scrollbar-color: rgba(235, 235, 245, 0.3) transparent;
  126. }
  127. .ShortcutsHelpContent::-webkit-scrollbar {
  128. width: 6px;
  129. }
  130. .ShortcutsHelpContent::-webkit-scrollbar-track {
  131. background: transparent;
  132. margin: 5px 0;
  133. }
  134. .ShortcutsHelpContent::-webkit-scrollbar-thumb {
  135. background-color: rgba(235, 235, 245, 0.4);
  136. border-radius: 3px;
  137. }
  138. .ShortcutsHelpContent::-webkit-scrollbar-thumb:hover {
  139. background-color: rgba(235, 235, 245, 0.6);
  140. }
  141. .ShortcutsHelpRow {
  142. display: flex;
  143. justify-content: space-between;
  144. align-items: center;
  145. margin-bottom: 10px;
  146. padding: 5px 0;
  147. }
  148. .ShortcutsHelpContent > .ShortcutsHelpRow:last-child {
  149. margin-bottom: 0;
  150. }
  151. .ShortcutsHelpKey {
  152. min-width: 90px;
  153. padding: 4px 8px;
  154. margin-left: 16px;
  155. background: var(--key-bg-color);
  156. border: 0.5px solid var(--panel-border-color);
  157. border-radius: 5px;
  158. box-shadow: 0 1px 1px rgba(0, 0, 0, 0.1);
  159. color: var(--panel-text-color);
  160. font-family: inherit;
  161. font-size: 13px;
  162. text-align: center;
  163. flex-shrink: 0;
  164. }
  165. .ShortcutsHelpDesc {
  166. flex-grow: 1;
  167. padding-right: 10px;
  168. color: var(--panel-secondary-text-color);
  169. }
  170. .ShortcutsHelpWarning {
  171. margin-top: 18px;
  172. padding: 12px;
  173. background: var(--warning-bg-color);
  174. border: 0.5px solid var(--warning-border-color);
  175. border-radius: 8px;
  176. color: var(--warning-text-color);
  177. font-size: 12px;
  178. line-height: 1.5;
  179. text-align: center;
  180. flex-shrink: 0;
  181. }
  182. `;
  183.  
  184. if (typeof GM_addStyle === "function") {
  185. GM_addStyle(helpPanelStyles);
  186. } else {
  187. const styleElement = document.createElement("style");
  188. styleElement.textContent = helpPanelStyles;
  189. document.head.appendChild(styleElement);
  190. }
  191.  
  192. const createFinder = (config) => () => {
  193. const {
  194. selector,
  195. filterCriteria,
  196. position = "first",
  197. parentSelector,
  198. parentPosition = "last",
  199. childPosition = "first",
  200. } = config;
  201.  
  202. if (parentSelector) {
  203. const parents = Array.from(document.querySelectorAll(parentSelector));
  204. if (parents.length === 0) return null;
  205. const parentIndex = parentPosition === "last" ? parents.length - 1 : 0;
  206. const targetParent = parents[parentIndex];
  207. if (!targetParent) return null;
  208. const children = Array.from(targetParent.querySelectorAll(selector));
  209. if (children.length === 0) return null;
  210. const childIndex = childPosition === "last" ? children.length - 1 : 0;
  211. return children[childIndex] || null;
  212. } else {
  213. const elements = Array.from(document.querySelectorAll(selector));
  214. if (elements.length === 0) return null;
  215. if (filterCriteria) {
  216. return (
  217. elements.find(
  218. (element) =>
  219. element.textContent?.includes(filterCriteria) ||
  220. element.querySelector(filterCriteria)
  221. ) || null
  222. );
  223. } else {
  224. const index = position === "last" ? elements.length - 1 : 0;
  225. return elements[index] || null;
  226. }
  227. }
  228. };
  229.  
  230. const findRegenerate = createFinder({
  231. selector: ".ds-icon-button",
  232. filterCriteria: "#重新生成",
  233. });
  234. const findContinue = createFinder({
  235. selector: ".ds-button",
  236. filterCriteria: "继续生成",
  237. });
  238. const findStop = createFinder({
  239. selector: "._7436101",
  240. position: "first",
  241. });
  242. const findLastCopy = createFinder({
  243. parentSelector: "div._4f9bf79.d7dc56a8",
  244. parentPosition: "last",
  245. selector: "._965abe9 .ds-icon-button",
  246. childPosition: "first",
  247. });
  248. const findLastEdit = createFinder({
  249. parentSelector: "._9663006",
  250. parentPosition: "last",
  251. selector: "._78e0558 .ds-icon-button",
  252. childPosition: "last",
  253. });
  254. const findDeepThink = createFinder({
  255. selector: ".ds-button span",
  256. filterCriteria: "深度思考",
  257. });
  258. const findSearch = createFinder({
  259. selector: ".ds-button span",
  260. filterCriteria: "联网搜索",
  261. });
  262. const findUpload = createFinder({
  263. selector: ".f02f0e25",
  264. position: "first",
  265. });
  266. const findNewChat = createFinder({
  267. selector: "._217e214",
  268. position: "first",
  269. });
  270. const findToggleSidebar = createFinder({
  271. selector: ".ds-icon-button",
  272. filterCriteria: "svg #打开边栏0730, svg #折叠边栏0730",
  273. });
  274. const findChatMenu = createFinder({
  275. parentSelector: "._83421f9.b64fb9ae",
  276. parentPosition: "last",
  277. selector: "._2090548",
  278. childPosition: "first",
  279. });
  280.  
  281. const getModifiers = () => {
  282. const isMac = /Macintosh|Mac OS X/i.test(navigator.userAgent);
  283. return {
  284. Character: isMac ? "Control" : "Alt",
  285. Property: isMac ? "ctrlKey" : "altKey",
  286. };
  287. };
  288.  
  289. const modifierKeys = getModifiers();
  290.  
  291. const shortcutDefs = [
  292. [`${modifierKeys.Character} + R`, "重新生成回答"],
  293. [`${modifierKeys.Character} + C`, "继续生成回答"],
  294. [`${modifierKeys.Character} + Q`, "中断当前生成"],
  295. [`${modifierKeys.Character} + K`, "复制末条回答"],
  296. [`${modifierKeys.Character} + E`, "编辑末次提问"],
  297. [`${modifierKeys.Character} + D`, "深度思考模式"],
  298. [`${modifierKeys.Character} + S`, "联网搜索模式"],
  299. [`${modifierKeys.Character} + U`, "上传本地文件"],
  300. [`${modifierKeys.Character} + N`, "新建对话窗口"],
  301. [`${modifierKeys.Character} + T`, "切换开关边栏"],
  302. [`${modifierKeys.Character} + I`, "当前对话菜单"],
  303. [`${modifierKeys.Character} + H`, "快捷按键帮助"],
  304. ];
  305.  
  306. let helpPanelElement = null;
  307.  
  308. const createHelpPanel = () => {
  309. const panelElement = document.createElement("div");
  310. panelElement.classList.add("ShortcutsHelpPanel");
  311.  
  312. const closeButton = document.createElement("button");
  313. closeButton.classList.add("ShortcutsHelpCloseButton");
  314. closeButton.addEventListener("click", (e) => {
  315. e.stopPropagation();
  316. closeHelpPanel();
  317. });
  318.  
  319. const titleElement = document.createElement("h3");
  320. titleElement.textContent = "快捷按键指北";
  321. titleElement.classList.add("ShortcutsHelpTitle");
  322.  
  323. const contentDiv = document.createElement("div");
  324. contentDiv.classList.add("ShortcutsHelpContent");
  325.  
  326. panelElement.append(closeButton, titleElement);
  327.  
  328. shortcutDefs.forEach(([keyShortcut, description]) => {
  329. const rowElement = document.createElement("div");
  330. rowElement.classList.add("ShortcutsHelpRow");
  331.  
  332. const keyElement = document.createElement("span");
  333. keyElement.textContent = keyShortcut;
  334. keyElement.classList.add("ShortcutsHelpKey");
  335.  
  336. const descriptionElement = document.createElement("span");
  337. descriptionElement.textContent = description;
  338. descriptionElement.classList.add("ShortcutsHelpDesc");
  339.  
  340. rowElement.append(descriptionElement, keyElement);
  341. contentDiv.append(rowElement);
  342. });
  343.  
  344. panelElement.append(contentDiv);
  345.  
  346. const warningElement = document.createElement("div");
  347. warningElement.textContent = "⚠️ 脚本依UA自动适配快捷键 篡改UA或致功能异常";
  348. warningElement.classList.add("ShortcutsHelpWarning");
  349. panelElement.append(warningElement);
  350.  
  351. document.body.append(panelElement);
  352. return panelElement;
  353. };
  354.  
  355. const handleOutsideClick = (mouseEvent) => {
  356. if (
  357. helpPanelElement &&
  358. helpPanelElement.classList.contains("visible") &&
  359. !helpPanelElement.contains(mouseEvent.target) &&
  360. !mouseEvent.target.classList.contains("ShortcutsHelpCloseButton")
  361. ) {
  362. closeHelpPanel();
  363. }
  364. };
  365.  
  366. const toggleHelpPanel = (panelElement) => {
  367. if (!panelElement) {
  368. return;
  369. }
  370. const isVisible = panelElement.classList.contains("visible");
  371. if (isVisible) {
  372. panelElement.classList.remove("visible");
  373. window.removeEventListener("click", handleOutsideClick, true);
  374. } else {
  375. panelElement.classList.add("visible");
  376. setTimeout(() => {
  377. window.addEventListener("click", handleOutsideClick, true);
  378. }, 0);
  379. }
  380. };
  381.  
  382. const initHelpPanel = () => {
  383. if (!helpPanelElement) {
  384. helpPanelElement = createHelpPanel();
  385. }
  386. };
  387.  
  388. const closeHelpPanel = () => {
  389. if (helpPanelElement && helpPanelElement.classList.contains("visible")) {
  390. helpPanelElement.classList.remove("visible");
  391. window.removeEventListener("click", handleOutsideClick, true);
  392. }
  393. };
  394.  
  395. const safeClick = (finderFunction) => {
  396. const element = finderFunction();
  397. if (element) {
  398. element.click();
  399. }
  400. };
  401.  
  402. const debounce = (func, wait) => {
  403. let timeout;
  404. return function executeDebounced(...args) {
  405. const later = () => {
  406. clearTimeout(timeout);
  407. func.apply(this, args);
  408. };
  409. clearTimeout(timeout);
  410. timeout = setTimeout(later, wait);
  411. };
  412. };
  413.  
  414. const toggleHelpPanelAction = () => {
  415. initHelpPanel();
  416. if (helpPanelElement) {
  417. toggleHelpPanel(helpPanelElement);
  418. }
  419. };
  420.  
  421. const debouncedToggleHelpPanel = debounce(toggleHelpPanelAction, 150);
  422.  
  423. const keyActionMap = {
  424. r: () => safeClick(findRegenerate),
  425. c: () => safeClick(findContinue),
  426. q: () => safeClick(findStop),
  427. k: () => safeClick(findLastCopy),
  428. e: () => safeClick(findLastEdit),
  429. d: () => safeClick(findDeepThink),
  430. s: () => safeClick(findSearch),
  431. u: () => safeClick(findUpload),
  432. n: () => safeClick(findNewChat),
  433. t: () => safeClick(findToggleSidebar),
  434. i: () => safeClick(findChatMenu),
  435. h: () => {
  436. debouncedToggleHelpPanel();
  437. return true;
  438. },
  439. };
  440.  
  441. const createKeyHandler = () => {
  442. const isModifierKeyPressed = (keyboardEvent) =>
  443. keyboardEvent[modifierKeys.Property];
  444.  
  445. return (keyboardEvent) => {
  446. if (keyboardEvent.key === "Escape") {
  447. if (
  448. helpPanelElement &&
  449. helpPanelElement.classList.contains("visible")
  450. ) {
  451. closeHelpPanel();
  452. keyboardEvent.preventDefault();
  453. keyboardEvent.stopPropagation();
  454. }
  455. return;
  456. }
  457.  
  458. if (!isModifierKeyPressed(keyboardEvent)) {
  459. return;
  460. }
  461.  
  462. const pressedKey = keyboardEvent.key.toLowerCase();
  463. const actionFunction = keyActionMap[pressedKey];
  464.  
  465. if (actionFunction) {
  466. const actionResult = actionFunction(keyboardEvent);
  467. if (actionResult !== false) {
  468. keyboardEvent.preventDefault();
  469. keyboardEvent.stopPropagation();
  470. }
  471. }
  472. };
  473. };
  474.  
  475. const mainKeyHandler = createKeyHandler();
  476. window.addEventListener("keydown", mainKeyHandler, true);
  477. })();