diff --git a/app/src/boot/globalEvent/searchKeydown.ts b/app/src/boot/globalEvent/searchKeydown.ts index 2fa1280ef..7476a979f 100644 --- a/app/src/boot/globalEvent/searchKeydown.ts +++ b/app/src/boot/globalEvent/searchKeydown.ts @@ -3,21 +3,20 @@ import * as path from "path"; /// #endif import {matchHotKey} from "../../protyle/util/hotKey"; import {fetchPost} from "../../util/fetch"; -import {openFileById} from "../../editor/util"; import {Constants} from "../../constants"; import {newFileByName} from "../../util/newFile"; import {App} from "../../index"; import {Dialog} from "../../dialog"; import {getAllModels} from "../../layout/getAll"; import {hasClosestByClassName} from "../../protyle/util/hasClosest"; -import {getArticle, inputEvent, replace} from "../../search/util"; +import {getArticle, inputEvent, openSearchEditor, replace} from "../../search/util"; import {useShell} from "../../util/pathName"; import {assetInputEvent, renderPreview} from "../../search/assets"; import {initSearchMenu} from "../../menus/search"; import {writeText} from "../../protyle/util/compatibility"; -import {checkFold} from "../../util/noRelyPCFunction"; import {getUnRefList} from "../../search/unRef"; import {toggleAssetHistory, toggleReplaceHistory, toggleSearchHistory} from "../../search/toggleHistory"; +import {Protyle} from "../../protyle"; export const searchKeydown = (app: App, event: KeyboardEvent) => { if (getSelection().rangeCount === 0) { @@ -29,7 +28,7 @@ export const searchKeydown = (app: App, event: KeyboardEvent) => { } let element: HTMLElement; let dialog: Dialog; - let edit; + let edit: Protyle; let unRefEdit; let config: Config.IUILayoutTabSearchConfig; window.siyuan.dialogs.find((item) => { @@ -98,20 +97,15 @@ export const searchKeydown = (app: App, event: KeyboardEvent) => { } if (searchType !== "asset") { if (matchHotKey(window.siyuan.config.keymap.editor.general.insertRight.custom, event)) { - const id = currentList.getAttribute("data-node-id"); - checkFold(id, (zoomIn) => { - openFileById({ - app, - id, - position: "right", - action: zoomIn ? [Constants.CB_GET_FOCUS, Constants.CB_GET_ALL, Constants.CB_GET_HL] : - [Constants.CB_GET_FOCUS, Constants.CB_GET_CONTEXT, Constants.CB_GET_HL], - zoomIn, - scrollPosition: "center" - }); - if (dialog) { - dialog.destroy({focus: "false"}); - } + openSearchEditor({ + protyle: edit.protyle, + id: currentList.getAttribute("data-node-id"), + cb: () => { + if (dialog) { + dialog.destroy({focus: "false"}); + } + }, + openPosition: "right", }); return true; } @@ -222,19 +216,14 @@ export const searchKeydown = (app: App, event: KeyboardEvent) => { if (targetId === "replaceInput") { replace(element, config, edit, false); } else { - const id = currentList.getAttribute("data-node-id"); - checkFold(id, (zoomIn) => { - openFileById({ - app, - id, - action: zoomIn ? [Constants.CB_GET_FOCUS, Constants.CB_GET_ALL, Constants.CB_GET_HL] : - [Constants.CB_GET_FOCUS, Constants.CB_GET_CONTEXT, Constants.CB_GET_HL], - zoomIn, - scrollPosition: "center" - }); - if (dialog) { - dialog.destroy({focus: "false"}); - } + openSearchEditor({ + protyle: edit.protyle, + id: currentList.getAttribute("data-node-id"), + cb: () => { + if (dialog) { + dialog.destroy({focus: "false"}); + } + }, }); } } else { diff --git a/app/src/constants.ts b/app/src/constants.ts index de1c852c8..4a377e959 100644 --- a/app/src/constants.ts +++ b/app/src/constants.ts @@ -130,6 +130,7 @@ export abstract class Constants { public static readonly CB_GET_BACKLINK = "cb-get-backlink"; // 悬浮窗为传递型需展示上下文 public static readonly CB_GET_UNUNDO = "cb-get-unundo"; // 不需要记录历史 public static readonly CB_GET_SCROLL = "cb-get-scroll"; // 滚动到指定位置,用于直接打开文档,必有 rootID + public static readonly CB_GET_SEARCH = "cb-get-search"; // 通过搜索打开 public static readonly CB_GET_CONTEXT = "cb-get-context"; // 包含上下文 public static readonly CB_GET_ROOTSCROLL = "cb-get-rootscroll"; // 如果为 rootID 就滚动到指定位置,必有 rootID public static readonly CB_GET_HTML = "cb-get-html"; // 直接渲染,不需要再 /api/block/getDocInfo,否则搜索表格无法定位 diff --git a/app/src/editor/util.ts b/app/src/editor/util.ts index d30befbb6..c99ef1c8e 100644 --- a/app/src/editor/util.ts +++ b/app/src/editor/util.ts @@ -10,7 +10,7 @@ import {Constants} from "../constants"; import {setEditMode} from "../protyle/util/setEditMode"; import {Files} from "../layout/dock/Files"; import {fetchPost, fetchSyncPost} from "../util/fetch"; -import {focusBlock, focusByRange} from "../protyle/util/selection"; +import {focusBlock, focusByOffset, focusByRange} from "../protyle/util/selection"; import {onGet} from "../protyle/util/onGet"; /// #if !BROWSER import {ipcRenderer} from "electron"; @@ -398,9 +398,14 @@ const switchEditor = (editor: Editor, options: IOpenFileOptions, allModels: IMod } if (options.action?.includes(Constants.CB_GET_FOCUS)) { if (nodeElement) { - const newRange = focusBlock(nodeElement, undefined, !options.action?.includes(Constants.CB_GET_OUTLINE)); - if (newRange) { - editor.editor.protyle.toolbar.range = newRange; + if (options.action.includes(Constants.CB_GET_SEARCH)) { + const scrollAttr = window.siyuan.storage[Constants.LOCAL_FILEPOSITION][editor.editor.protyle.block.rootID]; + focusByOffset(nodeElement, scrollAttr.focusStart, scrollAttr.focusEnd); + } else { + const newRange = focusBlock(nodeElement, undefined, !options.action?.includes(Constants.CB_GET_OUTLINE)); + if (newRange) { + editor.editor.protyle.toolbar.range = newRange; + } } scrollCenter(editor.editor.protyle, (editor.editor.protyle.disabled || options.scrollPosition) ? nodeElement : null, options.scrollPosition); editor.editor.protyle.observerLoad = new ResizeObserver(() => { diff --git a/app/src/protyle/scroll/saveScroll.ts b/app/src/protyle/scroll/saveScroll.ts index f4a69cf05..5ce34180c 100644 --- a/app/src/protyle/scroll/saveScroll.ts +++ b/app/src/protyle/scroll/saveScroll.ts @@ -85,6 +85,7 @@ export const getDocByScroll = (options: { highlight: !isSupportCSSHL(), }, response => { onGet({ + scrollPosition: options.mergedOptions.scrollPosition, data: response, protyle: options.protyle, action: actions, @@ -98,6 +99,7 @@ export const getDocByScroll = (options: { } else { actions.push(Constants.CB_GET_ALL); onGet({ + scrollPosition: options.mergedOptions.scrollPosition, data: response, protyle: options.protyle, action: actions, @@ -121,6 +123,7 @@ export const getDocByScroll = (options: { highlight: !isSupportCSSHL(), }, response => { onGet({ + scrollPosition: options.mergedOptions.scrollPosition, data: response, protyle: options.protyle, action: actions, diff --git a/app/src/search/util.ts b/app/src/search/util.ts index 5033cd6c2..eb03a2eca 100644 --- a/app/src/search/util.ts +++ b/app/src/search/util.ts @@ -15,7 +15,7 @@ import {onGet} from "../protyle/util/onGet"; import {addLoading} from "../protyle/ui/initUI"; import {getIconByType} from "../editor/getIcon"; import {unicode2Emoji} from "../emoji"; -import {hasClosestByClassName, hasClosestByTag} from "../protyle/util/hasClosest"; +import {hasClosestBlock, hasClosestByClassName, hasClosestByTag} from "../protyle/util/hasClosest"; import {isIPad, isNotCtrl, setStorageVal, updateHotkeyTip} from "../protyle/util/compatibility"; import {newFileByName} from "../util/newFile"; import { @@ -45,6 +45,7 @@ import {getDefaultType} from "./getDefault"; import {isSupportCSSHL, searchMarkRender} from "../protyle/render/searchMarkRender"; import {saveKeyList, toggleAssetHistory, toggleReplaceHistory, toggleSearchHistory} from "./toggleHistory"; import {highlightById} from "../util/highlightById"; +import {getSelectionOffset} from "../protyle/util/selection"; export const openGlobalSearch = (app: App, text: string, replace: boolean, searchData?: Config.IUILayoutTabSearchConfig) => { text = text.trim(); @@ -814,20 +815,11 @@ export const genSearch = (app: App, config: Config.IUILayoutTabSearchConfig, ele } } else { if (event.altKey) { - const id = target.getAttribute("data-node-id"); - checkFold(id, (zoomIn) => { - openFileById({ - app, - id, - action: zoomIn ? [Constants.CB_GET_FOCUS, Constants.CB_GET_ALL, Constants.CB_GET_HL] : - [Constants.CB_GET_FOCUS, Constants.CB_GET_CONTEXT, Constants.CB_GET_HL], - zoomIn, - position: "right", - scrollPosition: "center" - }); - if (closeCB) { - closeCB(); - } + openSearchEditor({ + protyle: edit.protyle, + id: target.getAttribute("data-node-id"), + cb: closeCB, + openPosition: "right", }); } else if (!target.classList.contains("b3-list-item--focus")) { (searchType === "doc" ? searchPanelElement : unRefPanelElement).querySelector(".b3-list-item--focus").classList.remove("b3-list-item--focus"); @@ -856,19 +848,10 @@ export const genSearch = (app: App, config: Config.IUILayoutTabSearchConfig, ele useShell("showItemInFolder", path.join(window.siyuan.config.system.dataDir, target.lastElementChild.getAttribute("aria-label"))); /// #endif } else { - const id = target.getAttribute("data-node-id"); - checkFold(id, (zoomIn) => { - openFileById({ - app, - id, - action: zoomIn ? [Constants.CB_GET_FOCUS, Constants.CB_GET_ALL, Constants.CB_GET_HL] : - [Constants.CB_GET_FOCUS, Constants.CB_GET_CONTEXT, Constants.CB_GET_HL], - zoomIn, - scrollPosition: "center" - }); - if (closeCB) { - closeCB(); - } + openSearchEditor({ + protyle: edit.protyle, + id: target.getAttribute("data-node-id"), + cb: closeCB }); } } @@ -925,6 +908,45 @@ export const genSearch = (app: App, config: Config.IUILayoutTabSearchConfig, ele return {edit, unRefEdit}; }; +export const openSearchEditor = (options: { + protyle: IProtyle, + openPosition?: string, + id?: string, + cb?: () => void +}) => { + const currentRange = options.protyle.highlight.ranges[options.protyle.highlight.rangeIndex]; + if (currentRange) { + const rangeBlockElement = hasClosestBlock(currentRange.startContainer); + if (rangeBlockElement) { + options.id = rangeBlockElement.getAttribute("data-node-id"); + const offset = getSelectionOffset(rangeBlockElement, null, options.protyle.highlight.ranges[options.protyle.highlight.rangeIndex]); + const scrollAttr: IScrollAttr = { + rootId: options.protyle.block.rootID, + focusId: options.id, + focusStart: offset.start, + focusEnd: offset.end, + zoomInId: options.protyle.block.showAll ? options.protyle.block.id : undefined + }; + window.siyuan.storage[Constants.LOCAL_FILEPOSITION][options.protyle.block.rootID] = scrollAttr; + } + } + checkFold(options.id, (zoomIn) => { + openFileById({ + app: options.protyle.app, + id:options.id, + action: currentRange ? + (zoomIn ? [Constants.CB_GET_FOCUS, Constants.CB_GET_ALL, Constants.CB_GET_SCROLL, Constants.CB_GET_SEARCH] : [Constants.CB_GET_FOCUS, Constants.CB_GET_CONTEXT, Constants.CB_GET_SCROLL, Constants.CB_GET_SEARCH]) : + (zoomIn ? [Constants.CB_GET_FOCUS, Constants.CB_GET_ALL, Constants.CB_GET_HL] : [Constants.CB_GET_FOCUS, Constants.CB_GET_CONTEXT, Constants.CB_GET_HL]), + zoomIn, + position: options.openPosition, + scrollPosition: "center" + }); + if (options.cb) { + options.cb(); + } + }); +}; + export const genQueryHTML = (method: number, id: string) => { let methodTip = ""; let methodIcon = ""; diff --git a/app/src/types/protyle.d.ts b/app/src/types/protyle.d.ts index 2de0a36a1..b0dfbd273 100644 --- a/app/src/types/protyle.d.ts +++ b/app/src/types/protyle.d.ts @@ -34,6 +34,7 @@ type TProtyleAction = "cb-get-append" | // 向下滚动加载 "cb-get-backlink" | // 悬浮窗为传递型需展示上下文 "cb-get-unundo" | // 不需要记录历史 "cb-get-scroll" | // 滚动到指定位置,用于直接打开文档,必有 rootID + "cb-get-search" | // 使用搜索打开搜索 "cb-get-context" | // 包含上下文 "cb-get-rootscroll" | // 如果为 rootID 就滚动到指定位置,必有 rootID "cb-get-html" | // 直接渲染,不需要再 /api/block/getDocInfo,否则搜索表格无法定位 @@ -343,9 +344,9 @@ interface IUpload { interface IScrollAttr { rootId: string, - startId: string, - endId: string - scrollTop: number, + startId?: string, + endId?: string + scrollTop?: number, focusId?: string, focusStart?: number focusEnd?: number