diff --git a/app/src/assets/scss/component/_form.scss b/app/src/assets/scss/component/_form.scss index ce997c6a0..ef175612f 100644 --- a/app/src/assets/scss/component/_form.scss +++ b/app/src/assets/scss/component/_form.scss @@ -19,6 +19,15 @@ width: 16px; } + &-list { + height: 28px; + width: 42px; + display: flex; + justify-content: center; + align-items: center; + position: absolute; + } + &-input { padding-left: 35px !important; } diff --git a/app/src/constants.ts b/app/src/constants.ts index 6107173a0..5ca1697b8 100644 --- a/app/src/constants.ts +++ b/app/src/constants.ts @@ -124,6 +124,7 @@ export abstract class Constants { public static readonly LOCAL_PLUGIN_DOCKS = "local-plugin-docks"; public static readonly LOCAL_IMAGES = "local-images"; public static readonly LOCAL_EMOJIS = "local-emojis"; + public static readonly LOCAL_MOVE_PATH = "local-move-path"; // dialog public static readonly DIALOG_CONFIRM = "dialog-confirm"; diff --git a/app/src/protyle/render/av/addToDatabase.ts b/app/src/protyle/render/av/addToDatabase.ts index 3254f7244..060f86861 100644 --- a/app/src/protyle/render/av/addToDatabase.ts +++ b/app/src/protyle/render/av/addToDatabase.ts @@ -34,7 +34,7 @@ export const addFilesToDatabase = (fileLiElements: Element[]) => { }; export const addEditorToDatabase = (protyle: IProtyle, range: Range, type?: string) => { - if (protyle.title?.editElement?.contains(range.startContainer) || type === "title") { + if ((range && protyle.title?.editElement?.contains(range.startContainer)) || type === "title") { openSearchAV("", protyle.breadcrumb.element, (listItemElement) => { const avID = listItemElement.dataset.avId; transaction(protyle, [{ diff --git a/app/src/protyle/util/compatibility.ts b/app/src/protyle/util/compatibility.ts index 223f6ffeb..c8c446a92 100644 --- a/app/src/protyle/util/compatibility.ts +++ b/app/src/protyle/util/compatibility.ts @@ -300,6 +300,7 @@ export const getLocalStorage = (cb: () => void) => { replaceTypes: Object.assign({}, Constants.SIYUAN_DEFAULT_REPLACETYPES), }; defaultStorage[Constants.LOCAL_ZOOM] = 1; + defaultStorage[Constants.LOCAL_MOVE_PATH] = {keys: [], k: ""}; [Constants.LOCAL_EXPORTIMG, Constants.LOCAL_SEARCHKEYS, Constants.LOCAL_PDFTHEME, Constants.LOCAL_BAZAAR, Constants.LOCAL_EXPORTWORD, Constants.LOCAL_EXPORTPDF, Constants.LOCAL_DOCINFO, Constants.LOCAL_FONTSTYLES, @@ -307,7 +308,7 @@ export const getLocalStorage = (cb: () => void) => { Constants.LOCAL_PLUGINTOPUNPIN, Constants.LOCAL_SEARCHASSET, Constants.LOCAL_FLASHCARD, Constants.LOCAL_DIALOGPOSITION, Constants.LOCAL_SEARCHUNREF, Constants.LOCAL_HISTORY, Constants.LOCAL_OUTLINE, Constants.LOCAL_FILEPOSITION, Constants.LOCAL_FILESPATHS, Constants.LOCAL_IMAGES, - Constants.LOCAL_PLUGIN_DOCKS, Constants.LOCAL_EMOJIS].forEach((key) => { + Constants.LOCAL_PLUGIN_DOCKS, Constants.LOCAL_EMOJIS, Constants.LOCAL_MOVE_PATH].forEach((key) => { if (typeof response.data[key] === "string") { try { const parseData = JSON.parse(response.data[key]); diff --git a/app/src/util/pathName.ts b/app/src/util/pathName.ts index 2565d3740..23af451b4 100644 --- a/app/src/util/pathName.ts +++ b/app/src/util/pathName.ts @@ -10,7 +10,10 @@ import {Constants} from "../constants"; import {ipcRenderer} from "electron"; /// #endif import {showMessage} from "../dialog/message"; -import {isOnlyMeta} from "../protyle/util/compatibility"; +import {isOnlyMeta, setStorageVal, updateHotkeyTip} from "../protyle/util/compatibility"; +import {matchHotKey} from "../protyle/util/hotKey"; +import {Menu} from "../plugin/Menu"; +import {hasClosestByClassName} from "../protyle/util/hasClosest"; export const showFileInFolder = (filePath: string) => { /// #if !BROWSER @@ -154,8 +157,11 @@ export const movePathTo = (cb: (toPath: string[], toNotebook: string[]) => void, title: `${title || window.siyuan.languages.move}
`, content: `
- - + + + + +
@@ -250,6 +256,75 @@ export const movePathTo = (cb: (toPath: string[], toNotebook: string[]) => void, searchListElement.innerHTML = fileHTML; }); }; + + const toggleMovePathHistory = () => { + const keys = window.siyuan.storage[Constants.LOCAL_MOVE_PATH].keys; + if (!keys || keys.length === 0) { + return; + } + const menu = new Menu("move-path-history"); + if (menu.isOpen) { + return; + } + menu.element.classList.add("b3-menu--list"); + menu.addItem({ + iconHTML: "", + label: window.siyuan.languages.clearHistory, + click() { + window.siyuan.storage[Constants.LOCAL_MOVE_PATH].keys = []; + setStorageVal(Constants.LOCAL_MOVE_PATH, window.siyuan.storage[Constants.LOCAL_MOVE_PATH]); + } + }); + const separatorElement = menu.addSeparator(1); + let current = true; + keys.forEach((s: string) => { + if (s !== inputElement.value && s) { + const menuItem = menu.addItem({ + iconHTML: "", + label: escapeHtml(s), + action: "iconCloseRound", + bind(element) { + element.addEventListener("click", (itemEvent) => { + if (hasClosestByClassName(itemEvent.target as Element, "b3-menu__action")) { + keys.find((item: string, index: number) => { + if (item === s) { + keys.splice(index, 1); + return true; + } + }); + window.siyuan.storage[Constants.LOCAL_MOVE_PATH].keys = keys; + setStorageVal(Constants.LOCAL_MOVE_PATH, window.siyuan.storage[Constants.LOCAL_MOVE_PATH]); + if (element.previousElementSibling?.classList.contains("b3-menu__separator") && !element.nextElementSibling) { + window.siyuan.menus.menu.remove(); + } else { + element.remove(); + } + } else { + inputElement.value = element.textContent; + inputEvent(); + window.siyuan.menus.menu.remove(); + } + itemEvent.preventDefault(); + itemEvent.stopPropagation(); + }); + } + }); + if (current) { + menuItem.classList.add("b3-menu__item--current"); + } + current = false; + } + }); + if (current) { + separatorElement.remove(); + } + const rect = inputElement.getBoundingClientRect(); + menu.open({ + x: rect.left, + y: rect.bottom + }); + } + inputElement.value = window.siyuan.storage[Constants.LOCAL_MOVE_PATH].k; inputEvent(); inputElement.addEventListener("compositionend", (event: InputEvent) => { inputEvent(event); @@ -257,11 +332,33 @@ export const movePathTo = (cb: (toPath: string[], toNotebook: string[]) => void, inputElement.addEventListener("input", (event: InputEvent) => { inputEvent(event); }); + inputElement.addEventListener("blur", () => { + if (!inputElement.value) { + return; + } + let list: string[] = window.siyuan.storage[Constants.LOCAL_MOVE_PATH].keys; + list.splice(0, 0, inputElement.value); + list = Array.from(new Set(list)); + if (list.length > window.siyuan.config.search.limit) { + list.splice(window.siyuan.config.search.limit, list.length - window.siyuan.config.search.limit); + } + window.siyuan.storage[Constants.LOCAL_MOVE_PATH].k = inputElement.value; + window.siyuan.storage[Constants.LOCAL_MOVE_PATH].keys = list; + setStorageVal(Constants.LOCAL_MOVE_PATH, window.siyuan.storage[Constants.LOCAL_MOVE_PATH]); + }); const lineHeight = 28; inputElement.addEventListener("keydown", (event: KeyboardEvent) => { if (event.isComposing) { return; } + if (matchHotKey("⌥↓", event)) { + event.stopPropagation(); + toggleMovePathHistory(); + return; + } + if (window.siyuan.menus.menu.element.getAttribute("data-name") === "move-path-history") { + return + } const currentPanelElement = searchListElement.classList.contains("fn__none") ? searchTreeElement : searchListElement; const currentItemElements = currentPanelElement.querySelectorAll(".b3-list-item--focus"); if (currentItemElements.length === 0) { @@ -424,6 +521,11 @@ export const movePathTo = (cb: (toPath: string[], toNotebook: string[]) => void, event.preventDefault(); event.stopPropagation(); break; + } else if (target.classList.contains("b3-form__icon-list")) { + toggleMovePathHistory(); + event.preventDefault(); + event.stopPropagation(); + break; } else if (target.classList.contains("b3-button--text")) { const currentPanelElement = searchListElement.classList.contains("fn__none") ? searchTreeElement : searchListElement; const currentItemElements = currentPanelElement.querySelectorAll(".b3-list-item--focus");