您需要先安装一个扩展,例如 篡改猴、Greasemonkey 或 暴力猴,之后才能安装此脚本。
您需要先安装一个扩展,例如 篡改猴 或 暴力猴,之后才能安装此脚本。
您需要先安装一个扩展,例如 篡改猴 或 暴力猴,之后才能安装此脚本。
您需要先安装一个扩展,例如 篡改猴 或 Userscripts ,之后才能安装此脚本。
您需要先安装一款用户脚本管理器扩展,例如 Tampermonkey,才能安装此脚本。
您需要先安装用户脚本管理器扩展后才能安装此脚本。
为DeepSeek提供快捷键支持 | 支持自定义快捷键
// ==UserScript== // @name DeepSeek Shortcuts // @name:zh-CN DeepSeek 快捷键 // @name:zh-TW DeepSeek 快捷鍵 // @description Keyboard Shortcuts For DeepSeek | Support Custom Shortcut Keys // @description:zh-CN 为DeepSeek提供快捷键支持 | 支持自定义快捷键 // @description:zh-TW 為DeepSeek提供快捷鍵支援 | 支援自定義快捷鍵 // @version 1.6.0 // @icon https://raw.githubusercontent.com/MiPoNianYou/UserScripts/main/Icons/DeepSeek-Shortcuts-Icon.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 // @grant GM_setValue // @grant GM_getValue // ==/UserScript== (function () { "use strict"; const Config = { SCRIPT_SETTINGS: { FONT_STACK: "-apple-system, BlinkMacSystemFont, system-ui, sans-serif", ANIMATION_DURATION_MS: 350, ANIMATION_EASING_POP_OUT: "cubic-bezier(0.34, 1.56, 0.64, 1)", ANIMATION_EASING_STANDARD_INTERACTIVE: "cubic-bezier(0, 0, 0.58, 1)", BREATHING_ANIMATION_DURATION: "2.2s", DEBOUNCE_DELAY_MS: 150, }, STORAGE_KEYS: { CUSTOM_SHORTCUTS_PREFIX: "dsk_custom_shortcuts_", }, ELEMENT_SELECTORS: { REGENERATE_BUTTON: { parentSelector: "div._4f9bf79.d7dc56a8", parentPosition: "last", selector: ".ds-icon-button", filterText: "#重新生成", }, CONTINUE_BUTTON: { selector: ".ds-button", filterText: "继续生成" }, STOP_GENERATING_BUTTON: { selector: "._7436101", position: "first" }, LAST_COPY_BUTTON: { parentSelector: "div._4f9bf79.d7dc56a8", parentPosition: "last", selector: "._965abe9 .ds-icon-button", childPosition: "first", }, LAST_EDIT_BUTTON: { parentSelector: "._9663006", parentPosition: "last", selector: "._78e0558 .ds-icon-button", childPosition: "last", }, DEEP_THINK_MODE_BUTTON: { selector: ".ds-button span", filterText: "深度思考", }, SEARCH_MODE_BUTTON: { selector: ".ds-button span", filterText: "联网搜索", }, UPLOAD_FILE_BUTTON: { selector: ".f02f0e25", position: "first" }, NEW_CHAT_BUTTON: { selector: "._217e214", position: "first" }, TOGGLE_SIDEBAR_BUTTON: { selector: ".ds-icon-button", filterText: "svg #打开边栏0730, svg #折叠边栏0730", }, CURRENT_CHAT_MENU_BUTTON: { parentSelector: "._83421f9.b64fb9ae", parentPosition: "last", selector: "._2090548", childPosition: "first", }, }, ELEMENT_IDS: { HELP_PANEL: "dsk-help-panel", HELP_PANEL_ANIMATE_IN: "dsk-help-panel-animate-in", HELP_PANEL_ANIMATE_OUT: "dsk-help-panel-animate-out", }, CSS_CLASSES: { HELP_PANEL_VISIBLE: "dsk-help-panel--visible", HELP_PANEL_CLOSE_BUTTON: "dsk-help-panel-close-button", HELP_PANEL_TITLE: "dsk-help-panel-title", HELP_PANEL_CONTENT: "dsk-help-panel-content", HELP_PANEL_ROW: "dsk-help-panel-row", HELP_PANEL_KEY: "dsk-help-panel-key", HELP_PANEL_KEY_DISPLAY: "dsk-help-panel-key--display", HELP_PANEL_KEY_SETTING: "dsk-help-panel-key--setting", HELP_PANEL_KEY_CONFIGURABLE_HIGHLIGHT: "dsk-help-panel-key--configurable-highlight", HELP_PANEL_KEY_LISTENING: "dsk-help-panel-key--listening", HELP_PANEL_KEY_INVALID_SHAKE: "dsk-key-invalid-shake", HELP_PANEL_DESCRIPTION: "dsk-help-panel-description", HELP_PANEL_WARNING: "dsk-help-panel-warning", }, UI_STRINGS: { HELP_PANEL_TITLE: "快捷按键指北", HELP_PANEL_WARNING_TEXT: "⚠️ 脚本依UA自动适配快捷键 篡改UA或致功能异常", CUSTOMIZE_SHORTCUTS_LABEL: "自定义快捷键", SETTINGS_BUTTON_TEXT: "设置自定按键", FINISH_CUSTOMIZING_BUTTON_TEXT: "完成自定设置", PRESS_NEW_SHORTCUT_TEXT: "请按下快捷键", KEY_CONFLICT_TEXT_PREFIX: "键 「", KEY_CONFLICT_TEXT_SUFFIX: "」 已被使用", INVALID_MODIFIER_TEXT_PREFIX: "请按 ", INVALID_MODIFIER_TEXT_SUFFIX: " + 字母/数字", }, DEFAULT_SHORTCUT_CONFIG: { MODIFIERS: (() => { const isMac = /Macintosh|Mac OS X/i.test(navigator.userAgent); return { CHARACTER_DISPLAY: isMac ? "⌃ Control" : "Alt", EVENT_PROPERTY: isMac ? "ctrlKey" : "altKey", }; })(), SHORTCUTS: [ { id: "regenerate", key: "R", description: "重新生成回答", selectorConfig: "REGENERATE_BUTTON", }, { id: "continueGenerating", key: "C", description: "继续生成回答", selectorConfig: "CONTINUE_BUTTON", }, { id: "stopGenerating", key: "Q", description: "中断当前生成", selectorConfig: "STOP_GENERATING_BUTTON", }, { id: "copyLastResponse", key: "K", description: "复制末条回答", selectorConfig: "LAST_COPY_BUTTON", }, { id: "editLastQuery", key: "E", description: "编辑末次提问", selectorConfig: "LAST_EDIT_BUTTON", }, { id: "deepThinkMode", key: "D", description: "深度思考模式", selectorConfig: "DEEP_THINK_MODE_BUTTON", }, { id: "searchMode", key: "S", description: "联网搜索模式", selectorConfig: "SEARCH_MODE_BUTTON", }, { id: "uploadFile", key: "U", description: "上传本地文件", selectorConfig: "UPLOAD_FILE_BUTTON", }, { id: "newChat", key: "N", description: "新建对话窗口", selectorConfig: "NEW_CHAT_BUTTON", }, { id: "toggleSidebar", key: "T", description: "切换开关边栏", selectorConfig: "TOGGLE_SIDEBAR_BUTTON", }, { id: "currentChatMenu", key: "I", description: "当前对话菜单", selectorConfig: "CURRENT_CHAT_MENU_BUTTON", }, { id: "toggleHelpPanel", key: "H", description: "快捷按键帮助", actionIdentifier: "toggleHelpPanel", isSpecialAction: true, }, { id: "settingsEntry", key: null, description: "placeholder_settings_label", isSettingsEntry: true, actionIdentifier: "toggleCustomizationMode", nonConfigurable: true, }, ], }, }; Config.DEFAULT_SHORTCUT_CONFIG.SHORTCUTS.find( (s) => s.id === "settingsEntry" ).description = Config.UI_STRINGS.CUSTOMIZE_SHORTCUTS_LABEL; const State = { isCustomizingShortcuts: false, activeCustomizationTarget: null, currentShortcutConfig: JSON.parse( JSON.stringify(Config.DEFAULT_SHORTCUT_CONFIG) ), initialize() { this.loadCustomShortcuts(); }, loadCustomShortcuts() { this.currentShortcutConfig.SHORTCUTS.forEach((shortcut) => { if (shortcut.nonConfigurable || shortcut.isSettingsEntry) return; try { const savedKey = GM_getValue( `${Config.STORAGE_KEYS.CUSTOM_SHORTCUTS_PREFIX}${shortcut.id}`, shortcut.key ); if ( savedKey && typeof savedKey === "string" && savedKey.match(/^[a-zA-Z0-9]$/i) ) { shortcut.key = savedKey.toUpperCase(); } } catch (e) {} }); }, saveCustomShortcut(shortcutId, newKey) { const shortcutToUpdate = this.currentShortcutConfig.SHORTCUTS.find( (s) => s.id === shortcutId ); if (shortcutToUpdate) { shortcutToUpdate.key = newKey.toUpperCase(); try { GM_setValue( `${Config.STORAGE_KEYS.CUSTOM_SHORTCUTS_PREFIX}${shortcutId}`, shortcutToUpdate.key ); } catch (e) {} EventManager.reinitializeGlobalKeyListener(); } }, getActiveModifierProperty() { return this.currentShortcutConfig.MODIFIERS.EVENT_PROPERTY; }, getActiveModifierCharDisplay() { return this.currentShortcutConfig.MODIFIERS.CHARACTER_DISPLAY; }, }; const UserInterface = { injectStyles() { const styles = ` :root { --ctp-frappe-rosewater: rgb(242, 213, 207); --ctp-frappe-flamingo: rgb(238, 190, 190); --ctp-frappe-pink: rgb(244, 184, 228); --ctp-frappe-mauve: rgb(202, 158, 230); --ctp-frappe-red: rgb(231, 130, 132); --ctp-frappe-maroon: rgb(234, 153, 156); --ctp-frappe-peach: rgb(239, 159, 118); --ctp-frappe-yellow: rgb(229, 200, 144); --ctp-frappe-green: rgb(166, 209, 137); --ctp-frappe-teal: rgb(129, 200, 190); --ctp-frappe-sky: rgb(153, 209, 219); --ctp-frappe-sapphire: rgb(133, 193, 220); --ctp-frappe-blue: rgb(140, 170, 238); --ctp-frappe-lavender: rgb(186, 187, 241); --ctp-frappe-text: rgb(198, 208, 245); --ctp-frappe-subtext1: rgb(181, 191, 226); --ctp-frappe-subtext0: rgb(165, 173, 206); --ctp-frappe-overlay2: rgb(148, 156, 187); --ctp-frappe-overlay1: rgb(131, 139, 167); --ctp-frappe-overlay0: rgb(115, 121, 148); --ctp-frappe-surface2: rgb(98, 104, 128); --ctp-frappe-surface1: rgb(81, 87, 109); --ctp-frappe-surface0: rgb(65, 69, 89); --ctp-frappe-base: rgb(48, 52, 70); --ctp-frappe-mantle: rgb(41, 44, 60); --ctp-frappe-crust: rgb(35, 38, 52); --ctp-frappe-crust-rgb: 35, 38, 52; --dsk-panel-bg: rgb(from var(--ctp-frappe-mantle) r g b / 0.85); --dsk-panel-border: rgb(from var(--ctp-frappe-surface0) r g b / 0.5); --dsk-panel-shadow: 0 1px 3px rgba(var(--ctp-frappe-crust-rgb), 0.12), 0 6px 16px rgba(var(--ctp-frappe-crust-rgb), 0.10), 0 12px 28px rgba(var(--ctp-frappe-crust-rgb), 0.08); --dsk-text-primary: var(--ctp-frappe-text); --dsk-text-secondary: var(--ctp-frappe-subtext0); --dsk-key-bg: var(--ctp-frappe-surface0); --dsk-key-border: var(--ctp-frappe-surface1); --dsk-key-setting-text: var(--ctp-frappe-blue); --dsk-key-setting-hover-bg: var(--ctp-frappe-surface1); --dsk-key-breathing-highlight-color: var(--ctp-frappe-mauve); --dsk-key-listening-border: var(--ctp-frappe-green); --dsk-key-listening-bg: color-mix(in srgb, var(--dsk-key-bg) 85%, var(--ctp-frappe-green) 15%); --dsk-key-invalid-shake-color: var(--ctp-frappe-red); --dsk-warning-bg: rgb(from var(--ctp-frappe-surface0) r g b / 0.5); --dsk-warning-border: var(--ctp-frappe-surface1); --dsk-warning-text: var(--ctp-frappe-yellow); --dsk-scrollbar-thumb: var(--ctp-frappe-overlay0); --dsk-scrollbar-thumb-hover: var(--ctp-frappe-overlay1); --dsk-close-button-bg: var(--ctp-frappe-red); --dsk-close-button-hover-bg: color-mix(in srgb, var(--ctp-frappe-red) 80%, var(--ctp-frappe-crust) 20%); --dsk-close-button-symbol: rgba(var(--ctp-frappe-crust-rgb), 0.7); } @keyframes dsk-opacity-breathing-effect { 0%, 100% { opacity: 0; } 50% { opacity: 0.25; } } @keyframes dsk-border-breathing-effect { 0%, 100% { border-color: var(--dsk-key-border); } 50% { border-color: var(--dsk-key-breathing-highlight-color); } } @keyframes dsk-invalid-shake-effect { 0%, 100% { transform: translateX(0); } 10%, 30%, 50%, 70%, 90% { transform: translateX(-3px); } 20%, 40%, 60%, 80% { transform: translateX(3px); } } @keyframes ${Config.ELEMENT_IDS.HELP_PANEL_ANIMATE_IN} { 0% { transform: translate(-50%, -50%) scale(0.88); opacity: 0; } 100% { transform: translate(-50%, -50%) scale(1); opacity: 1; } } @keyframes ${Config.ELEMENT_IDS.HELP_PANEL_ANIMATE_OUT} { 0% { transform: translate(-50%, -50%) scale(1); opacity: 1; } 100% { transform: translate(-50%, -50%) scale(0.9); opacity: 0; } } #${Config.ELEMENT_IDS.HELP_PANEL} { position: fixed; top: 50%; left: 50%; transform: translate(-50%, -50%) scale(0.88); opacity: 0; visibility: hidden; z-index: 2147483647; min-width: 300px; max-width: 480px; padding: 24px; border: 1px solid var(--dsk-panel-border); border-radius: 16px; background-color: var(--dsk-panel-bg); color: var(--dsk-text-primary); font-family: ${Config.SCRIPT_SETTINGS.FONT_STACK}; font-size: 14px; font-weight: 500; line-height: 1.5; box-shadow: var(--dsk-panel-shadow); backdrop-filter: blur(20px) saturate(180%); -webkit-backdrop-filter: blur(20px) saturate(180%); display: flex; flex-direction: column; pointer-events: none; } #${Config.ELEMENT_IDS.HELP_PANEL}.${Config.CSS_CLASSES.HELP_PANEL_VISIBLE} { pointer-events: auto; } .${Config.CSS_CLASSES.HELP_PANEL_CLOSE_BUTTON} { position: absolute; top: 14px; left: 14px; width: 12px; height: 12px; padding: 0; border: none; border-radius: 50%; background-color: var(--dsk-close-button-bg); cursor: pointer; display: flex; align-items: center; justify-content: center; transition: background-color 0.15s ${Config.SCRIPT_SETTINGS.ANIMATION_EASING_STANDARD_INTERACTIVE}, transform 0.15s ${Config.SCRIPT_SETTINGS.ANIMATION_EASING_STANDARD_INTERACTIVE}; appearance: none; -webkit-appearance: none; outline: none; } .${Config.CSS_CLASSES.HELP_PANEL_CLOSE_BUTTON}::before { content: '✕'; display: block; color: transparent; font-size: 10px; font-weight: bold; line-height: 12px; text-align: center; transition: color 0.1s ${Config.SCRIPT_SETTINGS.ANIMATION_EASING_STANDARD_INTERACTIVE}; } .${Config.CSS_CLASSES.HELP_PANEL_CLOSE_BUTTON}:hover { background-color: var(--dsk-close-button-hover-bg); } .${Config.CSS_CLASSES.HELP_PANEL_CLOSE_BUTTON}:hover::before { color: var(--dsk-close-button-symbol); } .${Config.CSS_CLASSES.HELP_PANEL_CLOSE_BUTTON}:active { filter: brightness(0.85); transform: scale(0.9); } .${Config.CSS_CLASSES.HELP_PANEL_TITLE} { margin: 0 0 18px 0; padding-top: 8px; color: var(--dsk-text-primary); font-size: 17px; font-weight: 600; text-align: center; flex-shrink: 0; } .${Config.CSS_CLASSES.HELP_PANEL_CONTENT} { flex-grow: 1; overflow-y: auto; max-height: 60vh; margin-right: -12px; padding-right: 12px; scrollbar-width: thin; scrollbar-color: var(--dsk-scrollbar-thumb) transparent; } .${Config.CSS_CLASSES.HELP_PANEL_CONTENT}::-webkit-scrollbar { width: 6px; } .${Config.CSS_CLASSES.HELP_PANEL_CONTENT}::-webkit-scrollbar-track { background: transparent; margin: 4px 0; } .${Config.CSS_CLASSES.HELP_PANEL_CONTENT}::-webkit-scrollbar-thumb { background-color: var(--dsk-scrollbar-thumb); border-radius: 3px; transition: background-color 0.15s ${Config.SCRIPT_SETTINGS.ANIMATION_EASING_STANDARD_INTERACTIVE}; } .${Config.CSS_CLASSES.HELP_PANEL_CONTENT}::-webkit-scrollbar-thumb:hover { background-color: var(--dsk-scrollbar-thumb-hover); } .${Config.CSS_CLASSES.HELP_PANEL_ROW} { display: flex; justify-content: space-between; align-items: center; margin-bottom: 12px; padding: 6px 2px; } .${Config.CSS_CLASSES.HELP_PANEL_CONTENT} > .${Config.CSS_CLASSES.HELP_PANEL_ROW}:last-child { margin-bottom: 0; } .${Config.CSS_CLASSES.HELP_PANEL_KEY} { min-width: 95px; padding: 5px 10px; margin-left: 18px; background-color: var(--dsk-key-bg); border: 1px solid var(--dsk-key-border); border-radius: 6px; box-shadow: 0 1px 1px rgba(0,0,0,0.08), inset 0 1px 1px rgba(255,255,255,0.03); color: var(--dsk-text-primary); font-family: inherit; font-size: 13px; font-weight: 500; text-align: center; flex-shrink: 0; transition: background-color 0.15s ${Config.SCRIPT_SETTINGS.ANIMATION_EASING_STANDARD_INTERACTIVE}, border-color 0.15s ${Config.SCRIPT_SETTINGS.ANIMATION_EASING_STANDARD_INTERACTIVE}, color 0.15s ${Config.SCRIPT_SETTINGS.ANIMATION_EASING_STANDARD_INTERACTIVE}; cursor: default; position: relative; } .${Config.CSS_CLASSES.HELP_PANEL_KEY}.${Config.CSS_CLASSES.HELP_PANEL_KEY_DISPLAY} { } .${Config.CSS_CLASSES.HELP_PANEL_KEY}.${Config.CSS_CLASSES.HELP_PANEL_KEY_SETTING} { color: var(--dsk-key-setting-text); cursor: pointer; } .${Config.CSS_CLASSES.HELP_PANEL_KEY}.${Config.CSS_CLASSES.HELP_PANEL_KEY_SETTING}:hover { background-color: var(--dsk-key-setting-hover-bg); border-color: var(--ctp-frappe-overlay0); } .${Config.CSS_CLASSES.HELP_PANEL_KEY}.${Config.CSS_CLASSES.HELP_PANEL_KEY_CONFIGURABLE_HIGHLIGHT} { animation: dsk-border-breathing-effect ${Config.SCRIPT_SETTINGS.BREATHING_ANIMATION_DURATION} infinite ease-in-out; cursor: pointer; } .${Config.CSS_CLASSES.HELP_PANEL_KEY}.${Config.CSS_CLASSES.HELP_PANEL_KEY_CONFIGURABLE_HIGHLIGHT}::after { content: ''; position: absolute; top: 0; left: 0; right: 0; bottom: 0; border-radius: inherit; background-color: var(--dsk-key-breathing-highlight-color); opacity: 0; z-index: 0; pointer-events: none; animation: dsk-opacity-breathing-effect ${Config.SCRIPT_SETTINGS.BREATHING_ANIMATION_DURATION} infinite ease-in-out; } .${Config.CSS_CLASSES.HELP_PANEL_KEY}.${Config.CSS_CLASSES.HELP_PANEL_KEY_LISTENING} { border-color: var(--dsk-key-listening-border) !important; background-color: var(--dsk-key-listening-bg) !important; color: var(--ctp-frappe-green) !important; animation: none !important; cursor: default !important; } .${Config.CSS_CLASSES.HELP_PANEL_KEY}.${Config.CSS_CLASSES.HELP_PANEL_KEY_LISTENING}::after { animation: none !important; opacity: 0 !important; } .${Config.CSS_CLASSES.HELP_PANEL_KEY}.${Config.CSS_CLASSES.HELP_PANEL_KEY_INVALID_SHAKE} { animation: dsk-invalid-shake-effect 0.5s ease; border-color: var(--dsk-key-invalid-shake-color) !important; color: var(--dsk-key-invalid-shake-color) !important; } .${Config.CSS_CLASSES.HELP_PANEL_DESCRIPTION} { flex-grow: 1; padding-right: 10px; color: var(--dsk-text-secondary); font-size: 13.5px; } .${Config.CSS_CLASSES.HELP_PANEL_WARNING} { margin-top: 20px; padding: 12px 16px; background-color: var(--dsk-warning-bg); border: 1px solid var(--dsk-warning-border); border-radius: 10px; color: var(--dsk-warning-text); font-size: 12.5px; font-weight: 500; line-height: 1.45; text-align: center; flex-shrink: 0; } `; try { GM_addStyle(styles); } catch (e) { const styleElement = document.createElement("style"); styleElement.textContent = styles; (document.head || document.documentElement).appendChild(styleElement); } }, }; const ShortcutManager = { getElementByConfig(selectorKeyName) { const config = Config.ELEMENT_SELECTORS[selectorKeyName]; if (!config || !config.selector) return null; const { selector, filterText, position = "first", parentSelector, parentPosition = "last", childPosition = "first", } = config; let baseElements = []; 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; baseElements = Array.from(targetParent.querySelectorAll(selector)); } else { baseElements = Array.from(document.querySelectorAll(selector)); } if (baseElements.length === 0) return null; let targetElements = baseElements; if (filterText) { const filters = filterText.split(",").map((f) => f.trim()); targetElements = baseElements.filter((element) => filters.some((ft) => { if (ft.startsWith("#")) { return ( !!element.querySelector(ft) || element.id === ft.substring(1) ); } return ( element.textContent?.includes(ft) || (ft.startsWith("svg #") && element.querySelector(ft.replace("svg ", ""))) ); }) ); if (targetElements.length === 0) return null; } const index = position === "last" ? targetElements.length - 1 : 0; return targetElements[index] || null; }, triggerElementClick(selectorKeyName) { const element = this.getElementByConfig(selectorKeyName); if (element && typeof element.click === "function") { element.click(); return true; } return false; }, formatShortcutDisplay(shortcutKey) { if (!shortcutKey) return "---"; return `${State.getActiveModifierCharDisplay()} + ${shortcutKey.toUpperCase()}`; }, }; const HelpPanelManager = { helpPanelElement: null, shortcutDisplaySpansMap: new Map(), panelCloseTimer: null, createPanel() { if ( this.helpPanelElement && document.body.contains(this.helpPanelElement) ) { this.updatePanelContentDynamic(); return this.helpPanelElement; } this.shortcutDisplaySpansMap.clear(); const panel = document.createElement("div"); panel.id = Config.ELEMENT_IDS.HELP_PANEL; const closeButton = this.createCloseButton(); const titleElement = this.createTitle(); const contentContainer = this.createContentContainer(); const warningElement = this.createWarning(); panel.appendChild(closeButton); panel.appendChild(titleElement); panel.appendChild(contentContainer); panel.appendChild(warningElement); this.helpPanelElement = panel; document.body.appendChild(panel); return panel; }, createCloseButton() { const button = document.createElement("button"); button.className = Config.CSS_CLASSES.HELP_PANEL_CLOSE_BUTTON; button.setAttribute("aria-label", "Close help panel"); button.addEventListener("click", (event) => { event.stopPropagation(); if (State.isCustomizingShortcuts) { const settingsBtn = this.helpPanelElement?.querySelector( `.${Config.CSS_CLASSES.HELP_PANEL_KEY_SETTING}` ); if (settingsBtn) this.toggleCustomizationModeUI(settingsBtn); } this.closePanel(); }); return button; }, createTitle() { const title = document.createElement("h3"); title.className = Config.CSS_CLASSES.HELP_PANEL_TITLE; title.textContent = Config.UI_STRINGS.HELP_PANEL_TITLE; return title; }, createContentContainer() { const container = document.createElement("div"); container.className = Config.CSS_CLASSES.HELP_PANEL_CONTENT; State.currentShortcutConfig.SHORTCUTS.forEach((shortcut) => { const row = document.createElement("div"); row.className = Config.CSS_CLASSES.HELP_PANEL_ROW; const descriptionSpan = document.createElement("span"); descriptionSpan.className = Config.CSS_CLASSES.HELP_PANEL_DESCRIPTION; descriptionSpan.textContent = shortcut.description; const keySpan = document.createElement("span"); keySpan.className = Config.CSS_CLASSES.HELP_PANEL_KEY; if (shortcut.isSettingsEntry) { keySpan.textContent = State.isCustomizingShortcuts ? Config.UI_STRINGS.FINISH_CUSTOMIZING_BUTTON_TEXT : Config.UI_STRINGS.SETTINGS_BUTTON_TEXT; keySpan.classList.add(Config.CSS_CLASSES.HELP_PANEL_KEY_SETTING); keySpan.addEventListener("click", () => this.toggleCustomizationModeUI(keySpan) ); } else { keySpan.textContent = ShortcutManager.formatShortcutDisplay( shortcut.key ); keySpan.classList.add(Config.CSS_CLASSES.HELP_PANEL_KEY_DISPLAY); this.shortcutDisplaySpansMap.set(shortcut.id, keySpan); if (!shortcut.nonConfigurable) { keySpan.addEventListener("click", () => { if ( State.isCustomizingShortcuts && (!State.activeCustomizationTarget || State.activeCustomizationTarget.shortcutId !== shortcut.id) ) { this.setListeningStateUI(shortcut.id, keySpan, true); } else if ( State.isCustomizingShortcuts && State.activeCustomizationTarget && State.activeCustomizationTarget.shortcutId === shortcut.id ) { this.setListeningStateUI(shortcut.id, keySpan, false); } }); } this.updateKeySpanAppearance(keySpan, shortcut); } row.appendChild(descriptionSpan); row.appendChild(keySpan); container.appendChild(row); }); return container; }, createWarning() { const warning = document.createElement("div"); warning.className = Config.CSS_CLASSES.HELP_PANEL_WARNING; warning.textContent = Config.UI_STRINGS.HELP_PANEL_WARNING_TEXT; return warning; }, updateKeySpanAppearance(keySpan, shortcut) { keySpan.classList.remove( Config.CSS_CLASSES.HELP_PANEL_KEY_CONFIGURABLE_HIGHLIGHT, Config.CSS_CLASSES.HELP_PANEL_KEY_LISTENING ); if ( State.isCustomizingShortcuts && !shortcut.isSettingsEntry && !shortcut.nonConfigurable ) { if ( State.activeCustomizationTarget && State.activeCustomizationTarget.shortcutId === shortcut.id ) { keySpan.textContent = Config.UI_STRINGS.PRESS_NEW_SHORTCUT_TEXT; keySpan.classList.add(Config.CSS_CLASSES.HELP_PANEL_KEY_LISTENING); } else { keySpan.classList.add( Config.CSS_CLASSES.HELP_PANEL_KEY_CONFIGURABLE_HIGHLIGHT ); } } }, updatePanelContentDynamic() { if (!this.helpPanelElement) return; const settingsButton = this.helpPanelElement.querySelector( `.${Config.CSS_CLASSES.HELP_PANEL_KEY_SETTING}` ); if (settingsButton) { settingsButton.textContent = State.isCustomizingShortcuts ? Config.UI_STRINGS.FINISH_CUSTOMIZING_BUTTON_TEXT : Config.UI_STRINGS.SETTINGS_BUTTON_TEXT; } this.shortcutDisplaySpansMap.forEach((span, id) => { const shortcut = State.currentShortcutConfig.SHORTCUTS.find( (s) => s.id === id ); if (shortcut) { span.textContent = ShortcutManager.formatShortcutDisplay( shortcut.key ); this.updateKeySpanAppearance(span, shortcut); } }); }, closePanel() { if ( this.helpPanelElement && this.helpPanelElement.classList.contains( Config.CSS_CLASSES.HELP_PANEL_VISIBLE ) ) { this.helpPanelElement.style.animation = `${Config.ELEMENT_IDS.HELP_PANEL_ANIMATE_OUT} ${Config.SCRIPT_SETTINGS.ANIMATION_DURATION_MS}ms ${Config.SCRIPT_SETTINGS.ANIMATION_EASING_POP_OUT} forwards`; clearTimeout(this.panelCloseTimer); this.panelCloseTimer = setTimeout(() => { if (this.helpPanelElement) { this.helpPanelElement.classList.remove( Config.CSS_CLASSES.HELP_PANEL_VISIBLE ); this.helpPanelElement.style.animation = ""; this.helpPanelElement.style.opacity = "0"; this.helpPanelElement.style.visibility = "hidden"; } window.removeEventListener( "click", EventManager.handlePanelInteractionOutsideProxy, true ); window.removeEventListener( "keydown", EventManager.handlePanelEscapeKeyProxy, true ); }, Config.SCRIPT_SETTINGS.ANIMATION_DURATION_MS); } }, toggleVisibility() { if ( !this.helpPanelElement || !document.body.contains(this.helpPanelElement) ) { this.createPanel(); } else { this.updatePanelContentDynamic(); } clearTimeout(this.panelCloseTimer); const isCurrentlyVisibleByClass = this.helpPanelElement.classList.contains( Config.CSS_CLASSES.HELP_PANEL_VISIBLE ); let currentOpacity = 0; if ( isCurrentlyVisibleByClass || this.helpPanelElement.style.animationName === Config.ELEMENT_IDS.HELP_PANEL_ANIMATE_OUT ) { currentOpacity = parseFloat( getComputedStyle(this.helpPanelElement).opacity ); } const isAnimatingOut = this.helpPanelElement.style.animationName === Config.ELEMENT_IDS.HELP_PANEL_ANIMATE_OUT; if ( isCurrentlyVisibleByClass && currentOpacity > 0.01 && !isAnimatingOut ) { if (State.isCustomizingShortcuts && !State.activeCustomizationTarget) { const settingsButton = this.helpPanelElement.querySelector( `.${Config.CSS_CLASSES.HELP_PANEL_KEY_SETTING}` ); if (settingsButton) this.toggleCustomizationModeUI(settingsButton); } this.closePanel(); } else { this.helpPanelElement.style.animation = ""; this.helpPanelElement.style.opacity = "0"; this.helpPanelElement.style.visibility = "hidden"; requestAnimationFrame(() => { this.helpPanelElement.classList.add( Config.CSS_CLASSES.HELP_PANEL_VISIBLE ); this.helpPanelElement.style.visibility = "visible"; this.helpPanelElement.style.animation = `${Config.ELEMENT_IDS.HELP_PANEL_ANIMATE_IN} ${Config.SCRIPT_SETTINGS.ANIMATION_DURATION_MS}ms ${Config.SCRIPT_SETTINGS.ANIMATION_EASING_POP_OUT} forwards`; }); setTimeout(() => { window.addEventListener( "click", EventManager.handlePanelInteractionOutsideProxy, true ); window.addEventListener( "keydown", EventManager.handlePanelEscapeKeyProxy, true ); }, 0); } }, updateShortcutDisplayUI(shortcutId, newKey) { const spanElement = this.shortcutDisplaySpansMap.get(shortcutId); if (spanElement) { spanElement.textContent = newKey ? ShortcutManager.formatShortcutDisplay(newKey) : Config.UI_STRINGS.PRESS_NEW_SHORTCUT_TEXT; } }, setListeningStateUI(shortcutId, spanElement, isListening) { if (isListening) { if ( State.activeCustomizationTarget && State.activeCustomizationTarget.spanElement !== spanElement ) { const prevShortcut = State.currentShortcutConfig.SHORTCUTS.find( (s) => s.id === State.activeCustomizationTarget.shortcutId ); State.activeCustomizationTarget.spanElement.textContent = ShortcutManager.formatShortcutDisplay(prevShortcut?.key); this.updateKeySpanAppearance( State.activeCustomizationTarget.spanElement, prevShortcut ); } State.activeCustomizationTarget = { shortcutId, spanElement }; spanElement.textContent = Config.UI_STRINGS.PRESS_NEW_SHORTCUT_TEXT; this.shortcutDisplaySpansMap.forEach((s) => s.classList.remove( Config.CSS_CLASSES.HELP_PANEL_KEY_CONFIGURABLE_HIGHLIGHT ) ); spanElement.classList.add(Config.CSS_CLASSES.HELP_PANEL_KEY_LISTENING); spanElement.classList.remove( Config.CSS_CLASSES.HELP_PANEL_KEY_CONFIGURABLE_HIGHLIGHT ); } else { if ( State.activeCustomizationTarget && State.activeCustomizationTarget.shortcutId === shortcutId ) { State.activeCustomizationTarget = null; } const currentKey = State.currentShortcutConfig.SHORTCUTS.find( (s) => s.id === shortcutId )?.key; spanElement.textContent = ShortcutManager.formatShortcutDisplay(currentKey); spanElement.classList.remove( Config.CSS_CLASSES.HELP_PANEL_KEY_LISTENING ); if (State.isCustomizingShortcuts) { this.shortcutDisplaySpansMap.forEach((s, id) => { const cfg = State.currentShortcutConfig.SHORTCUTS.find( (sc) => sc.id === id ); if (cfg && !cfg.nonConfigurable && !cfg.isSettingsEntry) { s.classList.add( Config.CSS_CLASSES.HELP_PANEL_KEY_CONFIGURABLE_HIGHLIGHT ); } }); } } }, toggleCustomizationModeUI(settingsButtonSpan) { State.isCustomizingShortcuts = !State.isCustomizingShortcuts; if (State.activeCustomizationTarget) { this.setListeningStateUI( State.activeCustomizationTarget.shortcutId, State.activeCustomizationTarget.spanElement, false ); } settingsButtonSpan.textContent = State.isCustomizingShortcuts ? Config.UI_STRINGS.FINISH_CUSTOMIZING_BUTTON_TEXT : Config.UI_STRINGS.SETTINGS_BUTTON_TEXT; this.shortcutDisplaySpansMap.forEach((span, id) => { const shortcut = State.currentShortcutConfig.SHORTCUTS.find( (s) => s.id === id ); if (shortcut) this.updateKeySpanAppearance(span, shortcut); }); }, displayKeyConflictWarning(spanElement, conflictingKey) { spanElement.textContent = `${ Config.UI_STRINGS.KEY_CONFLICT_TEXT_PREFIX }${conflictingKey.toUpperCase()}${ Config.UI_STRINGS.KEY_CONFLICT_TEXT_SUFFIX }`; setTimeout(() => { if ( State.activeCustomizationTarget && State.activeCustomizationTarget.spanElement === spanElement ) { spanElement.textContent = Config.UI_STRINGS.PRESS_NEW_SHORTCUT_TEXT; } }, 2000); }, displayInvalidModifierWarning(spanElement) { spanElement.textContent = `${ Config.UI_STRINGS.INVALID_MODIFIER_TEXT_PREFIX }${State.getActiveModifierCharDisplay()}${ Config.UI_STRINGS.INVALID_MODIFIER_TEXT_SUFFIX }`; setTimeout(() => { if ( State.activeCustomizationTarget && State.activeCustomizationTarget.spanElement === spanElement ) { spanElement.textContent = Config.UI_STRINGS.PRESS_NEW_SHORTCUT_TEXT; } }, 2000); }, triggerInvalidKeyShake(spanElement) { spanElement.classList.add( Config.CSS_CLASSES.HELP_PANEL_KEY_INVALID_SHAKE ); setTimeout(() => { spanElement.classList.remove( Config.CSS_CLASSES.HELP_PANEL_KEY_INVALID_SHAKE ); }, 500); }, }; const EventManager = { globalKeyDownListener: null, debouncedToggleHelpPanel: null, init() { this.debouncedToggleHelpPanel = this.debounce( HelpPanelManager.toggleVisibility.bind(HelpPanelManager), Config.SCRIPT_SETTINGS.DEBOUNCE_DELAY_MS ); this.reinitializeGlobalKeyListener(); }, debounce(func, wait) { let timeout; return function executedFunction(...args) { const later = () => { clearTimeout(timeout); func.apply(this, args); }; clearTimeout(timeout); timeout = setTimeout(later, wait); }; }, createGlobalKeyListener() { const specialActionHandlers = { toggleHelpPanel: this.debouncedToggleHelpPanel, toggleCustomizationMode: () => { if (HelpPanelManager.helpPanelElement) { const settingsButton = HelpPanelManager.helpPanelElement.querySelector( `.${Config.CSS_CLASSES.HELP_PANEL_KEY_SETTING}` ); if (settingsButton) HelpPanelManager.toggleCustomizationModeUI(settingsButton); } }, }; const shortcutActionMap = {}; State.currentShortcutConfig.SHORTCUTS.forEach((shortcut) => { if (shortcut.key && !shortcut.isSettingsEntry) { const lowerKey = shortcut.key.toLowerCase(); if ( shortcut.actionIdentifier && specialActionHandlers[shortcut.actionIdentifier] ) { shortcutActionMap[lowerKey] = specialActionHandlers[shortcut.actionIdentifier]; } else if (shortcut.selectorConfig) { shortcutActionMap[lowerKey] = () => ShortcutManager.triggerElementClick(shortcut.selectorConfig); } } }); return (event) => { if (event.key === "Escape") { return; } if (State.activeCustomizationTarget) { event.preventDefault(); event.stopPropagation(); const newKey = event.key; const targetSpan = State.activeCustomizationTarget.spanElement; if ( newKey && newKey.length === 1 && !event.ctrlKey && !event.altKey && !event.shiftKey && !event.metaKey && !["Control", "Alt", "Shift", "Meta"].includes(newKey) ) { if (newKey.match(/^[a-zA-Z0-9]$/i)) { const conflictingShortcut = State.currentShortcutConfig.SHORTCUTS.find( (s) => s.key && s.key.toLowerCase() === newKey.toLowerCase() && s.id !== State.activeCustomizationTarget.shortcutId ); if (conflictingShortcut) { HelpPanelManager.displayKeyConflictWarning(targetSpan, newKey); return; } State.saveCustomShortcut( State.activeCustomizationTarget.shortcutId, newKey ); HelpPanelManager.updateShortcutDisplayUI( State.activeCustomizationTarget.shortcutId, newKey ); HelpPanelManager.setListeningStateUI( State.activeCustomizationTarget.shortcutId, targetSpan, false ); } else { HelpPanelManager.triggerInvalidKeyShake(targetSpan); } } else if (!["Control", "Alt", "Shift", "Meta"].includes(newKey)) { if (event[State.getActiveModifierProperty()]) { const conflictingShortcut = State.currentShortcutConfig.SHORTCUTS.find( (s) => s.key && s.key.toLowerCase() === newKey.toLowerCase() && s.id !== State.activeCustomizationTarget.shortcutId ); if (conflictingShortcut) { HelpPanelManager.displayKeyConflictWarning(targetSpan, newKey); return; } State.saveCustomShortcut( State.activeCustomizationTarget.shortcutId, newKey ); HelpPanelManager.updateShortcutDisplayUI( State.activeCustomizationTarget.shortcutId, newKey ); HelpPanelManager.setListeningStateUI( State.activeCustomizationTarget.shortcutId, targetSpan, false ); } else { HelpPanelManager.displayInvalidModifierWarning(targetSpan); } } return; } if (State.isCustomizingShortcuts) { return; } if (!event[State.getActiveModifierProperty()]) { return; } const pressedKey = event.key.toLowerCase(); const actionToExecute = shortcutActionMap[pressedKey]; if (typeof actionToExecute === "function") { actionToExecute(); event.preventDefault(); event.stopPropagation(); } }; }, reinitializeGlobalKeyListener() { if (this.globalKeyDownListener) { window.removeEventListener("keydown", this.globalKeyDownListener, true); } this.globalKeyDownListener = this.createGlobalKeyListener(); window.addEventListener("keydown", this.globalKeyDownListener, true); }, handlePanelInteractionOutsideProxy(event) { if ( HelpPanelManager.helpPanelElement && HelpPanelManager.helpPanelElement.classList.contains( Config.CSS_CLASSES.HELP_PANEL_VISIBLE ) && !HelpPanelManager.helpPanelElement.contains(event.target) && parseFloat( getComputedStyle(HelpPanelManager.helpPanelElement).opacity ) === 1 ) { if (State.isCustomizingShortcuts) { const settingsButton = HelpPanelManager.helpPanelElement.querySelector( `.${Config.CSS_CLASSES.HELP_PANEL_KEY_SETTING}` ); if (settingsButton) HelpPanelManager.toggleCustomizationModeUI(settingsButton); } HelpPanelManager.closePanel(); } }, handlePanelEscapeKeyProxy(event) { if (event.key === "Escape") { if ( HelpPanelManager.helpPanelElement && HelpPanelManager.helpPanelElement.classList.contains( Config.CSS_CLASSES.HELP_PANEL_VISIBLE ) && parseFloat( getComputedStyle(HelpPanelManager.helpPanelElement).opacity ) === 1 ) { event.preventDefault(); event.stopPropagation(); if (State.activeCustomizationTarget) { HelpPanelManager.setListeningStateUI( State.activeCustomizationTarget.shortcutId, State.activeCustomizationTarget.spanElement, false ); } else if (State.isCustomizingShortcuts) { const settingsButton = HelpPanelManager.helpPanelElement.querySelector( `.${Config.CSS_CLASSES.HELP_PANEL_KEY_SETTING}` ); if (settingsButton) HelpPanelManager.toggleCustomizationModeUI(settingsButton); } HelpPanelManager.closePanel(); } } }, }; const ScriptManager = { init() { try { State.initialize(); UserInterface.injectStyles(); EventManager.init(); } catch (error) {} }, }; if (document.readyState === "loading") { document.addEventListener("DOMContentLoaded", () => ScriptManager.init(), { once: true, }); } else { ScriptManager.init(); } })();
QingJ © 2025
镜像随时可能失效,请加Q群300939539或关注我们的公众号极客氢云获取最新地址