2024-07-25 17:55:25 +08:00
|
|
|
|
import {hasClosestBlock, isInEmbedBlock} from "../protyle/util/hasClosest";
|
2022-05-26 15:18:53 +08:00
|
|
|
|
import {getContenteditableElement} from "../protyle/wysiwyg/getBlock";
|
|
|
|
|
|
import {focusByOffset, focusByRange, getSelectionOffset} from "../protyle/util/selection";
|
|
|
|
|
|
import {hideElements} from "../protyle/ui/hideElements";
|
2024-03-22 19:52:04 +08:00
|
|
|
|
import {fetchPost, fetchSyncPost} from "./fetch";
|
2022-05-26 15:18:53 +08:00
|
|
|
|
import {Constants} from "../constants";
|
|
|
|
|
|
import {Wnd} from "../layout/Wnd";
|
|
|
|
|
|
import {getInstanceById, getWndByLayout} from "../layout/util";
|
|
|
|
|
|
import {Tab} from "../layout/Tab";
|
|
|
|
|
|
import {Editor} from "../editor";
|
|
|
|
|
|
import {scrollCenter} from "./highlightById";
|
|
|
|
|
|
import {zoomOut} from "../menus/protyle";
|
2023-01-25 21:41:35 +08:00
|
|
|
|
import {showMessage} from "../dialog/message";
|
2023-03-03 12:43:40 +08:00
|
|
|
|
import {saveScroll} from "../protyle/scroll/saveScroll";
|
2023-05-15 11:46:51 +08:00
|
|
|
|
import {getAllModels} from "../layout/getAll";
|
2023-05-18 19:27:21 +08:00
|
|
|
|
import {App} from "../index";
|
2024-03-22 19:52:04 +08:00
|
|
|
|
import {onGet} from "../protyle/util/onGet";
|
2022-05-26 15:18:53 +08:00
|
|
|
|
|
|
|
|
|
|
let forwardStack: IBackStack[] = [];
|
|
|
|
|
|
let previousIsBack = false;
|
|
|
|
|
|
|
2023-05-18 19:27:21 +08:00
|
|
|
|
const focusStack = async (app: App, stack: IBackStack) => {
|
2022-05-26 15:18:53 +08:00
|
|
|
|
hideElements(["gutter", "toolbar", "hint", "util", "dialog"], stack.protyle);
|
2023-05-15 11:46:51 +08:00
|
|
|
|
let blockElement: HTMLElement;
|
2023-02-28 18:56:35 +08:00
|
|
|
|
if (!document.contains(stack.protyle.element)) {
|
2022-05-26 15:18:53 +08:00
|
|
|
|
const response = await fetchSyncPost("/api/block/checkBlockExist", {id: stack.protyle.block.rootID});
|
|
|
|
|
|
if (!response.data) {
|
|
|
|
|
|
// 页签删除
|
|
|
|
|
|
return false;
|
|
|
|
|
|
}
|
|
|
|
|
|
let wnd: Wnd;
|
|
|
|
|
|
// 获取光标所在 tab
|
|
|
|
|
|
const element = document.querySelector(".layout__wnd--active");
|
|
|
|
|
|
if (element) {
|
|
|
|
|
|
wnd = getInstanceById(element.getAttribute("data-id")) as Wnd;
|
|
|
|
|
|
}
|
|
|
|
|
|
if (!wnd) {
|
|
|
|
|
|
// 中心 tab
|
|
|
|
|
|
wnd = getWndByLayout(window.siyuan.layout.centerLayout);
|
|
|
|
|
|
}
|
|
|
|
|
|
if (wnd) {
|
|
|
|
|
|
const info = await fetchSyncPost("/api/block/getBlockInfo", {id: stack.id});
|
2023-01-25 21:41:35 +08:00
|
|
|
|
if (info.code === 3) {
|
|
|
|
|
|
showMessage(info.msg);
|
|
|
|
|
|
return;
|
|
|
|
|
|
}
|
2022-05-26 15:18:53 +08:00
|
|
|
|
const tab = new Tab({
|
|
|
|
|
|
title: info.data.rootTitle,
|
|
|
|
|
|
docIcon: info.data.rootIcon,
|
|
|
|
|
|
callback(tab) {
|
2023-03-03 12:43:40 +08:00
|
|
|
|
const scrollAttr = saveScroll(stack.protyle, true);
|
2023-05-17 12:37:56 +08:00
|
|
|
|
scrollAttr.rootId = stack.protyle.block.rootID;
|
2023-03-03 12:43:40 +08:00
|
|
|
|
scrollAttr.focusId = stack.id;
|
2023-03-03 23:57:51 +08:00
|
|
|
|
scrollAttr.focusStart = stack.position.start;
|
|
|
|
|
|
scrollAttr.focusEnd = stack.position.end;
|
2023-12-09 10:57:53 +08:00
|
|
|
|
window.siyuan.storage[Constants.LOCAL_FILEPOSITION][stack.protyle.block.rootID] = scrollAttr;
|
2022-05-26 15:18:53 +08:00
|
|
|
|
const editor = new Editor({
|
2023-05-18 19:27:21 +08:00
|
|
|
|
app: app,
|
2022-05-26 15:18:53 +08:00
|
|
|
|
tab,
|
2023-05-11 20:00:21 +08:00
|
|
|
|
blockId: stack.zoomId || stack.protyle.block.rootID,
|
2023-12-09 22:58:33 +08:00
|
|
|
|
rootId: stack.protyle.block.rootID,
|
2023-05-11 20:00:21 +08:00
|
|
|
|
action: stack.zoomId ? [Constants.CB_GET_FOCUS, Constants.CB_GET_ALL] : [Constants.CB_GET_FOCUS]
|
2022-05-26 15:18:53 +08:00
|
|
|
|
});
|
|
|
|
|
|
tab.addModel(editor);
|
|
|
|
|
|
}
|
|
|
|
|
|
});
|
2022-07-28 22:01:23 +08:00
|
|
|
|
if (window.siyuan.config.fileTree.openFilesUseCurrentTab) {
|
|
|
|
|
|
let unUpdateTab: Tab;
|
|
|
|
|
|
// 不能 reverse, 找到也不能提前退出循环,否则 https://github.com/siyuan-note/siyuan/issues/3271
|
|
|
|
|
|
wnd.children.forEach((item) => {
|
|
|
|
|
|
if (item.headElement && item.headElement.classList.contains("item--unupdate") && !item.headElement.classList.contains("item--pin")) {
|
|
|
|
|
|
unUpdateTab = item;
|
|
|
|
|
|
}
|
|
|
|
|
|
});
|
|
|
|
|
|
wnd.addTab(tab);
|
|
|
|
|
|
if (unUpdateTab) {
|
|
|
|
|
|
wnd.removeTab(unUpdateTab.id);
|
|
|
|
|
|
}
|
|
|
|
|
|
} else {
|
|
|
|
|
|
wnd.addTab(tab);
|
|
|
|
|
|
}
|
2022-05-26 15:18:53 +08:00
|
|
|
|
wnd.showHeading();
|
2023-02-28 18:56:35 +08:00
|
|
|
|
// 替换被关闭的 protyle
|
|
|
|
|
|
const protyle = (tab.model as Editor).editor.protyle;
|
|
|
|
|
|
stack.protyle = protyle;
|
|
|
|
|
|
forwardStack.forEach(item => {
|
|
|
|
|
|
if (!document.contains(item.protyle.element) && item.protyle.block.rootID === info.data.rootID) {
|
|
|
|
|
|
item.protyle = protyle;
|
|
|
|
|
|
}
|
|
|
|
|
|
});
|
|
|
|
|
|
window.siyuan.backStack.forEach(item => {
|
|
|
|
|
|
if (!document.contains(item.protyle.element) && item.protyle.block.rootID === info.data.rootID) {
|
|
|
|
|
|
item.protyle = protyle;
|
|
|
|
|
|
}
|
|
|
|
|
|
});
|
|
|
|
|
|
if (info.data.rootID === stack.id) {
|
|
|
|
|
|
focusByOffset(protyle.title.editElement, stack.position.start, stack.position.end);
|
|
|
|
|
|
} else {
|
2023-05-15 11:46:51 +08:00
|
|
|
|
Array.from(protyle.wysiwyg.element.querySelectorAll(`[data-node-id="${stack.id}"]`)).find((item: HTMLElement) => {
|
2024-07-25 17:55:25 +08:00
|
|
|
|
if (!isInEmbedBlock(item)) {
|
2023-02-28 18:56:35 +08:00
|
|
|
|
blockElement = item;
|
|
|
|
|
|
return true;
|
2022-05-26 15:18:53 +08:00
|
|
|
|
}
|
|
|
|
|
|
});
|
2023-02-28 18:56:35 +08:00
|
|
|
|
focusByOffset(getContenteditableElement(blockElement), stack.position.start, stack.position.end);
|
|
|
|
|
|
scrollCenter(protyle, blockElement);
|
|
|
|
|
|
}
|
2022-05-26 15:18:53 +08:00
|
|
|
|
return true;
|
|
|
|
|
|
} else {
|
|
|
|
|
|
return false;
|
|
|
|
|
|
}
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
if (stack.protyle.block.rootID === stack.id) {
|
|
|
|
|
|
if (stack.protyle.title.editElement.getBoundingClientRect().height === 0) {
|
|
|
|
|
|
// 切换 tab
|
|
|
|
|
|
stack.protyle.model.parent.parent.switchTab(stack.protyle.model.parent.headElement);
|
2023-05-11 20:00:21 +08:00
|
|
|
|
// 需要更新 range,否则 resize 中 `保持光标位置不变` 会导致光标跳动
|
|
|
|
|
|
stack.protyle.toolbar.range = undefined;
|
2022-05-26 15:18:53 +08:00
|
|
|
|
}
|
|
|
|
|
|
focusByOffset(stack.protyle.title.editElement, stack.position.start, stack.position.end);
|
|
|
|
|
|
return true;
|
|
|
|
|
|
}
|
2023-05-15 11:46:51 +08:00
|
|
|
|
Array.from(stack.protyle.wysiwyg.element.querySelectorAll(`[data-node-id="${stack.id}"]`)).find((item: HTMLElement) => {
|
2024-07-25 17:55:25 +08:00
|
|
|
|
if (!isInEmbedBlock(item)) {
|
2022-05-26 15:18:53 +08:00
|
|
|
|
blockElement = item;
|
|
|
|
|
|
return true;
|
|
|
|
|
|
}
|
|
|
|
|
|
});
|
2023-05-17 11:02:49 +08:00
|
|
|
|
if (blockElement &&
|
|
|
|
|
|
// 即使块存在,折叠的情况需要也需要 zoomOut,否则折叠块内的光标无法定位
|
|
|
|
|
|
(!stack.zoomId || (stack.zoomId && stack.zoomId === stack.protyle.block.id))
|
|
|
|
|
|
) {
|
2022-05-26 15:18:53 +08:00
|
|
|
|
if (blockElement.getBoundingClientRect().height === 0) {
|
|
|
|
|
|
// 切换 tab
|
|
|
|
|
|
stack.protyle.model.parent.parent.switchTab(stack.protyle.model.parent.headElement);
|
|
|
|
|
|
}
|
2023-05-11 20:00:21 +08:00
|
|
|
|
focusByOffset(getContenteditableElement(blockElement), stack.position.start, stack.position.end);
|
2024-01-02 19:55:11 +08:00
|
|
|
|
scrollCenter(stack.protyle, blockElement);
|
2023-05-15 11:46:51 +08:00
|
|
|
|
getAllModels().outline.forEach(item => {
|
|
|
|
|
|
if (item.blockId === stack.protyle.block.rootID) {
|
|
|
|
|
|
item.setCurrent(blockElement);
|
|
|
|
|
|
}
|
|
|
|
|
|
});
|
2022-05-26 15:18:53 +08:00
|
|
|
|
return true;
|
|
|
|
|
|
}
|
|
|
|
|
|
if (stack.protyle.element.parentElement) {
|
|
|
|
|
|
const response = await fetchSyncPost("/api/block/checkBlockExist", {id: stack.id});
|
|
|
|
|
|
if (!response.data) {
|
|
|
|
|
|
// 块被删除
|
|
|
|
|
|
if (getSelection().rangeCount > 0) {
|
|
|
|
|
|
focusByRange(getSelection().getRangeAt(0));
|
|
|
|
|
|
}
|
|
|
|
|
|
return false;
|
|
|
|
|
|
}
|
2024-03-22 19:52:04 +08:00
|
|
|
|
// 动态加载导致内容移除 https://github.com/siyuan-note/siyuan/issues/10692
|
|
|
|
|
|
if (!blockElement && !stack.zoomId && !stack.protyle.scroll.element.classList.contains("fn__none")) {
|
|
|
|
|
|
fetchPost("/api/filetree/getDoc", {
|
|
|
|
|
|
id: stack.id,
|
|
|
|
|
|
mode: 3,
|
|
|
|
|
|
size: window.siyuan.config.editor.dynamicLoadBlocks,
|
|
|
|
|
|
}, getResponse => {
|
|
|
|
|
|
onGet({
|
|
|
|
|
|
data: getResponse,
|
|
|
|
|
|
protyle: stack.protyle,
|
|
|
|
|
|
afterCB() {
|
|
|
|
|
|
Array.from(stack.protyle.wysiwyg.element.querySelectorAll(`[data-node-id="${stack.id}"]`)).find((item: HTMLElement) => {
|
2024-07-25 17:55:25 +08:00
|
|
|
|
if (!isInEmbedBlock(item)) {
|
2024-03-22 19:52:04 +08:00
|
|
|
|
blockElement = item;
|
|
|
|
|
|
return true;
|
|
|
|
|
|
}
|
|
|
|
|
|
});
|
|
|
|
|
|
if (!blockElement) {
|
|
|
|
|
|
return;
|
|
|
|
|
|
}
|
|
|
|
|
|
getAllModels().outline.forEach(item => {
|
|
|
|
|
|
if (item.blockId === stack.protyle.block.rootID) {
|
|
|
|
|
|
item.setCurrent(blockElement);
|
|
|
|
|
|
}
|
|
|
|
|
|
});
|
|
|
|
|
|
focusByOffset(getContenteditableElement(blockElement), stack.position.start, stack.position.end);
|
2024-03-22 23:09:12 +08:00
|
|
|
|
scrollCenter(stack.protyle, blockElement, true);
|
2024-03-22 19:52:04 +08:00
|
|
|
|
}
|
|
|
|
|
|
});
|
|
|
|
|
|
});
|
|
|
|
|
|
return true;
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
// 缩放
|
2023-06-01 14:56:21 +08:00
|
|
|
|
zoomOut({
|
|
|
|
|
|
protyle: stack.protyle,
|
|
|
|
|
|
id: stack.zoomId || stack.protyle.block.rootID,
|
|
|
|
|
|
isPushBack: false,
|
|
|
|
|
|
callback: () => {
|
|
|
|
|
|
Array.from(stack.protyle.wysiwyg.element.querySelectorAll(`[data-node-id="${stack.id}"]`)).find((item: HTMLElement) => {
|
2024-07-25 17:55:25 +08:00
|
|
|
|
if (!isInEmbedBlock(item)) {
|
2023-06-01 14:56:21 +08:00
|
|
|
|
blockElement = item;
|
|
|
|
|
|
return true;
|
|
|
|
|
|
}
|
|
|
|
|
|
});
|
|
|
|
|
|
if (!blockElement) {
|
|
|
|
|
|
return;
|
2022-05-26 15:18:53 +08:00
|
|
|
|
}
|
2023-06-01 14:56:21 +08:00
|
|
|
|
getAllModels().outline.forEach(item => {
|
|
|
|
|
|
if (item.blockId === stack.protyle.block.rootID) {
|
|
|
|
|
|
item.setCurrent(blockElement);
|
|
|
|
|
|
}
|
|
|
|
|
|
});
|
|
|
|
|
|
focusByOffset(getContenteditableElement(blockElement), stack.position.start, stack.position.end);
|
|
|
|
|
|
scrollCenter(stack.protyle, blockElement, true);
|
2023-05-15 11:46:51 +08:00
|
|
|
|
}
|
2022-05-26 15:18:53 +08:00
|
|
|
|
});
|
|
|
|
|
|
return true;
|
|
|
|
|
|
}
|
|
|
|
|
|
};
|
|
|
|
|
|
|
2023-05-18 19:27:21 +08:00
|
|
|
|
export const goBack = async (app: App) => {
|
2022-05-26 15:18:53 +08:00
|
|
|
|
if (window.siyuan.backStack.length === 0) {
|
|
|
|
|
|
if (forwardStack.length > 0) {
|
2023-05-18 19:27:21 +08:00
|
|
|
|
await focusStack(app, forwardStack[forwardStack.length - 1]);
|
2022-05-26 15:18:53 +08:00
|
|
|
|
}
|
|
|
|
|
|
return;
|
|
|
|
|
|
}
|
2023-01-26 18:53:57 +08:00
|
|
|
|
document.querySelector("#barForward")?.classList.remove("toolbar__item--disabled");
|
2023-02-28 18:56:35 +08:00
|
|
|
|
if (!previousIsBack &&
|
|
|
|
|
|
// 页签被关闭时应优先打开该页签,页签存在时即可返回上一步,不用再重置光标到该页签上
|
|
|
|
|
|
document.contains(window.siyuan.backStack[window.siyuan.backStack.length - 1].protyle.element)) {
|
2022-05-26 15:18:53 +08:00
|
|
|
|
forwardStack.push(window.siyuan.backStack.pop());
|
|
|
|
|
|
}
|
|
|
|
|
|
let stack = window.siyuan.backStack.pop();
|
|
|
|
|
|
while (stack) {
|
2023-05-18 19:27:21 +08:00
|
|
|
|
const isFocus = await focusStack(app, stack);
|
2022-05-26 15:18:53 +08:00
|
|
|
|
if (isFocus) {
|
|
|
|
|
|
forwardStack.push(stack);
|
|
|
|
|
|
break;
|
|
|
|
|
|
} else {
|
|
|
|
|
|
stack = window.siyuan.backStack.pop();
|
|
|
|
|
|
}
|
|
|
|
|
|
}
|
|
|
|
|
|
previousIsBack = true;
|
2022-10-19 11:13:28 +08:00
|
|
|
|
if (window.siyuan.backStack.length === 0) {
|
2023-01-26 18:53:57 +08:00
|
|
|
|
document.querySelector("#barBack")?.classList.add("toolbar__item--disabled");
|
2022-10-19 11:13:28 +08:00
|
|
|
|
}
|
2022-05-26 15:18:53 +08:00
|
|
|
|
};
|
|
|
|
|
|
|
2023-05-18 19:27:21 +08:00
|
|
|
|
export const goForward = async (app: App) => {
|
2022-05-26 15:18:53 +08:00
|
|
|
|
if (forwardStack.length === 0) {
|
|
|
|
|
|
if (window.siyuan.backStack.length > 0) {
|
2023-05-18 19:27:21 +08:00
|
|
|
|
await focusStack(app, window.siyuan.backStack[window.siyuan.backStack.length - 1]);
|
2022-05-26 15:18:53 +08:00
|
|
|
|
}
|
|
|
|
|
|
return;
|
|
|
|
|
|
}
|
2023-01-26 18:53:57 +08:00
|
|
|
|
document.querySelector("#barBack")?.classList.remove("toolbar__item--disabled");
|
2022-05-26 15:18:53 +08:00
|
|
|
|
if (previousIsBack) {
|
|
|
|
|
|
window.siyuan.backStack.push(forwardStack.pop());
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
let stack = forwardStack.pop();
|
|
|
|
|
|
while (stack) {
|
2023-05-18 19:27:21 +08:00
|
|
|
|
const isFocus = await focusStack(app, stack);
|
2022-05-26 15:18:53 +08:00
|
|
|
|
if (isFocus) {
|
|
|
|
|
|
window.siyuan.backStack.push(stack);
|
|
|
|
|
|
break;
|
|
|
|
|
|
} else {
|
|
|
|
|
|
stack = forwardStack.pop();
|
|
|
|
|
|
}
|
|
|
|
|
|
}
|
|
|
|
|
|
previousIsBack = false;
|
2022-10-19 11:13:28 +08:00
|
|
|
|
if (forwardStack.length === 0) {
|
2023-01-26 18:53:57 +08:00
|
|
|
|
document.querySelector("#barForward")?.classList.add("toolbar__item--disabled");
|
2022-10-19 11:13:28 +08:00
|
|
|
|
}
|
2022-05-26 15:18:53 +08:00
|
|
|
|
};
|
|
|
|
|
|
|
|
|
|
|
|
export const pushBack = (protyle: IProtyle, range?: Range, blockElement?: Element) => {
|
|
|
|
|
|
if (!protyle.model) {
|
|
|
|
|
|
return;
|
|
|
|
|
|
}
|
2022-08-10 11:28:57 +08:00
|
|
|
|
if (!blockElement && range) {
|
2022-05-26 15:18:53 +08:00
|
|
|
|
blockElement = hasClosestBlock(range.startContainer) as Element;
|
|
|
|
|
|
}
|
|
|
|
|
|
if (!blockElement) {
|
|
|
|
|
|
return;
|
|
|
|
|
|
}
|
|
|
|
|
|
let editElement;
|
|
|
|
|
|
if (blockElement.classList.contains("protyle-title__input")) {
|
|
|
|
|
|
editElement = blockElement;
|
|
|
|
|
|
} else {
|
|
|
|
|
|
editElement = getContenteditableElement(blockElement);
|
|
|
|
|
|
}
|
|
|
|
|
|
if (editElement) {
|
|
|
|
|
|
const position = getSelectionOffset(editElement, undefined, range);
|
|
|
|
|
|
const id = blockElement.getAttribute("data-node-id") || protyle.block.rootID;
|
|
|
|
|
|
const lastStack = window.siyuan.backStack[window.siyuan.backStack.length - 1];
|
2023-05-11 20:00:21 +08:00
|
|
|
|
if (lastStack && lastStack.id === id && (
|
|
|
|
|
|
(protyle.block.showAll && lastStack.zoomId === protyle.block.id) || (!lastStack.zoomId && !protyle.block.showAll)
|
|
|
|
|
|
)) {
|
2022-05-26 15:18:53 +08:00
|
|
|
|
lastStack.position = position;
|
|
|
|
|
|
} else {
|
|
|
|
|
|
if (forwardStack.length > 0) {
|
|
|
|
|
|
if (previousIsBack) {
|
|
|
|
|
|
window.siyuan.backStack.push(forwardStack.pop());
|
|
|
|
|
|
}
|
|
|
|
|
|
forwardStack = [];
|
2023-01-26 18:53:57 +08:00
|
|
|
|
document.querySelector("#barForward")?.classList.add("toolbar__item--disabled");
|
2022-05-26 15:18:53 +08:00
|
|
|
|
}
|
|
|
|
|
|
window.siyuan.backStack.push({
|
|
|
|
|
|
position,
|
|
|
|
|
|
id,
|
|
|
|
|
|
protyle,
|
2023-05-11 20:00:21 +08:00
|
|
|
|
zoomId: protyle.block.showAll ? protyle.block.id : undefined,
|
2022-05-26 15:18:53 +08:00
|
|
|
|
});
|
|
|
|
|
|
if (window.siyuan.backStack.length > Constants.SIZE_UNDO) {
|
|
|
|
|
|
window.siyuan.backStack.shift();
|
|
|
|
|
|
}
|
|
|
|
|
|
previousIsBack = false;
|
|
|
|
|
|
}
|
2022-10-19 11:13:28 +08:00
|
|
|
|
if (window.siyuan.backStack.length > 1) {
|
2023-01-26 18:53:57 +08:00
|
|
|
|
document.querySelector("#barBack")?.classList.remove("toolbar__item--disabled");
|
2022-10-19 11:13:28 +08:00
|
|
|
|
}
|
2022-05-26 15:18:53 +08:00
|
|
|
|
}
|
|
|
|
|
|
};
|