A privacy-first, self-hosted, fully open source personal knowledge management software, written in typescript and golang. (PERSONAL FORK)
at lambda-fork/main 197 lines 9.0 kB view raw
1import {Constants} from "../../constants"; 2import {fetchPost} from "../../util/fetch"; 3/// #if !BROWSER 4import {sendGlobalShortcut} from "./keydown"; 5import {ipcRenderer} from "electron"; 6/// #endif 7import {App} from "../../index"; 8import {isMac, isNotCtrl, isOnlyMeta} from "../../protyle/util/compatibility"; 9import {showPopover} from "../../block/popover"; 10 11const matchKeymap = (keymap: Config.IKeys, key1: "general" | "editor", key2?: "general" | "insert" | "heading" | "list" | "table") => { 12 if (key1 === "general") { 13 if (!window.siyuan.config.keymap[key1]) { 14 /// #if !BROWSER 15 ipcRenderer.send(Constants.SIYUAN_CMD, { 16 cmd: "writeLog", 17 msg: "window.siyuan.config.keymap.general is not found" 18 }); 19 /// #endif 20 window.siyuan.config.keymap[key1] = keymap as Config.IKeymapGeneral; 21 return false; 22 } 23 } else { 24 if (!window.siyuan.config.keymap[key1]) { 25 /// #if !BROWSER 26 ipcRenderer.send(Constants.SIYUAN_CMD, { 27 cmd: "writeLog", 28 msg: "window.siyuan.config.keymap.editor is not found" 29 }); 30 /// #endif 31 window.siyuan.config.keymap[key1] = JSON.parse(JSON.stringify(Constants.SIYUAN_KEYMAP.editor)); 32 return false; 33 } 34 if (!window.siyuan.config.keymap[key1][key2]) { 35 /// #if !BROWSER 36 ipcRenderer.send(Constants.SIYUAN_CMD, { 37 cmd: "writeLog", 38 msg: `window.siyuan.config.keymap.editor.${key2} is not found` 39 }); 40 /// #endif 41 (window.siyuan.config.keymap[key1][key2] as Config.IKeymapEditor[typeof key2]) = keymap as Config.IKeymapEditor[typeof key2]; 42 return false; 43 } 44 } 45 let match = true; 46 Object.keys(keymap).forEach(key => { 47 if (key1 === "general") { 48 if (!window.siyuan.config.keymap[key1][key] || window.siyuan.config.keymap[key1][key].default !== keymap[key].default) { 49 /// #if !BROWSER 50 ipcRenderer.send(Constants.SIYUAN_CMD, { 51 cmd: "writeLog", 52 msg: `window.siyuan.config.keymap.${key1}.${key} is not found or match: ${window.siyuan.config.keymap[key1][key]?.default}` 53 }); 54 /// #endif 55 match = false; 56 window.siyuan.config.keymap[key1][key] = keymap[key]; 57 } 58 } else { 59 if (!window.siyuan.config.keymap[key1][key2][key] || window.siyuan.config.keymap[key1][key2][key].default !== keymap[key].default) { 60 /// #if !BROWSER 61 ipcRenderer.send(Constants.SIYUAN_CMD, { 62 cmd: "writeLog", 63 msg: `window.siyuan.config.keymap.${key1}.${key2}.${key} is not found or match: ${window.siyuan.config.keymap[key1][key2][key]?.default}` 64 }); 65 /// #endif 66 match = false; 67 window.siyuan.config.keymap[key1][key2][key] = keymap[key]; 68 } 69 } 70 }); 71 return match; 72}; 73 74const hasKeymap = (keymap: Record<string, IKeymapItem>, key1: "general" | "editor", key2?: "general" | "insert" | "heading" | "list" | "table") => { 75 let match = true; 76 if (key1 === "editor") { 77 if (Object.keys(window.siyuan.config.keymap[key1][key2]).length !== Object.keys(Constants.SIYUAN_KEYMAP[key1][key2]).length) { 78 Object.keys(window.siyuan.config.keymap[key1][key2]).forEach(item => { 79 if (!Constants.SIYUAN_KEYMAP[key1][key2][item]) { 80 match = false; 81 delete window.siyuan.config.keymap[key1][key2][item]; 82 } 83 }); 84 } 85 } else { 86 if (Object.keys(window.siyuan.config.keymap[key1]).length !== Object.keys(Constants.SIYUAN_KEYMAP[key1]).length) { 87 Object.keys(window.siyuan.config.keymap[key1]).forEach(item => { 88 if (!Constants.SIYUAN_KEYMAP[key1][item]) { 89 match = false; 90 delete window.siyuan.config.keymap[key1][item]; 91 } 92 }); 93 } 94 } 95 return match; 96}; 97 98export const correctHotkey = (app: App) => { 99 const matchKeymap1 = matchKeymap(Constants.SIYUAN_KEYMAP.general, "general"); 100 const matchKeymap2 = matchKeymap(Constants.SIYUAN_KEYMAP.editor.general, "editor", "general"); 101 const matchKeymap3 = matchKeymap(Constants.SIYUAN_KEYMAP.editor.insert, "editor", "insert"); 102 const matchKeymap4 = matchKeymap(Constants.SIYUAN_KEYMAP.editor.heading, "editor", "heading"); 103 const matchKeymap5 = matchKeymap(Constants.SIYUAN_KEYMAP.editor.list, "editor", "list"); 104 const matchKeymap6 = matchKeymap(Constants.SIYUAN_KEYMAP.editor.table, "editor", "table"); 105 106 const hasKeymap1 = hasKeymap(Constants.SIYUAN_KEYMAP.general, "general"); 107 const hasKeymap2 = hasKeymap(Constants.SIYUAN_KEYMAP.editor.general, "editor", "general"); 108 const hasKeymap3 = hasKeymap(Constants.SIYUAN_KEYMAP.editor.insert, "editor", "insert"); 109 const hasKeymap4 = hasKeymap(Constants.SIYUAN_KEYMAP.editor.heading, "editor", "heading"); 110 const hasKeymap5 = hasKeymap(Constants.SIYUAN_KEYMAP.editor.list, "editor", "list"); 111 const hasKeymap6 = hasKeymap(Constants.SIYUAN_KEYMAP.editor.table, "editor", "table"); 112 if (!window.siyuan.config.readonly && 113 (!matchKeymap1 || !matchKeymap2 || !matchKeymap3 || !matchKeymap4 || !matchKeymap5 || !matchKeymap6 || 114 !hasKeymap1 || !hasKeymap2 || !hasKeymap3 || !hasKeymap4 || !hasKeymap5 || !hasKeymap6)) { 115 /// #if !BROWSER 116 ipcRenderer.send(Constants.SIYUAN_CMD, { 117 cmd: "writeLog", 118 msg: "update keymap" 119 }); 120 /// #endif 121 fetchPost("/api/setting/setKeymap", { 122 data: window.siyuan.config.keymap 123 }, () => { 124 /// #if !BROWSER 125 sendGlobalShortcut(app); 126 /// #endif 127 }); 128 } 129}; 130 131export const filterHotkey = (event: KeyboardEvent, app: App) => { 132 // https://github.com/siyuan-note/siyuan/issues/9848 忘记为什么要阻止了 .av__mask 的情况,测了下没问题就先移除 133 if (document.getElementById("progress") || document.getElementById("errorLog") || event.isComposing) { 134 return true; 135 } 136 const target = event.target as HTMLElement; 137 // 点击最近的文档列表会 dispatch keydown 的 Enter https://github.com/siyuan-note/siyuan/issues/12967 138 if (event.isTrusted && isNotCtrl(event) && !event.shiftKey && !event.altKey && 139 !["INPUT", "TEXTAREA"].includes(target.tagName) && 140 ["0", "1", "2", "3", "4", "j", "k", "l", ";", "s", " ", "p", "enter", "a", "s", "d", "f", "q", "x"].includes(event.key.toLowerCase())) { 141 let cardElement: Element; 142 window.siyuan.dialogs.find(item => { 143 if (item.element.getAttribute("data-key") === Constants.DIALOG_OPENCARD) { 144 cardElement = item.element; 145 return true; 146 } 147 }); 148 if (!cardElement) { 149 cardElement = document.querySelector(`.layout__wnd--active div[data-key="${Constants.DIALOG_OPENCARD}"]:not(.fn__none)`); 150 } 151 if (cardElement) { 152 event.preventDefault(); 153 cardElement.dispatchEvent(new CustomEvent("click", {detail: event.key.toLowerCase()})); 154 return true; 155 } 156 } 157 158 // 仅处理以下快捷键操作 159 if (isNotCtrl(event) && event.key !== "Escape" && !event.shiftKey && !event.altKey && 160 Constants.KEYCODELIST[event.keyCode] !== "PageUp" && 161 Constants.KEYCODELIST[event.keyCode] !== "PageDown" && 162 event.key !== "Home" && event.key !== "End" && 163 !/^F\d{1,2}$/.test(event.key) && event.key.indexOf("Arrow") === -1 && event.key !== "Enter" && event.key !== "Backspace" && event.key !== "Delete") { 164 return true; 165 } 166 167 if (!event.altKey && !event.shiftKey && isOnlyMeta(event)) { 168 if ((isMac() ? event.key === "Meta" : event.key === "Control") || isOnlyMeta(event)) { 169 window.siyuan.ctrlIsPressed = true; 170 if ((event.key === "Meta" || event.key === "Control") && 171 window.siyuan.config.editor.floatWindowMode === 1 && !event.repeat) { 172 showPopover(app); 173 } 174 } else { 175 window.siyuan.ctrlIsPressed = false; 176 } 177 } 178 179 if (!event.altKey && event.shiftKey && isNotCtrl(event)) { 180 if (event.key === "Shift") { 181 window.siyuan.shiftIsPressed = true; 182 if (!event.repeat) { 183 showPopover(app, true); 184 } 185 } else { 186 window.siyuan.shiftIsPressed = false; 187 } 188 } 189 190 if (event.altKey && !event.shiftKey && isNotCtrl(event)) { 191 if (event.key === "Alt") { 192 window.siyuan.altIsPressed = true; 193 } else { 194 window.siyuan.altIsPressed = false; 195 } 196 } 197};