diff --git a/app/src/constants.ts b/app/src/constants.ts index 9aae21d28..f8f41d194 100644 --- a/app/src/constants.ts +++ b/app/src/constants.ts @@ -50,6 +50,7 @@ export abstract class Constants { public static readonly CB_GET_SETID = "cb-get-setid"; // 重置 blockid public static readonly CB_GET_ALL = "cb-get-all"; // 获取所有块 public static readonly CB_GET_UNUNDO = "cb-get-unundo"; // 不需要记录历史 + public static readonly CB_GET_SCROLL = "cb-get-scroll"; // 滚动到指定位置 // localstorage public static readonly LOCAL_SEARCHEDATA = "local-searchedata"; diff --git a/app/src/layout/Wnd.ts b/app/src/layout/Wnd.ts index 2a00c5999..6ba753ea2 100644 --- a/app/src/layout/Wnd.ts +++ b/app/src/layout/Wnd.ts @@ -26,6 +26,7 @@ import {getAllModels} from "./getAll"; import {fetchPost} from "../util/fetch"; import {onGet} from "../protyle/util/onGet"; import {countBlockWord} from "./status"; +import {saveScroll} from "../protyle/scroll/saveScroll"; export class Wnd { public id: string; @@ -488,6 +489,9 @@ export class Wnd { private removeTabAction = (id: string, closeAll = false) => { this.children.find((item, index) => { if (item.id === id) { + if (item.model instanceof Editor) { + saveScroll(item.model.editor.protyle); + } if (this.children.length === 1) { this.destroyModel(this.children[0].model); this.children = []; diff --git a/app/src/layout/dock/Files.ts b/app/src/layout/dock/Files.ts index 7eee40100..3c828a22c 100644 --- a/app/src/layout/dock/Files.ts +++ b/app/src/layout/dock/Files.ts @@ -235,12 +235,12 @@ export class Files extends Model { openFileById({ id: target.getAttribute("data-node-id"), position: "right", - action: [Constants.CB_GET_FOCUS] + action: [Constants.CB_GET_FOCUS, Constants.CB_GET_SCROLL] }); } else { openFileById({ id: target.getAttribute("data-node-id"), - action: [Constants.CB_GET_FOCUS] + action: [Constants.CB_GET_FOCUS, Constants.CB_GET_SCROLL] }); } } else if (target.getAttribute("data-type") === "navigation-root") { diff --git a/app/src/menus/protyle.ts b/app/src/menus/protyle.ts index 653f89ca3..1d9e61d94 100644 --- a/app/src/menus/protyle.ts +++ b/app/src/menus/protyle.ts @@ -338,7 +338,7 @@ export const contentMenu = (protyle: IProtyle, nodeElement: Element) => { } }; -export const zoomOut = (protyle: IProtyle, id: string, focusId?: string, isPushBack = true) => { +export const zoomOut = (protyle: IProtyle, id: string, focusId?: string, isPushBack = true, callback?: () => void) => { const breadcrumbHLElement = protyle.breadcrumb.element.querySelector(".protyle-breadcrumb__item--active"); if (breadcrumbHLElement && breadcrumbHLElement.getAttribute("data-node-id") === id) { if (id === protyle.block.rootID) { @@ -393,6 +393,9 @@ export const zoomOut = (protyle: IProtyle, id: string, focusId?: string, isPushB updateBacklinkGraph(getAllModels(), protyle); } /// #endif + if (callback) { + callback(); + } }); }; diff --git a/app/src/protyle/header/Title.ts b/app/src/protyle/header/Title.ts index 3212abff9..edd08c8bc 100644 --- a/app/src/protyle/header/Title.ts +++ b/app/src/protyle/header/Title.ts @@ -26,6 +26,7 @@ import {getNoContainerElement} from "../wysiwyg/getBlock"; import {commonHotkey} from "../wysiwyg/commonHotkey"; import {code160to32} from "../util/code160to32"; import {deleteFile} from "../../editor/deleteFile"; +import {restoreScroll} from "../scroll/saveScroll"; export class Title { public element: HTMLElement; @@ -324,7 +325,7 @@ ${window.siyuan.languages.createdAt} ${dayjs(response.data.ial.id.substr(0, 14)) } } - public render(protyle: IProtyle, refresh = false) { + public render(protyle: IProtyle, refresh = false, action:string[] = []) { if (this.editElement.getAttribute("data-render") === "true" && !refresh) { return; } @@ -359,6 +360,10 @@ ${window.siyuan.languages.createdAt} ${dayjs(response.data.ial.id.substr(0, 14)) range.selectNodeContents(this.editElement); focusByRange(range); } + + if (action.includes(Constants.CB_GET_SCROLL)) { + restoreScroll(protyle); + } }); } } diff --git a/app/src/protyle/scroll/saveScroll.ts b/app/src/protyle/scroll/saveScroll.ts new file mode 100644 index 000000000..4b0323025 --- /dev/null +++ b/app/src/protyle/scroll/saveScroll.ts @@ -0,0 +1,59 @@ +import {Constants} from "../../constants"; +import {hasClosestBlock} from "../util/hasClosest"; +import {focusByOffset, getSelectionOffset} from "../util/selection"; +import {fetchPost} from "../../util/fetch"; +import {zoomOut} from "../../menus/protyle"; + +export const saveScroll = (protyle: IProtyle) => { + if (protyle.contentElement.clientHeight === protyle.contentElement.scrollHeight) { + return; + } + let attr = `${protyle.wysiwyg.element.firstElementChild.getAttribute("data-node-id")}${Constants.ZWSP}${protyle.wysiwyg.element.lastElementChild.getAttribute("data-node-id")}${Constants.ZWSP}${protyle.contentElement.scrollTop}`; + let range: Range + if (getSelection().rangeCount > 0) { + range = getSelection().getRangeAt(0) + } + + if (range && protyle.wysiwyg.element.contains(range.startContainer)) { + const blockElement = hasClosestBlock(range.startContainer); + if (blockElement) { + const position = getSelectionOffset(blockElement, undefined, range); + attr += `${Constants.ZWSP}${blockElement.getAttribute("data-node-id")}${Constants.ZWSP}${position.start}${Constants.ZWSP}${position.end}`; + } + } + if (protyle.block.showAll) { + attr += `${Constants.ZWSP}${protyle.block.id}`; + } + + fetchPost("/api/attr/setBlockAttrs", {id: protyle.block.rootID, attrs: {scroll: attr}}, () => { + protyle.wysiwyg.element.setAttribute("scroll", attr); + }); +} + +export const restoreScroll = (protyle: IProtyle) => { + const attr = protyle.wysiwyg.element.getAttribute("scroll") + if (!attr) { + return + } + const [startId, endId, scrollTop, focusId, focusStart, focusEnd, zoomInId] = attr.split(Constants.ZWSP); + if (protyle.wysiwyg.element.firstElementChild.getAttribute("data-node-id") === startId && + protyle.wysiwyg.element.lastElementChild.getAttribute("data-node-id") === endId) { + protyle.contentElement.scrollTop = parseInt(scrollTop); + focusByOffset(protyle.wysiwyg.element.querySelector(`[data-node-id="${focusId}"]`), parseInt(focusStart), parseInt(focusEnd)); + } else if (zoomInId && protyle.block.id !== zoomInId) { + zoomOut(protyle, zoomInId, undefined, false, () => { + protyle.contentElement.scrollTop = parseInt(scrollTop); + focusByOffset(protyle.wysiwyg.element.querySelector(`[data-node-id="${focusId}"]`), parseInt(focusStart), parseInt(focusEnd)); + }); + } else if (!protyle.scroll.element.classList.contains("fn__none")) { + fetchPost("/api/filetree/getDoc", { + id: protyle.block.id, + startID: startId, + endID: endId, + }, getResponse => { + protyle.wysiwyg.element.innerHTML = getResponse.data.content; + protyle.contentElement.scrollTop = parseInt(scrollTop); + focusByOffset(protyle.wysiwyg.element.querySelector(`[data-node-id="${focusId}"]`), parseInt(focusStart), parseInt(focusEnd)); + }) + } +} diff --git a/app/src/protyle/util/onGet.ts b/app/src/protyle/util/onGet.ts index 8df308641..a664988f7 100644 --- a/app/src/protyle/util/onGet.ts +++ b/app/src/protyle/util/onGet.ts @@ -80,7 +80,7 @@ export const onGet = (data: IWebSocketData, protyle: IProtyle, action: string[] } if (protyle.options.render.title) { - protyle.title.render(protyle); + protyle.title.render(protyle, false, action); } else if (protyle.options.render.background) { fetchPost("/api/block/getDocInfo", { id: protyle.block.rootID diff --git a/app/src/protyle/wysiwyg/index.ts b/app/src/protyle/wysiwyg/index.ts index 9d514050d..9cf2f5d9b 100644 --- a/app/src/protyle/wysiwyg/index.ts +++ b/app/src/protyle/wysiwyg/index.ts @@ -86,7 +86,7 @@ export class WYSIWYG { const ialKeys = Object.keys(ial); for (let i = 0; i < this.element.attributes.length; i++) { const oldKey = this.element.attributes[i].nodeName; - if (!["type", "class", "spellcheck", "contenteditable", "data-doc-type", "style"].includes(oldKey) && + if (!["type", "class", "spellcheck", "contenteditable", "data-doc-type", "style", "scroll"].includes(oldKey) && !ialKeys.includes(oldKey)) { this.element.removeAttribute(oldKey); i--;