import { copyPlainText, isMac, isNotCtrl, isOnlyMeta, updateHotkeyTip, writeText } from "../../protyle/util/compatibility"; import {matchAuxiliaryHotKey, matchHotKey} from "../../protyle/util/hotKey"; import {openSearch} from "../../search/spread"; import { hasClosestBlock, hasClosestByAttribute, hasClosestByClassName, hasTopClosestByTag, } from "../../protyle/util/hasClosest"; import {newFile} from "../../util/newFile"; import {Constants} from "../../constants"; import {openSetting} from "../../config"; import {getInstanceById} from "../../layout/util"; import {closeTabByType, copyTab, getActiveTab, getDockByType, resizeTabs, switchTabByIndex} from "../../layout/tabUtil"; import {Tab} from "../../layout/Tab"; import {Editor} from "../../editor"; import {setEditMode} from "../../protyle/util/setEditMode"; import {rename} from "../../editor/rename"; import {Files} from "../../layout/dock/Files"; import {newDailyNote} from "../../util/mount"; import {hideElements} from "../../protyle/ui/hideElements"; import {fetchPost} from "../../util/fetch"; import {goBack, goForward} from "../../util/backForward"; import {onGet} from "../../protyle/util/onGet"; import {getDisplayName, getNotebookName, getTopPaths, movePathTo, moveToPath} from "../../util/pathName"; import {openFileById} from "../../editor/util"; import {getAllDocks, getAllModels, getAllTabs} from "../../layout/getAll"; import {openGlobalSearch} from "../../search/util"; import {focusBlock, focusByOffset, focusByRange, getSelectionOffset} from "../../protyle/util/selection"; import {initFileMenu, initNavigationMenu} from "../../menus/navigation"; import {bindMenuKeydown} from "../../menus/Menu"; import {Dialog} from "../../dialog"; import {unicode2Emoji} from "../../emoji"; import {deleteFiles} from "../../editor/deleteFile"; import {escapeHtml} from "../../util/escape"; import {syncGuide} from "../../sync/syncGuide"; import {showPopover} from "../../block/popover"; import {getStartEndElement, goEnd, goHome} from "../../protyle/wysiwyg/commonHotkey"; import {getNextFileLi, getPreviousFileLi} from "../../protyle/wysiwyg/getBlock"; import {editor} from "../../config/editor"; import {hintMoveBlock} from "../../protyle/hint/extend"; import {Backlink} from "../../layout/dock/Backlink"; /// #if !BROWSER import {setZoom} from "../../layout/topBar"; import {ipcRenderer} from "electron"; /// #endif import {openHistory} from "../../history/history"; import {openCard, openCardByData} from "../../card/openCard"; import {lockScreen} from "../../dialog/processSystem"; import {isWindow} from "../../util/functions"; import {reloadProtyle} from "../../protyle/util/reload"; import {fullscreen, updateReadonly} from "../../protyle/breadcrumb/action"; import {openRecentDocs} from "../../business/openRecentDocs"; import {App} from "../../index"; import {commandPanel} from "../../plugin/commandPanel"; import {openBacklink, openGraph, openOutline, toggleDockBar} from "../../layout/dock/util"; import {workspaceMenu} from "../../menus/workspace"; import {resize} from "../../protyle/util/resize"; import {Search} from "../../search"; import {Custom} from "../../layout/dock/Custom"; import {Protyle} from "../../protyle"; import {transaction} from "../../protyle/wysiwyg/transaction"; import {quickMakeCard} from "../../card/makeCard"; import {copyPNG} from "../../menus/util"; import {getContentByInlineHTML} from "../../protyle/wysiwyg/keydown"; import {searchKeydown} from "./searchKeydown"; import {openNewWindow} from "../../window/openNewWindow"; import {historyKeydown} from "../../history/keydown"; import {zoomOut} from "../../menus/protyle"; import {openSearchAV} from "../../protyle/render/av/relation"; import * as dayjs from "dayjs"; import {getPlainText} from "../../protyle/util/paste"; const switchDialogEvent = (app: App, event: MouseEvent) => { event.preventDefault(); let target = event.target as HTMLElement; while (!target.isSameNode(switchDialog.element)) { if (target.classList.contains("b3-list-item")) { const currentType = target.getAttribute("data-type"); if (currentType) { if (currentType === "riffCard") { openCard(app); } else { getDockByType(currentType).toggleModel(currentType, true); } } else { const currentId = target.getAttribute("data-id"); getAllTabs().find(item => { if (item.id === currentId) { item.parent.switchTab(item.headElement); item.parent.showHeading(); return true; } }); } switchDialog.destroy(); switchDialog = undefined; break; } target = target.parentElement; } }; const dialogArrow = (app: App, element: HTMLElement, event: KeyboardEvent) => { let currentLiElement = element.querySelector(".b3-list-item--focus"); if (currentLiElement) { currentLiElement.classList.remove("b3-list-item--focus"); if (event.key === "ArrowUp") { if (currentLiElement.previousElementSibling) { currentLiElement.previousElementSibling.classList.add("b3-list-item--focus"); } else { currentLiElement.parentElement.lastElementChild.classList.add("b3-list-item--focus"); } } else if (event.key === "ArrowDown") { if (currentLiElement.nextElementSibling) { currentLiElement.nextElementSibling.classList.add("b3-list-item--focus"); } else { currentLiElement.parentElement.firstElementChild.classList.add("b3-list-item--focus"); } } else if (event.key === "ArrowLeft" || event.key === "ArrowRight") { const sideElement = currentLiElement.parentElement.previousElementSibling || currentLiElement.parentElement.nextElementSibling; if (sideElement) { const tempLiElement = sideElement.querySelector(`[data-index="${currentLiElement.getAttribute("data-index")}"]`) || sideElement.lastElementChild; if (tempLiElement) { tempLiElement.classList.add("b3-list-item--focus"); } else { currentLiElement.classList.add("b3-list-item--focus"); } } else { currentLiElement.classList.add("b3-list-item--focus"); } } else if (event.key === "Enter") { const currentType = currentLiElement.getAttribute("data-type"); if (currentType) { if (currentType === "riffCard") { openCard(app); } else { getDockByType(currentType).toggleModel(currentType, true); } } else { openFileById({ app, id: currentLiElement.getAttribute("data-node-id"), action: [Constants.CB_GET_FOCUS, Constants.CB_GET_SCROLL] }); } hideElements(["dialog"]); return; } currentLiElement = element.querySelector(".b3-list-item--focus"); const rootId = currentLiElement.getAttribute("data-node-id"); const pathElement = element.querySelector(".switch-doc__path"); if (rootId) { fetchPost("/api/filetree/getFullHPathByID", { id: rootId }, (response) => { pathElement.innerHTML = escapeHtml(response.data); }); } else { pathElement.innerHTML = currentLiElement.querySelector(".b3-list-item__text").innerHTML; } const currentRect = currentLiElement.getBoundingClientRect(); const currentParentRect = currentLiElement.parentElement.getBoundingClientRect(); if (currentRect.top < currentParentRect.top) { currentLiElement.scrollIntoView(true); } else if (currentRect.bottom > currentParentRect.bottom) { currentLiElement.scrollIntoView(false); } } }; const editKeydown = (app: App, event: KeyboardEvent) => { let protyle: IProtyle; let range: Range; if (getSelection().rangeCount > 0) { range = getSelection().getRangeAt(0); } const activePanelElement = document.querySelector(".layout__tab--active"); let isFileFocus = false; if (activePanelElement && activePanelElement.classList.contains("sy__file")) { isFileFocus = true; } if (range) { window.siyuan.dialogs.find(item => { if (item.editors) { Object.keys(item.editors).find(key => { if (item.editors[key].protyle.element.contains(range.startContainer)) { protyle = item.editors[key].protyle; // https://github.com/siyuan-note/siyuan/issues/9384 isFileFocus = false; return true; } }); if (protyle) { return true; } } }); } const activeTab = getActiveTab(); if (!protyle && activeTab) { if (activeTab.model instanceof Editor) { protyle = activeTab.model.editor.protyle; } else if (activeTab.model instanceof Search) { if (activeTab.model.element.querySelector("#searchUnRefPanel").classList.contains("fn__none")) { protyle = activeTab.model.editors.edit.protyle; } else { protyle = activeTab.model.editors.unRefEdit.protyle; } } else if (activeTab.model instanceof Custom && activeTab.model.data?.editor instanceof Protyle) { protyle = activeTab.model.data.editor.protyle; } else { return false; } } else if (!protyle) { if (!protyle && range) { window.siyuan.blockPanels.find(item => { item.editors.find(editorItem => { if (editorItem.protyle.element.contains(range.startContainer)) { protyle = editorItem.protyle; return true; } }); if (protyle) { return true; } }); } const models = getAllModels(); if (!protyle) { models.backlink.find(item => { if (item.element.classList.contains("layout__tab--active")) { if (range) { item.editors.find(editor => { if (editor.protyle.element.contains(range.startContainer)) { protyle = editor.protyle; return true; } }); } if (!protyle && item.editors.length > 0) { protyle = item.editors[0].protyle; } return true; } }); } if (!protyle) { models.editor.find(item => { if (item.parent.headElement.classList.contains("item--focus")) { protyle = item.editor.protyle; return true; } }); } if (!protyle) { return false; } } let searchKey = ""; if (matchHotKey(window.siyuan.config.keymap.general.replace.custom, event)) { searchKey = Constants.DIALOG_REPLACE; } else if (matchHotKey(window.siyuan.config.keymap.general.search.custom, event)) { searchKey = Constants.DIALOG_SEARCH; } if (!isFileFocus && searchKey) { if (range && protyle.element.contains(range.startContainer)) { openSearch({ app, hotkey: searchKey, key: range.toString(), notebookId: protyle.notebookId, searchPath: protyle.path }); } else { openSearch({ app, hotkey: searchKey, }); } event.preventDefault(); return true; } if (!isFileFocus && matchHotKey(window.siyuan.config.keymap.editor.general.quickMakeCard.custom, event)) { if (protyle.title?.editElement.contains(range.startContainer)) { quickMakeCard(protyle, [protyle.title.element]); } else { const selectElement: Element[] = []; protyle.wysiwyg.element.querySelectorAll(".protyle-wysiwyg--select").forEach(item => { selectElement.push(item); }); if (selectElement.length === 0) { const nodeElement = hasClosestBlock(range.startContainer); if (nodeElement) { selectElement.push(nodeElement); } } quickMakeCard(protyle, selectElement); } event.preventDefault(); return true; } if (!isFileFocus && matchHotKey(window.siyuan.config.keymap.general.addToDatabase.custom, event)) { if (protyle.title?.editElement.contains(range.startContainer)) { openSearchAV("", protyle.breadcrumb.element, (listItemElement) => { const sourceIds: string[] = [protyle.block.rootID]; const avID = listItemElement.dataset.avId; transaction(protyle, [{ action: "insertAttrViewBlock", avID, ignoreFillFilter: true, srcIDs: sourceIds, isDetached: false, blockID: listItemElement.dataset.nodeId }, { action: "doUpdateUpdated", id: listItemElement.dataset.nodeId, data: dayjs().format("YYYYMMDDHHmmss"), }], [{ action: "removeAttrViewBlock", srcIDs: sourceIds, avID, }]); focusByRange(range) }); } else { const selectElement: Element[] = []; protyle.wysiwyg.element.querySelectorAll(".protyle-wysiwyg--select").forEach(item => { selectElement.push(item); }); if (selectElement.length === 0) { const nodeElement = hasClosestBlock(range.startContainer); if (nodeElement) { selectElement.push(nodeElement); } } openSearchAV("", selectElement[0] as HTMLElement, (listItemElement) => { const sourceIds: string[] = []; selectElement.forEach(item => { sourceIds.push(item.getAttribute("data-node-id")); }); const avID = listItemElement.dataset.avId; transaction(protyle, [{ action: "insertAttrViewBlock", avID, ignoreFillFilter: true, srcIDs: sourceIds, isDetached: false, blockID: listItemElement.dataset.blockId }, { action: "doUpdateUpdated", id: listItemElement.dataset.blockId, data: dayjs().format("YYYYMMDDHHmmss"), }], [{ action: "removeAttrViewBlock", srcIDs: sourceIds, avID, }]); focusByRange(range) }); } event.preventDefault(); return true; } if (!isFileFocus && matchHotKey(window.siyuan.config.keymap.editor.general.spaceRepetition.custom, event)) { fetchPost("/api/riff/getTreeRiffDueCards", {rootID: protyle.block.rootID}, (response) => { openCardByData(app, response.data, "doc", protyle.block.rootID, protyle.title?.editElement.textContent || window.siyuan.languages.untitled); }); event.preventDefault(); return true; } if (!isFileFocus && matchHotKey(window.siyuan.config.keymap.general.move.custom, event)) { let range: Range; let nodeElement: false | HTMLElement; if (getSelection().rangeCount > 0) { range = getSelection().getRangeAt(0); nodeElement = hasClosestBlock(range.startContainer); } if (protyle.title?.editElement.contains(range.startContainer)) { movePathTo((toPath, toNotebook) => { moveToPath([protyle.path], toNotebook[0], toPath[0]); }, [protyle.path], range); } else if (nodeElement && range && protyle.element.contains(range.startContainer)) { let selectElements = Array.from(protyle.wysiwyg.element.querySelectorAll(".protyle-wysiwyg--select")); if (selectElements.length === 0) { selectElements = [nodeElement]; } movePathTo((toPath) => { hintMoveBlock(toPath[0], selectElements, protyle); }); } event.preventDefault(); return true; } const target = event.target as HTMLElement; if (target.tagName !== "TABLE" && ["INPUT", "TEXTAREA"].includes(target.tagName)) { return false; } // ctrl+home 光标移动到顶 if (!event.altKey && !event.shiftKey && isOnlyMeta(event) && event.key === "Home") { goHome(protyle); hideElements(["select"], protyle); event.stopPropagation(); event.preventDefault(); return; } // ctrl+end 光标移动到尾 if (!event.altKey && !event.shiftKey && isOnlyMeta(event) && event.key === "End") { goEnd(protyle); hideElements(["select"], protyle); event.stopPropagation(); event.preventDefault(); return; } if (matchHotKey(window.siyuan.config.keymap.editor.general.exitFocus.custom, event)) { event.preventDefault(); zoomOut({protyle, id: protyle.block.rootID, focusId: protyle.block.id}); return true; } if (matchHotKey(window.siyuan.config.keymap.editor.general.switchReadonly.custom, event)) { event.preventDefault(); updateReadonly(protyle.breadcrumb.element.parentElement.querySelector('.block__icon[data-type="readonly"]'), protyle); return true; } if (matchHotKey(window.siyuan.config.keymap.editor.general.backlinks.custom, event)) { event.preventDefault(); if (range) { const refElement = hasClosestByAttribute(range.startContainer, "data-type", "block-ref"); if (refElement) { openBacklink({ app: protyle.app, blockId: refElement.dataset.id, }); return true; } } openBacklink({ app: protyle.app, blockId: protyle.block.id, rootId: protyle.block.rootID, useBlockId: protyle.block.showAll, title: protyle.title ? (protyle.title.editElement.textContent || window.siyuan.languages.untitled) : null, }); return true; } if (matchHotKey(window.siyuan.config.keymap.editor.general.graphView.custom, event)) { event.preventDefault(); if (range) { const refElement = hasClosestByAttribute(range.startContainer, "data-type", "block-ref"); if (refElement) { openGraph({ app: protyle.app, blockId: refElement.dataset.id, }); return true; } } openGraph({ app: protyle.app, blockId: protyle.block.id, rootId: protyle.block.rootID, useBlockId: protyle.block.showAll, title: protyle.title ? (protyle.title.editElement.textContent || window.siyuan.languages.untitled) : null, }); return true; } if (matchHotKey(window.siyuan.config.keymap.editor.general.outline.custom, event)) { event.preventDefault(); const offset = getSelectionOffset(target); openOutline(protyle); // switchWnd 后,range会被清空,需要重新设置 focusByOffset(target, offset.start, offset.end); return true; } if (matchHotKey(window.siyuan.config.keymap.editor.general.copyPlainText.custom, event)) { const nodeElement = hasClosestBlock(range.startContainer); if (!nodeElement) { return false; } if (range.toString() === "") { const selectsElement: HTMLElement[] = Array.from(protyle.wysiwyg.element.querySelectorAll(".protyle-wysiwyg--select")); let html = ""; if (selectsElement.length === 0) { selectsElement.push(nodeElement); } selectsElement.forEach(item => { html += getPlainText(item) + "\n"; }); copyPlainText(html.trimEnd()); } else { copyPlainText(range.toString()); } event.preventDefault(); return true; } if (matchHotKey(window.siyuan.config.keymap.editor.general.refresh.custom, event)) { reloadProtyle(protyle, true); event.preventDefault(); return true; } if (matchHotKey(window.siyuan.config.keymap.editor.general.fullscreen.custom, event)) { fullscreen(protyle.element); resize(protyle); event.preventDefault(); return true; } if (matchHotKey(window.siyuan.config.keymap.editor.general.preview.custom, event)) { setEditMode(protyle, "preview"); event.preventDefault(); return true; } if (matchHotKey(window.siyuan.config.keymap.editor.general.wysiwyg.custom, event) && !protyle.options.backlinkData) { setEditMode(protyle, "wysiwyg"); protyle.scroll.lastScrollTop = 0; fetchPost("/api/filetree/getDoc", { id: protyle.block.parentID, size: window.siyuan.config.editor.dynamicLoadBlocks, }, getResponse => { onGet({data: getResponse, protyle}); }); event.preventDefault(); return true; } if (matchHotKey(window.siyuan.config.keymap.editor.general.copyBlockRef.custom, event)) { event.preventDefault(); event.stopPropagation(); if (hasClosestByClassName(range.startContainer, "protyle-title")) { fetchPost("/api/block/getRefText", {id: protyle.block.rootID}, (response) => { writeText(`((${protyle.block.rootID} '${response.data}'))`); }); } else { const nodeElement = hasClosestBlock(range.startContainer); if (!nodeElement) { return false; } const selectElements = protyle.wysiwyg.element.querySelectorAll(".protyle-wysiwyg--select"); let actionElement; if (selectElements.length === 1) { actionElement = selectElements[0]; } else { const selectImgElement = nodeElement.querySelector(".img--select"); if (selectImgElement) { copyPNG(selectImgElement.querySelector("img")); return true; } actionElement = nodeElement; } const actionElementId = actionElement.getAttribute("data-node-id"); if (range.toString() !== "") { getContentByInlineHTML(range, (content) => { writeText(`((${actionElementId} "${content.trim()}"))`); }); } else { fetchPost("/api/block/getRefText", {id: actionElementId}, (response) => { writeText(`((${actionElementId} '${response.data}'))`); }); } } } if (hasClosestByClassName(target, "protyle-title__input")) { return false; } // 没有光标时,无法撤销 https://ld246.com/article/1624021111567 if (matchHotKey(window.siyuan.config.keymap.editor.general.undo.custom, event)) { protyle.undo.undo(protyle); event.preventDefault(); return true; } if (matchHotKey(window.siyuan.config.keymap.editor.general.redo.custom, event)) { protyle.undo.redo(protyle); event.preventDefault(); return true; } return false; }; const fileTreeKeydown = (app: App, event: KeyboardEvent) => { const dockFile = getDockByType("file"); if (!dockFile) { return false; } const files = dockFile.data.file as Files; if (matchHotKey(window.siyuan.config.keymap.general.selectOpen1.custom, event)) { event.preventDefault(); const element = document.querySelector(".layout__wnd--active > .fn__flex > .layout-tab-bar > .item--focus") || document.querySelector("ul.layout-tab-bar > .item--focus"); if (element) { const tab = getInstanceById(element.getAttribute("data-id")) as Tab; if (tab && tab.model instanceof Editor) { tab.model.editor.protyle.wysiwyg.element.blur(); tab.model.editor.protyle.title.editElement.blur(); files.selectItem(tab.model.editor.protyle.notebookId, tab.model.editor.protyle.path); } } dockFile.toggleModel("file", true); return; } if (!files.element.parentElement.classList.contains("layout__tab--active")) { return false; } let matchCommand = false; app.plugins.find(item => { item.commands.find(command => { if (command.fileTreeCallback && matchHotKey(command.customHotkey, event)) { matchCommand = true; command.fileTreeCallback(files); return true; } }); if (matchCommand) { return true; } }); if (matchCommand) { return true; } const liElements = Array.from(files.element.querySelectorAll(".b3-list-item--focus")); if (liElements.length === 0) { if (event.key.startsWith("Arrow") && isNotCtrl(event)) { const liElement = files.element.querySelector(".b3-list-item"); if (liElement) { liElement.classList.add("b3-list-item--focus"); } event.preventDefault(); } return false; } const topULElement = hasTopClosestByTag(liElements[0], "UL"); if (!topULElement) { return false; } const notebookId = topULElement.getAttribute("data-url"); const pathString = liElements[0].getAttribute("data-path"); const isFile = liElements[0].getAttribute("data-type") === "navigation-file"; if (matchHotKey(window.siyuan.config.keymap.editor.general.spaceRepetition.custom, event)) { if (isFile) { const id = liElements[0].getAttribute("data-node-id"); fetchPost("/api/riff/getTreeRiffDueCards", {rootID: id}, (response) => { openCardByData(app, response.data, "doc", id, getDisplayName(liElements[0].getAttribute("data-name"), false, true)); }); } else { fetchPost("/api/riff/getNotebookRiffDueCards", {notebook: notebookId}, (response) => { openCardByData(app, response.data, "notebook", notebookId, getNotebookName(notebookId)); }); } } if (matchHotKey(window.siyuan.config.keymap.editor.general.quickMakeCard.custom, event)) { const blockIDs: string[] = []; liElements.forEach(item => { const id = item.getAttribute("data-node-id"); if (id) { blockIDs.push(id); } }); if (blockIDs.length > 0) { transaction(undefined, [{ action: "addFlashcards", deckID: Constants.QUICK_DECK_ID, blockIDs, }], [{ action: "removeFlashcards", deckID: Constants.QUICK_DECK_ID, blockIDs, }]); } event.preventDefault(); return true; } if (matchHotKey(window.siyuan.config.keymap.editor.general.rename.custom, event)) { window.siyuan.menus.menu.remove(); rename({ notebookId, path: pathString, name: isFile ? getDisplayName(liElements[0].getAttribute("data-name"), false, true) : getNotebookName(notebookId), type: isFile ? "file" : "notebook", }); event.preventDefault(); return true; } if (matchHotKey("⌘/", event)) { const liRect = liElements[0].getBoundingClientRect(); if (isFile) { initFileMenu(app, notebookId, pathString, liElements[0]).popup({ x: liRect.right - 15, y: liRect.top + 15 }); } else { initNavigationMenu(app, liElements[0] as HTMLElement).popup({x: liRect.right - 15, y: liRect.top + 15}); } return true; } if (isFile && matchHotKey(window.siyuan.config.keymap.general.move.custom, event)) { window.siyuan.menus.menu.remove(); const pathes = getTopPaths(liElements); movePathTo((toPath, toNotebook) => { moveToPath(pathes, toNotebook[0], toPath[0]); }, pathes); event.preventDefault(); return true; } if (isFile && matchHotKey(window.siyuan.config.keymap.editor.general.insertRight.custom, event)) { window.siyuan.menus.menu.remove(); openFileById({ app, id: liElements[0].getAttribute("data-node-id"), action: [Constants.CB_GET_FOCUS], position: "right", }); event.preventDefault(); return true; } let searchKey = ""; if (matchHotKey(window.siyuan.config.keymap.general.replace.custom, event)) { searchKey = Constants.DIALOG_REPLACE; } else if (matchHotKey(window.siyuan.config.keymap.general.search.custom, event)) { searchKey = Constants.DIALOG_SEARCH; } if (searchKey) { window.siyuan.menus.menu.remove(); if (isFile) { openSearch({ app, hotkey: searchKey, notebookId: notebookId, searchPath: getDisplayName(pathString, false, true) }); } else { openSearch({ app, hotkey: searchKey, notebookId: notebookId, }); } event.preventDefault(); return true; } const target = event.target as HTMLElement; if (["INPUT", "TEXTAREA"].includes(target.tagName) || hasClosestByAttribute(target, "contenteditable", null) || hasClosestByClassName(target, "protyle", true)) { return false; } if (event.shiftKey) { if (event.key === "ArrowUp") { const startEndElement = getStartEndElement(liElements); let previousElement: Element; if (startEndElement.startElement.getBoundingClientRect().top >= startEndElement.endElement.getBoundingClientRect().top) { previousElement = getPreviousFileLi(startEndElement.endElement) as Element; if (previousElement) { previousElement.classList.add("b3-list-item--focus"); previousElement.setAttribute("select-end", "true"); startEndElement.endElement.removeAttribute("select-end"); } } else { startEndElement.endElement.classList.remove("b3-list-item--focus"); startEndElement.endElement.removeAttribute("select-end"); previousElement = getPreviousFileLi(startEndElement.endElement) as Element; if (previousElement) { previousElement.setAttribute("select-end", "true"); } } if (previousElement) { const previousRect = previousElement.getBoundingClientRect(); const fileRect = files.element.getBoundingClientRect(); if (previousRect.top < fileRect.top || previousRect.bottom > fileRect.bottom) { previousElement.scrollIntoView(previousRect.top < fileRect.top); } } } else if (event.key === "ArrowDown") { const startEndElement = getStartEndElement(liElements); let nextElement: Element; if (startEndElement.startElement.getBoundingClientRect().top <= startEndElement.endElement.getBoundingClientRect().top) { nextElement = getNextFileLi(startEndElement.endElement) as Element; if (nextElement) { nextElement.classList.add("b3-list-item--focus"); nextElement.setAttribute("select-end", "true"); startEndElement.endElement.removeAttribute("select-end"); } } else { startEndElement.endElement.classList.remove("b3-list-item--focus"); startEndElement.endElement.removeAttribute("select-end"); nextElement = getNextFileLi(startEndElement.endElement) as Element; if (nextElement) { nextElement.setAttribute("select-end", "true"); } } if (nextElement) { const nextRect = nextElement.getBoundingClientRect(); const fileRect = files.element.getBoundingClientRect(); if (nextRect.top < fileRect.top || nextRect.bottom > fileRect.bottom) { nextElement.scrollIntoView(nextRect.top < fileRect.top); } } } return; } else if (isNotCtrl(event)) { files.element.querySelector('[select-end="true"]')?.removeAttribute("select-end"); files.element.querySelector('[select-start="true"]')?.removeAttribute("select-start"); if ((event.key === "ArrowRight" && !liElements[0].querySelector(".b3-list-item__arrow--open") && !liElements[0].querySelector(".b3-list-item__toggle").classList.contains("fn__hidden")) || (event.key === "ArrowLeft" && liElements[0].querySelector(".b3-list-item__arrow--open"))) { files.getLeaf(liElements[0], notebookId); liElements.forEach((item, index) => { if (index !== 0) { item.classList.remove("b3-list-item--focus"); } }); event.preventDefault(); return true; } if (event.key === "ArrowLeft") { let parentElement = liElements[0].parentElement.previousElementSibling; if (parentElement) { if (parentElement.tagName !== "LI") { parentElement = files.element.querySelector(".b3-list-item"); } liElements.forEach((item) => { item.classList.remove("b3-list-item--focus"); }); parentElement.classList.add("b3-list-item--focus"); const parentRect = parentElement.getBoundingClientRect(); const fileRect = files.element.getBoundingClientRect(); if (parentRect.top < fileRect.top || parentRect.bottom > fileRect.bottom) { parentElement.scrollIntoView(parentRect.top < fileRect.top); } } event.preventDefault(); return true; } if (event.key === "ArrowDown" || event.key === "ArrowRight") { let nextElement = liElements[0]; while (nextElement) { if (nextElement.nextElementSibling) { if (nextElement.nextElementSibling.tagName === "UL") { nextElement = nextElement.nextElementSibling.firstElementChild; } else { nextElement = nextElement.nextElementSibling; } break; } else { if (nextElement.parentElement.classList.contains("fn__flex-1")) { break; } else { nextElement = nextElement.parentElement; } } } if (nextElement.classList.contains("b3-list-item")) { liElements.forEach((item) => { item.classList.remove("b3-list-item--focus"); }); nextElement.classList.add("b3-list-item--focus"); const nextRect = nextElement.getBoundingClientRect(); const fileRect = files.element.getBoundingClientRect(); if (nextRect.top < fileRect.top || nextRect.bottom > fileRect.bottom) { nextElement.scrollIntoView(nextRect.top < fileRect.top); } } event.preventDefault(); return true; } if (event.key === "ArrowUp") { let previousElement = liElements[0]; while (previousElement) { if (previousElement.previousElementSibling) { if (previousElement.previousElementSibling.tagName === "LI") { previousElement = previousElement.previousElementSibling; } else { const liElements = previousElement.previousElementSibling.querySelectorAll(".b3-list-item"); previousElement = liElements[liElements.length - 1]; } break; } else { if (previousElement.parentElement.classList.contains("fn__flex-1")) { break; } else { previousElement = previousElement.parentElement; } } } if (previousElement.classList.contains("b3-list-item")) { liElements.forEach((item) => { item.classList.remove("b3-list-item--focus"); }); previousElement.classList.add("b3-list-item--focus"); const previousRect = previousElement.getBoundingClientRect(); const fileRect = files.element.getBoundingClientRect(); if (previousRect.top < fileRect.top || previousRect.bottom > fileRect.bottom) { previousElement.scrollIntoView(previousRect.top < fileRect.top); } } event.preventDefault(); return true; } } if (event.key === "Delete" || (event.key === "Backspace" && isMac())) { window.siyuan.menus.menu.remove(); deleteFiles(liElements); return true; } if (event.key === "Enter") { window.siyuan.menus.menu.remove(); liElements.forEach(item => { if (item.getAttribute("data-type") === "navigation-file") { openFileById({app, id: item.getAttribute("data-node-id"), action: [Constants.CB_GET_FOCUS]}); } else { const itemTopULElement = hasTopClosestByTag(item, "UL"); if (itemTopULElement) { files.getLeaf(item, itemTopULElement.getAttribute("data-url")); } } }); return true; } }; const panelTreeKeydown = (app: App, event: KeyboardEvent) => { // 面板折叠展开操作 const target = event.target as HTMLElement; if (["INPUT", "TEXTAREA"].includes(target.tagName) || hasClosestByAttribute(target, "contenteditable", null) || hasClosestByClassName(target, "protyle", true)) { return false; } let activePanelElement = document.querySelector(".layout__tab--active"); if (!activePanelElement) { Array.from(document.querySelectorAll(".layout__wnd--active .layout-tab-container > div")).find(item => { if (!item.classList.contains("fn__none") && item.className.indexOf("sy__") > -1) { activePanelElement = item; return true; } }); } if (!activePanelElement) { return false; } if (activePanelElement.className.indexOf("sy__") === -1) { return false; } let matchCommand = false; app.plugins.find(item => { item.commands.find(command => { if (command.dockCallback && matchHotKey(command.customHotkey, event)) { matchCommand = true; command.dockCallback(activePanelElement as HTMLElement); return true; } }); if (matchCommand) { return true; } }); if (matchCommand) { return true; } if (!matchHotKey(window.siyuan.config.keymap.editor.general.collapse.custom, event) && !matchHotKey(window.siyuan.config.keymap.editor.general.expand.custom, event) && !event.key.startsWith("Arrow") && event.key !== "Enter") { return false; } if (!event.repeat && matchHotKey(window.siyuan.config.keymap.editor.general.collapse.custom, event)) { const collapseElement = activePanelElement.querySelector('.block__icon[data-type="collapse"]'); if (collapseElement) { collapseElement.dispatchEvent(new CustomEvent("click")); event.preventDefault(); return true; } } if (!event.repeat && matchHotKey(window.siyuan.config.keymap.editor.general.expand.custom, event)) { const expandElement = activePanelElement.querySelector('.block__icon[data-type="expand"]'); if (expandElement) { expandElement.dispatchEvent(new CustomEvent("click")); event.preventDefault(); return true; } } if (activePanelElement.classList.contains("sy__inbox") || activePanelElement.classList.contains("sy__globalGraph") || activePanelElement.classList.contains("sy__graph")) { return false; } const model = (getInstanceById(activePanelElement.getAttribute("data-id"), window.siyuan.layout.layout) as Tab)?.model; if (!model) { return false; } let activeItemElement = activePanelElement.querySelector(".b3-list-item--focus"); if (!activeItemElement) { activeItemElement = activePanelElement.querySelector(".b3-list .b3-list-item"); if (activeItemElement) { activeItemElement.classList.add("b3-list-item--focus"); } return false; } let tree = (model as Backlink).tree; if (activeItemElement.parentElement.parentElement.classList.contains("backlinkMList")) { tree = (model as Backlink).mTree; } if (!tree) { return false; } if (event.key === "Enter") { tree.click(activeItemElement); event.preventDefault(); return true; } const arrowElement = activeItemElement.querySelector(".b3-list-item__arrow"); if ((event.key === "ArrowRight" && !arrowElement.classList.contains("b3-list-item__arrow--open") && !arrowElement.parentElement.classList.contains("fn__hidden")) || (event.key === "ArrowLeft" && arrowElement.classList.contains("b3-list-item__arrow--open") && !arrowElement.parentElement.classList.contains("fn__hidden"))) { tree.toggleBlocks(activeItemElement); event.preventDefault(); return true; } const ulElement = hasClosestByClassName(activeItemElement, "b3-list"); if (!ulElement) { return false; } if (event.key === "ArrowLeft") { let parentElement = activeItemElement.parentElement.previousElementSibling; if (parentElement) { if (parentElement.tagName !== "LI") { parentElement = ulElement.querySelector(".b3-list-item"); } activeItemElement.classList.remove("b3-list-item--focus"); parentElement.classList.add("b3-list-item--focus"); const parentRect = parentElement.getBoundingClientRect(); const scrollRect = ulElement.parentElement.getBoundingClientRect(); if (parentRect.top < scrollRect.top || parentRect.bottom > scrollRect.bottom) { parentElement.scrollIntoView(parentRect.top < scrollRect.top); } } event.preventDefault(); return true; } if (event.key === "ArrowDown" || event.key === "ArrowRight") { let nextElement = activeItemElement; while (nextElement) { if (nextElement.nextElementSibling) { if (nextElement.nextElementSibling.tagName === "UL") { if (nextElement.nextElementSibling.classList.contains("fn__none")) { // 遇到折叠内容 if (nextElement.nextElementSibling.nextElementSibling) { nextElement = nextElement.nextElementSibling.nextElementSibling; } } else { nextElement = nextElement.nextElementSibling.firstElementChild; } } else if (nextElement.nextElementSibling.classList.contains("protyle")) { // backlink if (nextElement.nextElementSibling.nextElementSibling) { nextElement = nextElement.nextElementSibling.nextElementSibling; } } else { nextElement = nextElement.nextElementSibling; } break; } else { if (nextElement.parentElement.classList.contains("fn__flex-1")) { break; } else { nextElement = nextElement.parentElement; } } } if (nextElement.classList.contains("b3-list-item") && !nextElement.classList.contains("b3-list-item--focus")) { activeItemElement.classList.remove("b3-list-item--focus"); nextElement.classList.add("b3-list-item--focus"); const nextRect = nextElement.getBoundingClientRect(); const scrollRect = ulElement.parentElement.getBoundingClientRect(); if (nextRect.top < scrollRect.top || nextRect.bottom > scrollRect.bottom) { nextElement.scrollIntoView(nextRect.top < scrollRect.top); } } event.preventDefault(); return true; } if (event.key === "ArrowUp") { let previousElement = activeItemElement; while (previousElement) { if (previousElement.previousElementSibling) { if (previousElement.previousElementSibling.tagName === "LI") { previousElement = previousElement.previousElementSibling; } else if (previousElement.previousElementSibling.classList.contains("protyle")) { if (previousElement.previousElementSibling.previousElementSibling) { previousElement = previousElement.previousElementSibling.previousElementSibling; } } else if (previousElement.previousElementSibling.tagName === "UL" && previousElement.previousElementSibling.classList.contains("fn__none")) { // 遇到折叠内容 if (previousElement.previousElementSibling.previousElementSibling) { previousElement = previousElement.previousElementSibling.previousElementSibling; } } else { const liElements = previousElement.previousElementSibling.querySelectorAll(".b3-list-item"); previousElement = liElements[liElements.length - 1]; } break; } else { if (previousElement.parentElement.classList.contains("fn__flex-1")) { break; } else { previousElement = previousElement.parentElement; } } } if (previousElement.classList.contains("b3-list-item") && !previousElement.classList.contains("b3-list-item--focus")) { activeItemElement.classList.remove("b3-list-item--focus"); previousElement.classList.add("b3-list-item--focus"); const previousRect = previousElement.getBoundingClientRect(); const scrollRect = ulElement.parentElement.getBoundingClientRect(); if (previousRect.top < scrollRect.top || previousRect.bottom > scrollRect.bottom) { previousElement.scrollIntoView(previousRect.top < scrollRect.top); } } event.preventDefault(); return true; } return false; }; let switchDialog: Dialog; export const windowKeyDown = (app: App, event: KeyboardEvent) => { // https://github.com/siyuan-note/siyuan/issues/9848 忘记为什么要阻止了 .av__mask 的情况,测了下没问题就先移除 if (document.getElementById("progress") || document.getElementById("errorLog") || event.isComposing) { return; } const target = event.target as HTMLElement; if (isNotCtrl(event) && !event.shiftKey && !event.altKey && !["INPUT", "TEXTAREA"].includes(target.tagName) && ["0", "1", "2", "3", "4", "j", "k", "l", ";", "s", " ", "p", "enter", "a", "s", "d", "f", "q", "x"].includes(event.key.toLowerCase())) { let cardElement: Element; window.siyuan.dialogs.find(item => { if (item.element.getAttribute("data-key") === Constants.DIALOG_OPENCARD) { cardElement = item.element; return true; } }); if (!cardElement) { cardElement = document.querySelector(`.layout__wnd--active div[data-key="${Constants.DIALOG_OPENCARD}"]:not(.fn__none)`); } if (cardElement) { event.preventDefault(); cardElement.dispatchEvent(new CustomEvent("click", {detail: event.key.toLowerCase()})); return; } } // 仅处理以下快捷键操作 if (isNotCtrl(event) && event.key !== "Escape" && !event.shiftKey && !event.altKey && Constants.KEYCODELIST[event.keyCode] !== "PageUp" && Constants.KEYCODELIST[event.keyCode] !== "PageDown" && event.key !== "Home" && event.key !== "End" && !/^F\d{1,2}$/.test(event.key) && event.key.indexOf("Arrow") === -1 && event.key !== "Enter" && event.key !== "Backspace" && event.key !== "Delete") { return; } if (!event.altKey && !event.shiftKey && isOnlyMeta(event)) { if ((isMac() ? event.key === "Meta" : event.key === "Control") || isOnlyMeta(event)) { window.siyuan.ctrlIsPressed = true; if ((event.key === "Meta" || event.key === "Control") && window.siyuan.config.editor.floatWindowMode === 1 && !event.repeat) { showPopover(app); } } else { window.siyuan.ctrlIsPressed = false; } } if (!event.altKey && event.shiftKey && isNotCtrl(event)) { if (event.key === "Shift") { window.siyuan.shiftIsPressed = true; if (!event.repeat) { showPopover(app, true); } } else { window.siyuan.shiftIsPressed = false; } } if (event.altKey && !event.shiftKey && isNotCtrl(event)) { if (event.key === "Alt") { window.siyuan.altIsPressed = true; } else { window.siyuan.altIsPressed = false; } } if (switchDialog && (matchAuxiliaryHotKey(window.siyuan.config.keymap.general.goToEditTabNext.custom, event) || matchAuxiliaryHotKey(window.siyuan.config.keymap.general.goToEditTabPrev.custom, event)) && event.key.startsWith("Arrow")) { dialogArrow(app, switchDialog.element, event); return; } if (searchKeydown(app, event)) { event.preventDefault(); event.stopPropagation(); return; } const isTabWindow = isWindow(); if (matchHotKey(window.siyuan.config.keymap.general.goToEditTabNext.custom, event) || matchHotKey(window.siyuan.config.keymap.general.goToEditTabPrev.custom, event)) { if (switchDialog && switchDialog.element.parentElement) { return; } let tabHtml = ""; let currentTabElement = document.querySelector(".layout__wnd--active ul.layout-tab-bar > .item--focus"); if (!currentTabElement) { currentTabElement = document.querySelector("ul.layout-tab-bar > .item--focus"); } if (currentTabElement) { const currentId = currentTabElement.getAttribute("data-id"); getAllTabs().sort((itemA, itemB) => { return itemA.headElement.getAttribute("data-activetime") > itemB.headElement.getAttribute("data-activetime") ? -1 : 1; }).forEach((item, index) => { let icon = ``; let rootId = ""; const initData = item.headElement.getAttribute("data-initdata"); if (item.model instanceof Editor) { rootId = ` data-node-id="${item.model.editor.protyle.block.rootID}"`; icon = unicode2Emoji(item.docIcon || Constants.SIYUAN_IMAGE_FILE, "b3-list-item__graphic", true); } else if (initData) { const initDataObj = JSON.parse(initData); if (initDataObj.instance === "Editor") { rootId = ` data-node-id="${initDataObj.rootId}"`; icon = unicode2Emoji(item.docIcon || Constants.SIYUAN_IMAGE_FILE, "b3-list-item__graphic", true); } } tabHtml += `