2023-05-07 17:31:50 +08:00
|
|
|
|
import {confirmDialog} from "../dialog/confirmDialog";
|
|
|
|
|
|
import {Plugin} from "./index";
|
2025-08-10 18:22:01 +08:00
|
|
|
|
import {hideMessage, showMessage} from "../dialog/message";
|
2023-05-07 22:00:41 +08:00
|
|
|
|
import {Dialog} from "../dialog";
|
2023-05-08 15:11:50 +08:00
|
|
|
|
import {fetchGet, fetchPost, fetchSyncPost} from "../util/fetch";
|
2023-05-30 09:42:25 +08:00
|
|
|
|
import {getBackend, getFrontend} from "../util/functions";
|
2023-05-10 16:11:02 +08:00
|
|
|
|
/// #if !MOBILE
|
2023-05-28 23:45:23 +08:00
|
|
|
|
import {openFile, openFileById} from "../editor/util";
|
2023-08-30 22:28:25 +08:00
|
|
|
|
import {openNewWindow, openNewWindowById} from "../window/openNewWindow";
|
|
|
|
|
|
import {Tab} from "../layout/Tab";
|
2023-05-10 16:11:02 +08:00
|
|
|
|
/// #endif
|
2023-05-12 17:11:43 +08:00
|
|
|
|
import {updateHotkeyTip} from "../protyle/util/compatibility";
|
2024-10-27 17:20:46 +08:00
|
|
|
|
import * as platformUtils from "../protyle/util/compatibility";
|
2023-05-28 23:20:30 +08:00
|
|
|
|
import {App} from "../index";
|
|
|
|
|
|
import {Constants} from "../constants";
|
2023-06-02 21:49:22 +08:00
|
|
|
|
import {Setting} from "./Setting";
|
2023-06-12 12:42:22 +08:00
|
|
|
|
import {Menu} from "./Menu";
|
2023-08-18 17:38:11 +08:00
|
|
|
|
import {Protyle} from "../protyle";
|
2023-11-24 21:58:16 +08:00
|
|
|
|
import {openMobileFileById} from "../mobile/editor";
|
2024-10-13 20:25:45 +08:00
|
|
|
|
import {lockScreen, exitSiYuan} from "../dialog/processSystem";
|
2024-09-14 17:05:06 +08:00
|
|
|
|
import {Model} from "../layout/Model";
|
2025-08-20 12:08:47 +08:00
|
|
|
|
import {getActiveTab, getDockByType} from "../layout/tabUtil";
|
2025-03-18 11:56:26 +08:00
|
|
|
|
/// #if !MOBILE
|
|
|
|
|
|
import {getAllModels} from "../layout/getAll";
|
|
|
|
|
|
/// #endif
|
|
|
|
|
|
import {getAllEditor} from "../layout/getAll";
|
2025-01-11 21:31:18 +08:00
|
|
|
|
import {openSetting} from "../config";
|
2025-03-06 11:54:24 +08:00
|
|
|
|
import {openAttr, openFileAttr} from "../menus/commonMenuItem";
|
2025-04-02 14:23:28 +08:00
|
|
|
|
import {globalCommand} from "../boot/globalEvent/command/global";
|
2025-07-16 23:18:51 +08:00
|
|
|
|
import {exportLayout} from "../layout/util";
|
|
|
|
|
|
import {saveScroll} from "../protyle/scroll/saveScroll";
|
2025-08-21 13:19:13 +08:00
|
|
|
|
import {hasClosestByClassName} from "../protyle/util/hasClosest";
|
2025-09-03 09:41:42 +08:00
|
|
|
|
import {Files} from "../layout/dock/Files";
|
2023-05-07 17:31:50 +08:00
|
|
|
|
|
2023-05-10 16:11:02 +08:00
|
|
|
|
let openTab;
|
2023-08-30 22:28:25 +08:00
|
|
|
|
let openWindow;
|
2023-05-10 16:11:02 +08:00
|
|
|
|
/// #if MOBILE
|
|
|
|
|
|
openTab = () => {
|
|
|
|
|
|
// TODO: Mobile
|
2023-05-10 16:11:51 +08:00
|
|
|
|
};
|
2023-08-30 22:28:25 +08:00
|
|
|
|
openWindow = () => {
|
|
|
|
|
|
// TODO: Mobile
|
|
|
|
|
|
};
|
2023-05-10 16:11:02 +08:00
|
|
|
|
/// #else
|
2023-08-30 22:28:25 +08:00
|
|
|
|
openWindow = (options: {
|
2023-10-01 10:26:31 +08:00
|
|
|
|
position?: IPosition,
|
2023-09-02 20:09:52 +08:00
|
|
|
|
height?: number,
|
|
|
|
|
|
width?: number,
|
2023-08-30 22:28:25 +08:00
|
|
|
|
tab?: Tab,
|
|
|
|
|
|
doc?: {
|
|
|
|
|
|
id: string, // 块 id
|
|
|
|
|
|
},
|
|
|
|
|
|
}) => {
|
2023-09-06 10:50:13 +08:00
|
|
|
|
if (options.doc && options.doc.id) {
|
2023-09-02 20:09:52 +08:00
|
|
|
|
openNewWindowById(options.doc.id, {position: options.position, width: options.width, height: options.height});
|
2023-08-30 22:28:25 +08:00
|
|
|
|
return;
|
|
|
|
|
|
}
|
|
|
|
|
|
if (options.tab) {
|
2023-09-02 20:09:52 +08:00
|
|
|
|
openNewWindow(options.tab, {position: options.position, width: options.width, height: options.height});
|
2023-08-30 22:28:25 +08:00
|
|
|
|
return;
|
|
|
|
|
|
}
|
|
|
|
|
|
};
|
|
|
|
|
|
|
2023-05-28 23:20:30 +08:00
|
|
|
|
openTab = (options: {
|
|
|
|
|
|
app: App,
|
|
|
|
|
|
doc?: {
|
|
|
|
|
|
id: string, // 块 id
|
2024-10-23 11:47:06 +08:00
|
|
|
|
action?: TProtyleAction [] // cb-get-all:获取所有内容;cb-get-focus:打开后光标定位在 id 所在的块;cb-get-hl: 打开后 id 块高亮
|
2023-05-28 23:20:30 +08:00
|
|
|
|
zoomIn?: boolean // 是否缩放
|
|
|
|
|
|
},
|
|
|
|
|
|
pdf?: {
|
|
|
|
|
|
path: string,
|
|
|
|
|
|
page?: number, // pdf 页码
|
|
|
|
|
|
id?: string, // File Annotation id
|
|
|
|
|
|
},
|
|
|
|
|
|
asset?: {
|
|
|
|
|
|
path: string,
|
|
|
|
|
|
},
|
2024-03-25 09:33:22 +08:00
|
|
|
|
search?: Config.IUILayoutTabSearchConfig
|
2023-05-28 23:20:30 +08:00
|
|
|
|
card?: {
|
2023-05-28 23:45:23 +08:00
|
|
|
|
type: TCardType,
|
2023-05-28 23:20:30 +08:00
|
|
|
|
id?: string, // cardType 为 all 时不传,否则传文档或笔记本 id
|
|
|
|
|
|
title?: string // cardType 为 all 时不传,否则传文档或笔记本名称
|
|
|
|
|
|
},
|
|
|
|
|
|
custom?: {
|
|
|
|
|
|
title: string,
|
|
|
|
|
|
icon: string,
|
|
|
|
|
|
data?: any
|
2023-08-18 17:38:11 +08:00
|
|
|
|
id: string
|
2023-05-28 23:20:30 +08:00
|
|
|
|
}
|
|
|
|
|
|
position?: "right" | "bottom",
|
|
|
|
|
|
keepCursor?: boolean // 是否跳转到新 tab 上
|
|
|
|
|
|
removeCurrentTab?: boolean // 在当前页签打开时需移除原有页签
|
2024-09-14 17:05:06 +08:00
|
|
|
|
afterOpen?: (model?: Model) => void // 打开后回调
|
2023-05-28 23:20:30 +08:00
|
|
|
|
}) => {
|
|
|
|
|
|
if (options.doc) {
|
2023-05-28 23:45:23 +08:00
|
|
|
|
if (options.doc.zoomIn) {
|
|
|
|
|
|
if (options.doc.action && !options.doc.action.includes(Constants.CB_GET_ALL)) {
|
|
|
|
|
|
options.doc.action.push(Constants.CB_GET_ALL);
|
|
|
|
|
|
} else {
|
|
|
|
|
|
options.doc.action = [Constants.CB_GET_ALL];
|
|
|
|
|
|
}
|
2023-05-28 23:20:30 +08:00
|
|
|
|
}
|
2023-05-30 12:11:50 +08:00
|
|
|
|
if (!options.doc.action) {
|
2023-06-01 20:50:49 +08:00
|
|
|
|
options.doc.action = [];
|
2023-05-30 12:11:50 +08:00
|
|
|
|
}
|
|
|
|
|
|
return openFileById({
|
2023-05-28 23:20:30 +08:00
|
|
|
|
app: options.app,
|
|
|
|
|
|
keepCursor: options.keepCursor,
|
|
|
|
|
|
removeCurrentTab: options.removeCurrentTab,
|
|
|
|
|
|
position: options.position,
|
|
|
|
|
|
afterOpen: options.afterOpen,
|
|
|
|
|
|
id: options.doc.id,
|
|
|
|
|
|
action: options.doc.action,
|
|
|
|
|
|
zoomIn: options.doc.zoomIn
|
|
|
|
|
|
});
|
|
|
|
|
|
}
|
|
|
|
|
|
if (options.asset) {
|
2023-05-30 12:11:50 +08:00
|
|
|
|
return openFile({
|
2023-05-28 23:20:30 +08:00
|
|
|
|
app: options.app,
|
|
|
|
|
|
keepCursor: options.keepCursor,
|
|
|
|
|
|
removeCurrentTab: options.removeCurrentTab,
|
|
|
|
|
|
position: options.position,
|
|
|
|
|
|
afterOpen: options.afterOpen,
|
|
|
|
|
|
assetPath: options.asset.path,
|
|
|
|
|
|
});
|
|
|
|
|
|
}
|
|
|
|
|
|
if (options.pdf) {
|
2023-05-30 12:11:50 +08:00
|
|
|
|
return openFile({
|
2023-05-28 23:20:30 +08:00
|
|
|
|
app: options.app,
|
|
|
|
|
|
keepCursor: options.keepCursor,
|
|
|
|
|
|
removeCurrentTab: options.removeCurrentTab,
|
|
|
|
|
|
position: options.position,
|
|
|
|
|
|
afterOpen: options.afterOpen,
|
|
|
|
|
|
assetPath: options.pdf.path,
|
|
|
|
|
|
page: options.pdf.id || options.pdf.page,
|
|
|
|
|
|
});
|
|
|
|
|
|
}
|
|
|
|
|
|
if (options.search) {
|
2023-05-28 23:45:23 +08:00
|
|
|
|
if (!options.search.idPath) {
|
|
|
|
|
|
options.search.idPath = [];
|
|
|
|
|
|
}
|
|
|
|
|
|
if (!options.search.hPath) {
|
|
|
|
|
|
options.search.hPath = "";
|
|
|
|
|
|
}
|
2023-05-30 12:11:50 +08:00
|
|
|
|
return openFile({
|
2023-05-28 23:20:30 +08:00
|
|
|
|
app: options.app,
|
|
|
|
|
|
keepCursor: options.keepCursor,
|
|
|
|
|
|
removeCurrentTab: options.removeCurrentTab,
|
|
|
|
|
|
position: options.position,
|
|
|
|
|
|
afterOpen: options.afterOpen,
|
|
|
|
|
|
searchData: options.search,
|
|
|
|
|
|
});
|
|
|
|
|
|
}
|
|
|
|
|
|
if (options.card) {
|
2023-05-30 12:11:50 +08:00
|
|
|
|
return openFile({
|
2023-05-28 23:20:30 +08:00
|
|
|
|
app: options.app,
|
|
|
|
|
|
keepCursor: options.keepCursor,
|
|
|
|
|
|
removeCurrentTab: options.removeCurrentTab,
|
|
|
|
|
|
position: options.position,
|
|
|
|
|
|
afterOpen: options.afterOpen,
|
|
|
|
|
|
custom: {
|
|
|
|
|
|
icon: "iconRiffCard",
|
|
|
|
|
|
title: window.siyuan.languages.spaceRepetition,
|
2023-05-28 23:45:23 +08:00
|
|
|
|
data: {
|
|
|
|
|
|
cardType: options.card.type,
|
|
|
|
|
|
id: options.card.id || "",
|
|
|
|
|
|
title: options.card.title,
|
|
|
|
|
|
},
|
2023-08-18 17:38:11 +08:00
|
|
|
|
id: "siyuan-card"
|
2023-05-28 23:20:30 +08:00
|
|
|
|
},
|
|
|
|
|
|
});
|
|
|
|
|
|
}
|
|
|
|
|
|
if (options.custom) {
|
2023-05-30 12:11:50 +08:00
|
|
|
|
return openFile(options);
|
2023-05-28 23:20:30 +08:00
|
|
|
|
}
|
|
|
|
|
|
|
2023-05-29 11:20:08 +08:00
|
|
|
|
};
|
2023-05-10 16:11:02 +08:00
|
|
|
|
/// #endif
|
|
|
|
|
|
|
2024-10-23 15:47:36 +08:00
|
|
|
|
const getModelByDockType = (type: TDock | string) => {
|
|
|
|
|
|
/// #if MOBILE
|
|
|
|
|
|
return window.siyuan.mobile.docks[type];
|
|
|
|
|
|
/// #else
|
|
|
|
|
|
return getDockByType(type).data[type];
|
|
|
|
|
|
/// #endif
|
2024-10-23 16:14:44 +08:00
|
|
|
|
};
|
2024-10-23 15:47:36 +08:00
|
|
|
|
|
2025-03-06 11:54:24 +08:00
|
|
|
|
const openAttributePanel = (options: {
|
|
|
|
|
|
data?: IObject // 块属性值
|
|
|
|
|
|
nodeElement?: HTMLElement, // 块元素
|
|
|
|
|
|
focusName: "bookmark" | "name" | "alias" | "memo" | "av" | "custom", // av 为数据库页签,custom 为自定义页签,其余为内置输入框
|
|
|
|
|
|
protyle?: IProtyle, // 有数据库时需要传入 protyle
|
|
|
|
|
|
}) => {
|
|
|
|
|
|
if (options.data) {
|
|
|
|
|
|
openFileAttr(options.data, options.focusName, options.protyle);
|
|
|
|
|
|
} else {
|
|
|
|
|
|
openAttr(options.nodeElement, options.focusName, options.protyle);
|
|
|
|
|
|
}
|
|
|
|
|
|
};
|
|
|
|
|
|
|
2025-07-16 23:18:51 +08:00
|
|
|
|
const saveLayout = (cb: () => void) => {
|
|
|
|
|
|
/// #if MOBILE
|
|
|
|
|
|
if (window.siyuan.mobile.editor) {
|
|
|
|
|
|
const result = saveScroll(window.siyuan.mobile.editor.protyle);
|
|
|
|
|
|
if (cb && result instanceof Promise) {
|
|
|
|
|
|
result.then(() => {
|
|
|
|
|
|
cb();
|
|
|
|
|
|
});
|
|
|
|
|
|
}
|
|
|
|
|
|
}
|
|
|
|
|
|
/// #else
|
|
|
|
|
|
exportLayout({cb, errorExit: false});
|
|
|
|
|
|
/// #endif
|
|
|
|
|
|
};
|
|
|
|
|
|
|
2025-08-21 20:25:10 +08:00
|
|
|
|
const getActiveEditor = (wndActive = true) => {
|
2025-08-21 13:19:13 +08:00
|
|
|
|
let editor;
|
|
|
|
|
|
/// #if !MOBILE
|
|
|
|
|
|
const range = getSelection().rangeCount > 0 ? getSelection().getRangeAt(0) : null;
|
2025-08-21 20:25:10 +08:00
|
|
|
|
const allEditor = getAllEditor();
|
2025-08-21 13:19:13 +08:00
|
|
|
|
if (range) {
|
2025-08-21 20:25:10 +08:00
|
|
|
|
editor = allEditor.find(item => {
|
2025-08-21 13:19:13 +08:00
|
|
|
|
if (item.protyle.element.contains(range.startContainer)) {
|
|
|
|
|
|
return true;
|
|
|
|
|
|
}
|
|
|
|
|
|
});
|
|
|
|
|
|
}
|
|
|
|
|
|
if (!editor) {
|
2025-08-21 20:25:10 +08:00
|
|
|
|
editor = allEditor.find(item => {
|
2025-08-21 13:19:13 +08:00
|
|
|
|
if (hasClosestByClassName(item.protyle.element, "layout__wnd--active", true)) {
|
|
|
|
|
|
return true;
|
|
|
|
|
|
}
|
|
|
|
|
|
});
|
|
|
|
|
|
}
|
2025-08-21 20:25:10 +08:00
|
|
|
|
if (!editor && !wndActive) {
|
2025-08-21 20:27:51 +08:00
|
|
|
|
let activeTime = 0;
|
2025-08-21 20:28:35 +08:00
|
|
|
|
allEditor.forEach(item => {
|
2025-09-05 08:59:52 +08:00
|
|
|
|
let headerElement = item.protyle.model?.parent.headElement;
|
|
|
|
|
|
if (!headerElement && item.protyle.element.getBoundingClientRect().height > 0) {
|
|
|
|
|
|
const tabBodyElement = item.protyle.element.parentElement.parentElement.parentElement;
|
|
|
|
|
|
const tabId = tabBodyElement.getAttribute("data-id");
|
|
|
|
|
|
if (tabBodyElement.classList.contains("fn__flex-1") && tabId) {
|
|
|
|
|
|
headerElement = document.querySelector(`.layout-tab-bar .item[data-id="${tabId}"]`);
|
|
|
|
|
|
}
|
|
|
|
|
|
}
|
2025-09-04 20:48:40 +08:00
|
|
|
|
if (headerElement) {
|
|
|
|
|
|
if (headerElement.classList.contains("item--focus") && parseInt(headerElement.dataset.activetime) > activeTime) {
|
|
|
|
|
|
activeTime = parseInt(headerElement.dataset.activetime);
|
|
|
|
|
|
editor = item;
|
|
|
|
|
|
}
|
|
|
|
|
|
} else if (item.protyle.element.getBoundingClientRect().height > 0) {
|
2025-08-21 20:28:35 +08:00
|
|
|
|
editor = item;
|
2025-08-21 20:25:10 +08:00
|
|
|
|
}
|
|
|
|
|
|
});
|
|
|
|
|
|
}
|
2025-08-21 13:19:13 +08:00
|
|
|
|
/// #else
|
|
|
|
|
|
editor = window.siyuan.mobile.popEditor || window.siyuan.mobile.editor;
|
2025-09-04 16:38:07 +08:00
|
|
|
|
if (editor?.protyle.element.classList.contains("fn__none")) {
|
|
|
|
|
|
return undefined;
|
|
|
|
|
|
}
|
2025-08-21 13:19:13 +08:00
|
|
|
|
/// #endif
|
|
|
|
|
|
return editor;
|
|
|
|
|
|
};
|
|
|
|
|
|
|
2025-09-03 09:41:42 +08:00
|
|
|
|
export const expandDocTree = async (options: {
|
|
|
|
|
|
id: string,
|
|
|
|
|
|
isSetCurrent?: boolean
|
|
|
|
|
|
}) => {
|
|
|
|
|
|
let isNotebook = false;
|
|
|
|
|
|
window.siyuan.notebooks.find(item => {
|
|
|
|
|
|
if (options.id === item.id) {
|
|
|
|
|
|
isNotebook = true;
|
|
|
|
|
|
return true;
|
|
|
|
|
|
}
|
|
|
|
|
|
});
|
|
|
|
|
|
let liElement: HTMLElement;
|
|
|
|
|
|
let notebookId = options.id;
|
|
|
|
|
|
const file = getModelByDockType("file") as Files;
|
|
|
|
|
|
if (typeof options.isSetCurrent === "undefined") {
|
|
|
|
|
|
options.isSetCurrent = true;
|
|
|
|
|
|
}
|
|
|
|
|
|
if (isNotebook) {
|
2025-09-04 10:29:54 +08:00
|
|
|
|
liElement = file.element.querySelector(`.b3-list[data-url="${options.id}"]`)?.firstElementChild as HTMLElement;
|
2025-09-03 09:41:42 +08:00
|
|
|
|
} else {
|
|
|
|
|
|
const response = await fetchSyncPost("api/block/getBlockInfo", {id: options.id});
|
2025-09-04 10:29:54 +08:00
|
|
|
|
if (response.code === -1) {
|
|
|
|
|
|
return;
|
|
|
|
|
|
}
|
2025-09-03 09:41:42 +08:00
|
|
|
|
notebookId = response.data.box;
|
|
|
|
|
|
liElement = await file.selectItem(response.data.box, response.data.path, undefined, undefined, options.isSetCurrent);
|
|
|
|
|
|
}
|
|
|
|
|
|
if (!liElement) {
|
|
|
|
|
|
return;
|
|
|
|
|
|
}
|
|
|
|
|
|
if (options.isSetCurrent || typeof options.isSetCurrent === "undefined") {
|
|
|
|
|
|
file.setCurrent(liElement);
|
|
|
|
|
|
}
|
|
|
|
|
|
const toggleElement = liElement.querySelector(".b3-list-item__arrow");
|
|
|
|
|
|
if (toggleElement.classList.contains("b3-list-item__arrow--open")) {
|
|
|
|
|
|
return;
|
|
|
|
|
|
}
|
|
|
|
|
|
file.getLeaf(liElement, notebookId);
|
|
|
|
|
|
};
|
|
|
|
|
|
|
2023-05-07 17:31:50 +08:00
|
|
|
|
export const API = {
|
2024-10-23 11:47:06 +08:00
|
|
|
|
adaptHotkey: updateHotkeyTip,
|
2023-05-07 22:00:41 +08:00
|
|
|
|
confirm: confirmDialog,
|
2024-10-23 11:47:06 +08:00
|
|
|
|
Constants,
|
2023-05-07 22:00:41 +08:00
|
|
|
|
showMessage,
|
2025-08-10 18:22:01 +08:00
|
|
|
|
hideMessage,
|
2023-05-08 15:11:50 +08:00
|
|
|
|
fetchPost,
|
|
|
|
|
|
fetchSyncPost,
|
|
|
|
|
|
fetchGet,
|
2023-05-30 09:29:50 +08:00
|
|
|
|
getFrontend,
|
2023-05-30 09:42:25 +08:00
|
|
|
|
getBackend,
|
2024-10-23 15:47:36 +08:00
|
|
|
|
getModelByDockType,
|
2023-05-10 16:11:02 +08:00
|
|
|
|
openTab,
|
2023-08-30 22:28:25 +08:00
|
|
|
|
openWindow,
|
2024-10-23 11:47:06 +08:00
|
|
|
|
openMobileFileById,
|
|
|
|
|
|
lockScreen,
|
|
|
|
|
|
exitSiYuan,
|
2023-06-23 11:28:32 +08:00
|
|
|
|
Protyle,
|
2023-05-10 00:01:47 +08:00
|
|
|
|
Plugin,
|
2023-05-08 10:56:52 +08:00
|
|
|
|
Dialog,
|
2023-05-08 12:35:56 +08:00
|
|
|
|
Menu,
|
2023-10-30 10:03:27 +08:00
|
|
|
|
Setting,
|
2024-10-23 17:03:36 +08:00
|
|
|
|
getAllEditor,
|
2025-03-18 11:56:26 +08:00
|
|
|
|
/// #if !MOBILE
|
2025-08-20 12:09:37 +08:00
|
|
|
|
getActiveTab,
|
2024-12-22 23:30:31 +08:00
|
|
|
|
getAllModels,
|
2025-03-18 11:56:26 +08:00
|
|
|
|
/// #endif
|
2025-08-21 13:19:13 +08:00
|
|
|
|
getActiveEditor,
|
2025-01-11 21:31:18 +08:00
|
|
|
|
platformUtils,
|
2025-03-06 11:54:24 +08:00
|
|
|
|
openSetting,
|
|
|
|
|
|
openAttributePanel,
|
2025-07-16 23:18:51 +08:00
|
|
|
|
saveLayout,
|
2025-04-02 14:23:28 +08:00
|
|
|
|
globalCommand,
|
2025-09-03 09:41:42 +08:00
|
|
|
|
expandDocTree
|
2023-05-07 17:31:50 +08:00
|
|
|
|
};
|