A privacy-first, self-hosted, fully open source personal knowledge management software, written in typescript and golang. (PERSONAL FORK)
at lambda-fork/main 320 lines 14 kB view raw
1/// #if !BROWSER 2import * as path from "path"; 3/// #endif 4import {matchHotKey} from "../../protyle/util/hotKey"; 5import {fetchPost} from "../../util/fetch"; 6import {openFileById} from "../../editor/util"; 7import {Constants} from "../../constants"; 8import {newFileByName} from "../../util/newFile"; 9import {App} from "../../index"; 10import {Dialog} from "../../dialog"; 11import {getAllModels} from "../../layout/getAll"; 12import {hasClosestByClassName} from "../../protyle/util/hasClosest"; 13import {getArticle, inputEvent, replace} from "../../search/util"; 14import {useShell} from "../../util/pathName"; 15import {assetInputEvent, renderPreview} from "../../search/assets"; 16import {initSearchMenu} from "../../menus/search"; 17import {writeText} from "../../protyle/util/compatibility"; 18import {checkFold} from "../../util/noRelyPCFunction"; 19import {getUnRefList} from "../../search/unRef"; 20import {toggleAssetHistory, toggleReplaceHistory, toggleSearchHistory} from "../../search/toggleHistory"; 21 22export const searchKeydown = (app: App, event: KeyboardEvent) => { 23 if (getSelection().rangeCount === 0) { 24 return false; 25 } 26 const range = getSelection().getRangeAt(0); 27 if (hasClosestByClassName(range.startContainer, "protyle", true)) { 28 return false; 29 } 30 let element: HTMLElement; 31 let dialog: Dialog; 32 let edit; 33 let unRefEdit; 34 let config: Config.IUILayoutTabSearchConfig; 35 window.siyuan.dialogs.find((item) => { 36 if (item.element.contains(range.startContainer) && item.element.querySelector("#searchList")) { 37 element = item.element.querySelector(".b3-dialog__body"); 38 dialog = item; 39 config = dialog.data; 40 edit = dialog.editors.edit; 41 unRefEdit = dialog.editors.unRefEdit; 42 return true; 43 } 44 }); 45 if (!element) { 46 getAllModels().search.find((item) => { 47 if (item.element.contains(range.startContainer)) { 48 element = item.element; 49 edit = item.editors.edit; 50 config = item.config; 51 unRefEdit = item.editors.unRefEdit; 52 return true; 53 } 54 }); 55 } 56 if (!element) { 57 return false; 58 } 59 const assetsElement = element.querySelector("#searchAssets"); 60 const unRefElement = element.querySelector("#searchUnRefPanel"); 61 const searchType = assetsElement.classList.contains("fn__none") ? (unRefElement.classList.contains("fn__none") ? "doc" : "unRef") : "asset"; 62 const listElement = searchType === "asset" ? assetsElement.querySelector("#searchAssetList") : (searchType === "doc" ? element.querySelector("#searchList") : unRefElement.querySelector("#searchUnRefList")); 63 const searchInputElement = element.querySelector("#searchInput") as HTMLInputElement; 64 if (searchType === "doc" && matchHotKey(window.siyuan.config.keymap.general.newFile.custom, event)) { 65 if (config.method === 0) { 66 newFileByName(app, searchInputElement.value); 67 } 68 return true; 69 } 70 const targetId = (event.target as HTMLElement).id; 71 if (event.key === "ArrowDown" && event.altKey) { 72 if (searchType === "asset") { 73 toggleAssetHistory(assetsElement); 74 } else if (searchType === "doc") { 75 if (targetId === "replaceInput") { 76 toggleReplaceHistory(element.querySelector("#replaceInput")); 77 } else { 78 toggleSearchHistory(element, config, edit); 79 } 80 } 81 return true; 82 } 83 const assetLocal = window.siyuan.storage[Constants.LOCAL_SEARCHASSET] as ISearchAssetOption; 84 if (!window.siyuan.menus.menu.element.classList.contains("fn__none")) { 85 // 不能返回 true,否则历史菜单无法使用快捷键 86 return false; 87 } 88 let currentList: HTMLElement = listElement.querySelector(".b3-list-item--focus"); 89 if (!currentList) { 90 return false; 91 } 92 if (currentList.getAttribute("data-type") === "search-new") { 93 if (event.key === "Enter" && config.method === 0) { 94 newFileByName(app, searchInputElement.value); 95 return true; 96 } 97 return false; 98 } 99 if (searchType !== "asset") { 100 if (matchHotKey(window.siyuan.config.keymap.editor.general.insertRight.custom, event)) { 101 const id = currentList.getAttribute("data-node-id"); 102 checkFold(id, (zoomIn, action) => { 103 openFileById({ 104 app, 105 id, 106 position: "right", 107 action, 108 zoomIn 109 }); 110 if (dialog) { 111 dialog.destroy({focus: "false"}); 112 } 113 }); 114 return true; 115 } 116 const id = currentList.getAttribute("data-node-id"); 117 if (matchHotKey("⌘/", event)) { 118 const currentRect = currentList.getBoundingClientRect(); 119 initSearchMenu(id).popup({ 120 x: currentRect.left + 30, 121 y: currentRect.bottom 122 }); 123 return true; 124 } 125 if (matchHotKey(window.siyuan.config.keymap.editor.general.copyBlockRef.custom, event)) { 126 fetchPost("/api/block/getRefText", {id}, (response) => { 127 writeText(`((${id} '${response.data}'))`); 128 }); 129 return true; 130 } 131 if (matchHotKey(window.siyuan.config.keymap.editor.general.copyBlockEmbed.custom, event)) { 132 writeText(`{{select * from blocks where id='${id}'}}`); 133 return true; 134 } 135 if (matchHotKey(window.siyuan.config.keymap.editor.general.copyProtocol.custom, event)) { 136 writeText(`siyuan://blocks/${id}`); 137 return true; 138 } 139 if (matchHotKey(window.siyuan.config.keymap.editor.general.copyProtocolInMd.custom, event)) { 140 fetchPost("/api/block/getRefText", {id}, (response) => { 141 writeText(`[${response.data}](siyuan://blocks/${id})`); 142 }); 143 return true; 144 } 145 if (matchHotKey(window.siyuan.config.keymap.editor.general.copyHPath.custom, event)) { 146 fetchPost("/api/filetree/getHPathByID", { 147 id 148 }, (response) => { 149 writeText(response.data); 150 }); 151 return true; 152 } 153 if (matchHotKey(window.siyuan.config.keymap.editor.general.copyID.custom, event)) { 154 writeText(id); 155 return true; 156 } 157 } 158 159 if (Constants.KEYCODELIST[event.keyCode] === "PageUp") { 160 if (searchType === "asset") { 161 if (!assetsElement.querySelector('[data-type="assetPrevious"]').getAttribute("disabled")) { 162 let currentPage = parseInt(assetsElement.querySelector("#searchAssetResult .fn__flex-center").textContent.split("/")[0]); 163 if (currentPage > 1) { 164 currentPage--; 165 assetInputEvent(assetsElement, assetLocal, currentPage); 166 } 167 } 168 } else if (searchType === "doc") { 169 if (!element.querySelector('[data-type="previous"]').getAttribute("disabled")) { 170 if (config.page > 1) { 171 config.page--; 172 inputEvent(element, config, edit); 173 } 174 } 175 } else if (searchType === "unRef") { 176 if (!element.querySelector('[data-type="unRefPrevious"]').getAttribute("disabled")) { 177 let currentPage = parseInt(unRefElement.querySelector("#searchUnRefResult").textContent); 178 if (currentPage > 1) { 179 currentPage--; 180 getUnRefList(unRefElement, unRefEdit, currentPage); 181 } 182 } 183 } 184 return true; 185 } 186 if (Constants.KEYCODELIST[event.keyCode] === "PageDown") { 187 if (searchType === "asset") { 188 if (!assetsElement.querySelector('[data-type="assetNext"]').getAttribute("disabled")) { 189 const assetPages = assetsElement.querySelector("#searchAssetResult .fn__flex-center").textContent.split("/"); 190 let currentPage = parseInt(assetPages[0]); 191 if (currentPage < parseInt(assetPages[1])) { 192 currentPage++; 193 assetInputEvent(assetsElement, assetLocal, currentPage); 194 } 195 } 196 } else if (searchType === "doc") { 197 const nextElement = element.querySelector('[data-type="next"]'); 198 if (!nextElement.getAttribute("disabled")) { 199 if (config.page < parseInt(nextElement.parentElement.querySelector("#searchResult").getAttribute("data-pagecount"))) { 200 config.page++; 201 inputEvent(element, config, edit); 202 } 203 } 204 } else if (searchType === "unRef") { 205 if (!element.querySelector('[data-type="unRefNext"]').getAttribute("disabled")) { 206 let currentPage = parseInt(unRefElement.querySelector("#searchUnRefResult").textContent); 207 if (currentPage < parseInt(unRefElement.querySelector("#searchUnRefResult").textContent.split("/")[1])) { 208 currentPage++; 209 getUnRefList(unRefElement, unRefEdit, currentPage); 210 } 211 } 212 } 213 return true; 214 } 215 if (!window.siyuan.menus.menu.element.classList.contains("fn__none")) { 216 return false; 217 } 218 if (event.key === "Enter") { 219 if (searchType !== "asset") { 220 if (targetId === "replaceInput") { 221 replace(element, config, edit, false); 222 } else { 223 const id = currentList.getAttribute("data-node-id"); 224 checkFold(id, (zoomIn, action) => { 225 openFileById({ 226 app, 227 id, 228 action, 229 zoomIn 230 }); 231 if (dialog) { 232 dialog.destroy({focus: "false"}); 233 } 234 }); 235 } 236 } else { 237 /// #if !BROWSER 238 useShell("showItemInFolder", path.join(window.siyuan.config.system.dataDir, currentList.lastElementChild.getAttribute("aria-label"))); 239 /// #endif 240 } 241 return true; 242 } 243 const lineHeight = 28; 244 const assetPreviewElement = assetsElement.querySelector("#searchAssetPreview"); 245 if (event.key === "ArrowDown") { 246 currentList.classList.remove("b3-list-item--focus"); 247 if (!currentList.nextElementSibling) { 248 if (config.group === 1 && searchType === "doc") { 249 if (currentList.parentElement.nextElementSibling) { 250 currentList.parentElement.nextElementSibling.nextElementSibling.firstElementChild.classList.add("b3-list-item--focus"); 251 } else { 252 listElement.children[1].firstElementChild.classList.add("b3-list-item--focus"); 253 } 254 } else { 255 listElement.firstElementChild.classList.add("b3-list-item--focus"); 256 } 257 } else { 258 currentList.nextElementSibling.classList.add("b3-list-item--focus"); 259 } 260 currentList = listElement.querySelector(".b3-list-item--focus"); 261 if (listElement.scrollTop < currentList.offsetTop - listElement.clientHeight + lineHeight || 262 listElement.scrollTop > currentList.offsetTop) { 263 listElement.scrollTop = currentList.offsetTop - listElement.clientHeight + lineHeight; 264 } 265 if (searchType === "asset") { 266 renderPreview(assetPreviewElement, currentList.dataset.id, searchInputElement.value, assetLocal.method); 267 } else if (searchType === "doc") { 268 getArticle({ 269 id: currentList.getAttribute("data-node-id"), 270 config, 271 value: searchInputElement.value, 272 edit, 273 }); 274 } else { 275 getArticle({ 276 id: currentList.getAttribute("data-node-id"), 277 edit: unRefEdit, 278 }); 279 } 280 return true; 281 } 282 if (event.key === "ArrowUp") { 283 currentList.classList.remove("b3-list-item--focus"); 284 if (!currentList.previousElementSibling) { 285 if (config.group === 1 && searchType === "doc") { 286 if (currentList.parentElement.previousElementSibling.previousElementSibling) { 287 currentList.parentElement.previousElementSibling.previousElementSibling.lastElementChild.classList.add("b3-list-item--focus"); 288 } else { 289 listElement.lastElementChild.lastElementChild.classList.add("b3-list-item--focus"); 290 } 291 } else { 292 listElement.lastElementChild.classList.add("b3-list-item--focus"); 293 } 294 } else { 295 currentList.previousElementSibling.classList.add("b3-list-item--focus"); 296 } 297 currentList = listElement.querySelector(".b3-list-item--focus"); 298 if (listElement.scrollTop < currentList.offsetTop - listElement.clientHeight + lineHeight || 299 listElement.scrollTop > currentList.offsetTop - lineHeight * 2) { 300 listElement.scrollTop = currentList.offsetTop - lineHeight * 2; 301 } 302 if (searchType === "asset") { 303 renderPreview(assetPreviewElement, currentList.dataset.id, searchInputElement.value, assetLocal.method); 304 } else if (searchType === "doc") { 305 getArticle({ 306 id: currentList.getAttribute("data-node-id"), 307 config, 308 value: searchInputElement.value, 309 edit, 310 }); 311 } else if (searchType === "unRef") { 312 getArticle({ 313 id: currentList.getAttribute("data-node-id"), 314 edit: unRefEdit, 315 }); 316 } 317 return true; 318 } 319 return false; 320};