// ==UserScript==
// @name DeepSeek ShortCuts
// @name:zh-CN DeepSeek快捷键
// @name:zh-TW DeepSeek快捷鍵
// @description Keyboard Shortcuts For DeepSeek (Mac & Windows & Linux)
// @description:zh-CN 为DeepSeek提供快捷键支持(Mac & Windows & Linux)
// @description:zh-TW 為DeepSeek提供快捷鍵支持(Mac & Windows & Linux)
// @version 1.4.0
// @icon https://raw.githubusercontent.com/MiPoNianYou/UserScripts/refs/heads/main/Icons/DeepSeekShortcutsIcon.svg
// @author 念柚
// @namespace https://github.com/MiPoNianYou/UserScripts
// @supportURL https://github.com/MiPoNianYou/UserScripts/issues
// @license GPL-3.0
// @match https://chat.deepseek.com/*
// @grant GM_addStyle
// ==/UserScript==
(function () {
"use strict";
const helpPanelStyles = `
.ShortcutsHelpPanel {
--panel-bg-color: rgba(44, 44, 46, 0.85);
--panel-text-color: rgba(255, 255, 255, 0.9);
--panel-secondary-text-color: rgba(235, 235, 245, 0.6);
--panel-border-color: rgba(84, 84, 88, 0.65);
--panel-padding: 24px;
--panel-radius: 12px;
--key-bg-color: rgba(118, 118, 128, 0.24);
--warning-bg-color: rgba(118, 118, 128, 0.24);
--warning-border-color: rgba(84, 84, 88, 0.65);
--warning-text-color: rgb(255, 159, 10);
--font-stack: system-ui, sans-serif;
--closebtn-color: #FF5F57;
--closebtn-hover-color: #E0443E;
--closebtn-symbol-color: #4D0000;
position: fixed;
top: 50%;
left: 50%;
transform: translate(-50%, -48%) scale(0.95);
z-index: 9999;
pointer-events: none;
visibility: hidden;
min-width: 280px;
max-width: 450px;
padding: var(--panel-padding);
border: 0.5px solid var(--panel-border-color);
border-radius: var(--panel-radius);
background: var(--panel-bg-color);
box-shadow: 0 12px 28px rgba(0, 0, 0, 0.2), 0 2px 4px rgba(0, 0, 0, 0.1);
backdrop-filter: blur(20px) saturate(180%);
color: var(--panel-text-color);
font-family: var(--font-stack);
font-size: 14px;
font-weight: 500;
line-height: 1.5;
opacity: 0;
transition: opacity 0.3s ease-in-out, transform 0.3s ease-in-out, visibility 0s linear 0.3s;
display: flex;
flex-direction: column;
}
.ShortcutsHelpPanel.visible {
opacity: 1;
transform: translate(-50%, -50%) scale(1);
pointer-events: auto;
visibility: visible;
transition-delay: 0s;
}
.ShortcutsHelpCloseButton {
position: absolute;
top: 14px;
left: 14px;
width: 12px;
height: 12px;
padding: 0;
border: 0.5px solid rgba(0, 0, 0, 0.2);
border-radius: 50%;
background-color: var(--closebtn-color);
cursor: pointer;
display: flex;
align-items: center;
justify-content: center;
transition: background-color 0.15s ease;
appearance: none;
-webkit-appearance: none;
}
.ShortcutsHelpCloseButton::before {
content: '×';
display: block;
color: transparent;
font-size: 11px;
font-weight: bold;
line-height: 12px;
text-align: center;
transition: color 0.15s ease;
}
.ShortcutsHelpCloseButton:hover {
background-color: var(--closebtn-hover-color);
}
.ShortcutsHelpCloseButton:hover::before {
color: var(--closebtn-symbol-color);
}
.ShortcutsHelpCloseButton:active {
background-color: var(--closebtn-hover-color);
filter: brightness(0.9);
}
.ShortcutsHelpTitle {
margin: 0 0 15px 0;
width: 100%;
color: var(--panel-text-color);
font-size: 16px;
font-weight: 600;
text-align: center;
padding-top: 5px;
flex-shrink: 0;
}
.ShortcutsHelpContent {
flex-grow: 1;
overflow-y: auto;
max-height: 65vh;
margin-right: -10px;
padding-right: 10px;
scrollbar-width: thin;
scrollbar-color: rgba(235, 235, 245, 0.3) transparent;
}
.ShortcutsHelpContent::-webkit-scrollbar {
width: 6px;
}
.ShortcutsHelpContent::-webkit-scrollbar-track {
background: transparent;
margin: 5px 0;
}
.ShortcutsHelpContent::-webkit-scrollbar-thumb {
background-color: rgba(235, 235, 245, 0.4);
border-radius: 3px;
}
.ShortcutsHelpContent::-webkit-scrollbar-thumb:hover {
background-color: rgba(235, 235, 245, 0.6);
}
.ShortcutsHelpRow {
display: flex;
justify-content: space-between;
align-items: center;
margin-bottom: 10px;
padding: 5px 0;
}
.ShortcutsHelpContent > .ShortcutsHelpRow:last-child {
margin-bottom: 0;
}
.ShortcutsHelpKey {
min-width: 90px;
padding: 4px 8px;
margin-left: 16px;
background: var(--key-bg-color);
border: 0.5px solid var(--panel-border-color);
border-radius: 5px;
box-shadow: 0 1px 1px rgba(0, 0, 0, 0.1);
color: var(--panel-text-color);
font-family: inherit;
font-size: 13px;
text-align: center;
flex-shrink: 0;
}
.ShortcutsHelpDesc {
flex-grow: 1;
padding-right: 10px;
color: var(--panel-secondary-text-color);
}
.ShortcutsHelpWarning {
margin-top: 18px;
padding: 12px;
background: var(--warning-bg-color);
border: 0.5px solid var(--warning-border-color);
border-radius: 8px;
color: var(--warning-text-color);
font-size: 12px;
line-height: 1.5;
text-align: center;
flex-shrink: 0;
}
`;
if (typeof GM_addStyle === "function") {
GM_addStyle(helpPanelStyles);
} else {
const styleElement = document.createElement("style");
styleElement.textContent = helpPanelStyles;
document.head.appendChild(styleElement);
}
const createFinder = (config) => () => {
const {
selector,
filterCriteria,
position = "first",
parentSelector,
parentPosition = "last",
childPosition = "first",
} = config;
if (parentSelector) {
const parents = Array.from(document.querySelectorAll(parentSelector));
if (parents.length === 0) return null;
const parentIndex = parentPosition === "last" ? parents.length - 1 : 0;
const targetParent = parents[parentIndex];
if (!targetParent) return null;
const children = Array.from(targetParent.querySelectorAll(selector));
if (children.length === 0) return null;
const childIndex = childPosition === "last" ? children.length - 1 : 0;
return children[childIndex] || null;
} else {
const elements = Array.from(document.querySelectorAll(selector));
if (elements.length === 0) return null;
if (filterCriteria) {
return (
elements.find(
(element) =>
element.textContent?.includes(filterCriteria) ||
element.querySelector(filterCriteria)
) || null
);
} else {
const index = position === "last" ? elements.length - 1 : 0;
return elements[index] || null;
}
}
};
const findRegenerate = createFinder({
selector: ".ds-icon-button",
filterCriteria: "#重新生成",
});
const findContinue = createFinder({
selector: ".ds-button",
filterCriteria: "继续生成",
});
const findStop = createFinder({
selector: "._7436101",
position: "first",
});
const findLastCopy = createFinder({
parentSelector: "div._4f9bf79.d7dc56a8",
parentPosition: "last",
selector: "._965abe9 .ds-icon-button",
childPosition: "first",
});
const findLastEdit = createFinder({
parentSelector: "._9663006",
parentPosition: "last",
selector: "._78e0558 .ds-icon-button",
childPosition: "last",
});
const findDeepThink = createFinder({
selector: ".ds-button span",
filterCriteria: "深度思考",
});
const findSearch = createFinder({
selector: ".ds-button span",
filterCriteria: "联网搜索",
});
const findUpload = createFinder({
selector: ".f02f0e25",
position: "first",
});
const findNewChat = createFinder({
selector: "._217e214",
position: "first",
});
const findToggleSidebar = createFinder({
selector: ".ds-icon-button",
filterCriteria: "svg #打开边栏0730, svg #折叠边栏0730",
});
const findChatMenu = createFinder({
parentSelector: "._83421f9.b64fb9ae",
parentPosition: "last",
selector: "._2090548",
childPosition: "first",
});
const getModifiers = () => {
const isMac = /Macintosh|Mac OS X/i.test(navigator.userAgent);
return {
Character: isMac ? "Control" : "Alt",
Property: isMac ? "ctrlKey" : "altKey",
};
};
const modifierKeys = getModifiers();
const shortcutDefs = [
[`${modifierKeys.Character} + R`, "重新生成回答"],
[`${modifierKeys.Character} + C`, "继续生成回答"],
[`${modifierKeys.Character} + Q`, "中断当前生成"],
[`${modifierKeys.Character} + K`, "复制末条回答"],
[`${modifierKeys.Character} + E`, "编辑末次提问"],
[`${modifierKeys.Character} + D`, "深度思考模式"],
[`${modifierKeys.Character} + S`, "联网搜索模式"],
[`${modifierKeys.Character} + U`, "上传本地文件"],
[`${modifierKeys.Character} + N`, "新建对话窗口"],
[`${modifierKeys.Character} + T`, "切换开关边栏"],
[`${modifierKeys.Character} + I`, "当前对话菜单"],
[`${modifierKeys.Character} + H`, "快捷按键帮助"],
];
let helpPanelElement = null;
const createHelpPanel = () => {
const panelElement = document.createElement("div");
panelElement.classList.add("ShortcutsHelpPanel");
const closeButton = document.createElement("button");
closeButton.classList.add("ShortcutsHelpCloseButton");
closeButton.addEventListener("click", (e) => {
e.stopPropagation();
closeHelpPanel();
});
const titleElement = document.createElement("h3");
titleElement.textContent = "快捷按键指北";
titleElement.classList.add("ShortcutsHelpTitle");
const contentDiv = document.createElement("div");
contentDiv.classList.add("ShortcutsHelpContent");
panelElement.append(closeButton, titleElement);
shortcutDefs.forEach(([keyShortcut, description]) => {
const rowElement = document.createElement("div");
rowElement.classList.add("ShortcutsHelpRow");
const keyElement = document.createElement("span");
keyElement.textContent = keyShortcut;
keyElement.classList.add("ShortcutsHelpKey");
const descriptionElement = document.createElement("span");
descriptionElement.textContent = description;
descriptionElement.classList.add("ShortcutsHelpDesc");
rowElement.append(descriptionElement, keyElement);
contentDiv.append(rowElement);
});
panelElement.append(contentDiv);
const warningElement = document.createElement("div");
warningElement.textContent = "⚠️ 脚本依UA自动适配快捷键 篡改UA或致功能异常";
warningElement.classList.add("ShortcutsHelpWarning");
panelElement.append(warningElement);
document.body.append(panelElement);
return panelElement;
};
const handleOutsideClick = (mouseEvent) => {
if (
helpPanelElement &&
helpPanelElement.classList.contains("visible") &&
!helpPanelElement.contains(mouseEvent.target) &&
!mouseEvent.target.classList.contains("ShortcutsHelpCloseButton")
) {
closeHelpPanel();
}
};
const toggleHelpPanel = (panelElement) => {
if (!panelElement) {
return;
}
const isVisible = panelElement.classList.contains("visible");
if (isVisible) {
panelElement.classList.remove("visible");
window.removeEventListener("click", handleOutsideClick, true);
} else {
panelElement.classList.add("visible");
setTimeout(() => {
window.addEventListener("click", handleOutsideClick, true);
}, 0);
}
};
const initHelpPanel = () => {
if (!helpPanelElement) {
helpPanelElement = createHelpPanel();
}
};
const closeHelpPanel = () => {
if (helpPanelElement && helpPanelElement.classList.contains("visible")) {
helpPanelElement.classList.remove("visible");
window.removeEventListener("click", handleOutsideClick, true);
}
};
const safeClick = (finderFunction) => {
const element = finderFunction();
if (element) {
element.click();
}
};
const debounce = (func, wait) => {
let timeout;
return function executeDebounced(...args) {
const later = () => {
clearTimeout(timeout);
func.apply(this, args);
};
clearTimeout(timeout);
timeout = setTimeout(later, wait);
};
};
const toggleHelpPanelAction = () => {
initHelpPanel();
if (helpPanelElement) {
toggleHelpPanel(helpPanelElement);
}
};
const debouncedToggleHelpPanel = debounce(toggleHelpPanelAction, 150);
const keyActionMap = {
r: () => safeClick(findRegenerate),
c: () => safeClick(findContinue),
q: () => safeClick(findStop),
k: () => safeClick(findLastCopy),
e: () => safeClick(findLastEdit),
d: () => safeClick(findDeepThink),
s: () => safeClick(findSearch),
u: () => safeClick(findUpload),
n: () => safeClick(findNewChat),
t: () => safeClick(findToggleSidebar),
i: () => safeClick(findChatMenu),
h: () => {
debouncedToggleHelpPanel();
return true;
},
};
const createKeyHandler = () => {
const isModifierKeyPressed = (keyboardEvent) =>
keyboardEvent[modifierKeys.Property];
return (keyboardEvent) => {
if (keyboardEvent.key === "Escape") {
if (
helpPanelElement &&
helpPanelElement.classList.contains("visible")
) {
closeHelpPanel();
keyboardEvent.preventDefault();
keyboardEvent.stopPropagation();
}
return;
}
if (!isModifierKeyPressed(keyboardEvent)) {
return;
}
const pressedKey = keyboardEvent.key.toLowerCase();
const actionFunction = keyActionMap[pressedKey];
if (actionFunction) {
const actionResult = actionFunction(keyboardEvent);
if (actionResult !== false) {
keyboardEvent.preventDefault();
keyboardEvent.stopPropagation();
}
}
};
};
const mainKeyHandler = createKeyHandler();
window.addEventListener("keydown", mainKeyHandler, true);
})();