A privacy-first, self-hosted, fully open source personal knowledge management software, written in typescript and golang. (PERSONAL FORK)
at lambda-fork/main 339 lines 11 kB view raw
1import {confirmDialog} from "../dialog/confirmDialog"; 2import {Plugin} from "./index"; 3import {hideMessage, showMessage} from "../dialog/message"; 4import {Dialog} from "../dialog"; 5import {fetchGet, fetchPost, fetchSyncPost} from "../util/fetch"; 6import {getBackend, getFrontend} from "../util/functions"; 7/// #if !MOBILE 8import {openFile, openFileById} from "../editor/util"; 9import {openNewWindow, openNewWindowById} from "../window/openNewWindow"; 10import {Tab} from "../layout/Tab"; 11/// #endif 12import {updateHotkeyTip} from "../protyle/util/compatibility"; 13import * as platformUtils from "../protyle/util/compatibility"; 14import {App} from "../index"; 15import {Constants} from "../constants"; 16import {Setting} from "./Setting"; 17import {Menu} from "./Menu"; 18import {Protyle} from "../protyle"; 19import {openMobileFileById} from "../mobile/editor"; 20import {lockScreen, exitSiYuan} from "../dialog/processSystem"; 21import {Model} from "../layout/Model"; 22import {getActiveTab, getDockByType} from "../layout/tabUtil"; 23/// #if !MOBILE 24import {getAllModels} from "../layout/getAll"; 25/// #endif 26import {getAllEditor} from "../layout/getAll"; 27import {openSetting} from "../config"; 28import {openAttr, openFileAttr} from "../menus/commonMenuItem"; 29import {globalCommand} from "../boot/globalEvent/command/global"; 30import {exportLayout} from "../layout/util"; 31import {saveScroll} from "../protyle/scroll/saveScroll"; 32import {hasClosestByClassName} from "../protyle/util/hasClosest"; 33import {Files} from "../layout/dock/Files"; 34 35let openTab; 36let openWindow; 37/// #if MOBILE 38openTab = () => { 39 // TODO: Mobile 40}; 41openWindow = () => { 42 // TODO: Mobile 43}; 44/// #else 45openWindow = (options: { 46 position?: IPosition, 47 height?: number, 48 width?: number, 49 tab?: Tab, 50 doc?: { 51 id: string, // 块 id 52 }, 53}) => { 54 if (options.doc && options.doc.id) { 55 openNewWindowById(options.doc.id, {position: options.position, width: options.width, height: options.height}); 56 return; 57 } 58 if (options.tab) { 59 openNewWindow(options.tab, {position: options.position, width: options.width, height: options.height}); 60 return; 61 } 62}; 63 64openTab = (options: { 65 app: App, 66 doc?: { 67 id: string, // 块 id 68 action?: TProtyleAction [] // cb-get-all:获取所有内容;cb-get-focus:打开后光标定位在 id 所在的块;cb-get-hl: 打开后 id 块高亮 69 zoomIn?: boolean // 是否缩放 70 }, 71 pdf?: { 72 path: string, 73 page?: number, // pdf 页码 74 id?: string, // File Annotation id 75 }, 76 asset?: { 77 path: string, 78 }, 79 search?: Config.IUILayoutTabSearchConfig 80 card?: { 81 type: TCardType, 82 id?: string, // cardType 为 all 时不传,否则传文档或笔记本 id 83 title?: string // cardType 为 all 时不传,否则传文档或笔记本名称 84 }, 85 custom?: { 86 title: string, 87 icon: string, 88 data?: any 89 id: string 90 } 91 position?: "right" | "bottom", 92 keepCursor?: boolean // 是否跳转到新 tab 上 93 removeCurrentTab?: boolean // 在当前页签打开时需移除原有页签 94 afterOpen?: (model?: Model) => void // 打开后回调 95}) => { 96 if (options.doc) { 97 if (options.doc.zoomIn) { 98 if (options.doc.action && !options.doc.action.includes(Constants.CB_GET_ALL)) { 99 options.doc.action.push(Constants.CB_GET_ALL); 100 } else { 101 options.doc.action = [Constants.CB_GET_ALL]; 102 } 103 } 104 if (!options.doc.action) { 105 options.doc.action = []; 106 } 107 return openFileById({ 108 app: options.app, 109 keepCursor: options.keepCursor, 110 removeCurrentTab: options.removeCurrentTab, 111 position: options.position, 112 afterOpen: options.afterOpen, 113 id: options.doc.id, 114 action: options.doc.action, 115 zoomIn: options.doc.zoomIn 116 }); 117 } 118 if (options.asset) { 119 return openFile({ 120 app: options.app, 121 keepCursor: options.keepCursor, 122 removeCurrentTab: options.removeCurrentTab, 123 position: options.position, 124 afterOpen: options.afterOpen, 125 assetPath: options.asset.path, 126 }); 127 } 128 if (options.pdf) { 129 return openFile({ 130 app: options.app, 131 keepCursor: options.keepCursor, 132 removeCurrentTab: options.removeCurrentTab, 133 position: options.position, 134 afterOpen: options.afterOpen, 135 assetPath: options.pdf.path, 136 page: options.pdf.id || options.pdf.page, 137 }); 138 } 139 if (options.search) { 140 if (!options.search.idPath) { 141 options.search.idPath = []; 142 } 143 if (!options.search.hPath) { 144 options.search.hPath = ""; 145 } 146 return openFile({ 147 app: options.app, 148 keepCursor: options.keepCursor, 149 removeCurrentTab: options.removeCurrentTab, 150 position: options.position, 151 afterOpen: options.afterOpen, 152 searchData: options.search, 153 }); 154 } 155 if (options.card) { 156 return openFile({ 157 app: options.app, 158 keepCursor: options.keepCursor, 159 removeCurrentTab: options.removeCurrentTab, 160 position: options.position, 161 afterOpen: options.afterOpen, 162 custom: { 163 icon: "iconRiffCard", 164 title: window.siyuan.languages.spaceRepetition, 165 data: { 166 cardType: options.card.type, 167 id: options.card.id || "", 168 title: options.card.title, 169 }, 170 id: "siyuan-card" 171 }, 172 }); 173 } 174 if (options.custom) { 175 return openFile(options); 176 } 177 178}; 179/// #endif 180 181const getModelByDockType = (type: TDock | string) => { 182 /// #if MOBILE 183 return window.siyuan.mobile.docks[type]; 184 /// #else 185 return getDockByType(type).data[type]; 186 /// #endif 187}; 188 189const openAttributePanel = (options: { 190 data?: IObject // 块属性值 191 nodeElement?: HTMLElement, // 块元素 192 focusName: "bookmark" | "name" | "alias" | "memo" | "av" | "custom", // av 为数据库页签,custom 为自定义页签,其余为内置输入框 193 protyle?: IProtyle, // 有数据库时需要传入 protyle 194}) => { 195 if (options.data) { 196 openFileAttr(options.data, options.focusName, options.protyle); 197 } else { 198 openAttr(options.nodeElement, options.focusName, options.protyle); 199 } 200}; 201 202const saveLayout = (cb: () => void) => { 203 /// #if MOBILE 204 if (window.siyuan.mobile.editor) { 205 const result = saveScroll(window.siyuan.mobile.editor.protyle); 206 if (cb && result instanceof Promise) { 207 result.then(() => { 208 cb(); 209 }); 210 } 211 } 212 /// #else 213 exportLayout({cb, errorExit: false}); 214 /// #endif 215}; 216 217const getActiveEditor = (wndActive = true) => { 218 let editor; 219 /// #if !MOBILE 220 const range = getSelection().rangeCount > 0 ? getSelection().getRangeAt(0) : null; 221 const allEditor = getAllEditor(); 222 if (range) { 223 editor = allEditor.find(item => { 224 if (item.protyle.element.contains(range.startContainer)) { 225 return true; 226 } 227 }); 228 } 229 if (!editor) { 230 editor = allEditor.find(item => { 231 if (hasClosestByClassName(item.protyle.element, "layout__wnd--active", true)) { 232 return true; 233 } 234 }); 235 } 236 if (!editor && !wndActive) { 237 let activeTime = 0; 238 allEditor.forEach(item => { 239 let headerElement = item.protyle.model?.parent.headElement; 240 if (!headerElement && item.protyle.element.getBoundingClientRect().height > 0) { 241 const tabBodyElement = item.protyle.element.closest(".fn__flex-1[data-id]"); 242 if (tabBodyElement) { 243 headerElement = document.querySelector(`.layout-tab-bar .item[data-id="${tabBodyElement.getAttribute("data-id")}"]`); 244 } 245 } 246 if (headerElement) { 247 if (headerElement.classList.contains("item--focus") && parseInt(headerElement.dataset.activetime) > activeTime) { 248 activeTime = parseInt(headerElement.dataset.activetime); 249 editor = item; 250 } 251 } else if (item.protyle.element.getBoundingClientRect().height > 0) { 252 editor = item; 253 } 254 }); 255 } 256 /// #else 257 editor = window.siyuan.mobile.popEditor || window.siyuan.mobile.editor; 258 if (editor?.protyle.element.classList.contains("fn__none")) { 259 return undefined; 260 } 261 /// #endif 262 return editor; 263}; 264 265export const expandDocTree = async (options: { 266 id: string, 267 isSetCurrent?: boolean 268}) => { 269 let isNotebook = false; 270 window.siyuan.notebooks.find(item => { 271 if (options.id === item.id) { 272 isNotebook = true; 273 return true; 274 } 275 }); 276 let liElement: HTMLElement; 277 let notebookId = options.id; 278 const file = getModelByDockType("file") as Files; 279 if (typeof options.isSetCurrent === "undefined") { 280 options.isSetCurrent = true; 281 } 282 if (isNotebook) { 283 liElement = file.element.querySelector(`.b3-list[data-url="${options.id}"]`)?.firstElementChild as HTMLElement; 284 } else { 285 const response = await fetchSyncPost("api/block/getBlockInfo", {id: options.id}); 286 if (response.code === -1) { 287 return; 288 } 289 notebookId = response.data.box; 290 liElement = await file.selectItem(response.data.box, response.data.path, undefined, undefined, options.isSetCurrent); 291 } 292 if (!liElement) { 293 return; 294 } 295 if (options.isSetCurrent || typeof options.isSetCurrent === "undefined") { 296 file.setCurrent(liElement); 297 } 298 const toggleElement = liElement.querySelector(".b3-list-item__arrow"); 299 if (toggleElement.classList.contains("b3-list-item__arrow--open")) { 300 return; 301 } 302 file.getLeaf(liElement, notebookId); 303}; 304 305export const API = { 306 adaptHotkey: updateHotkeyTip, 307 confirm: confirmDialog, 308 Constants, 309 showMessage, 310 hideMessage, 311 fetchPost, 312 fetchSyncPost, 313 fetchGet, 314 getFrontend, 315 getBackend, 316 getModelByDockType, 317 openTab, 318 openWindow, 319 openMobileFileById, 320 lockScreen, 321 exitSiYuan, 322 Protyle, 323 Plugin, 324 Dialog, 325 Menu, 326 Setting, 327 getAllEditor, 328 /// #if !MOBILE 329 getActiveTab, 330 getAllModels, 331 /// #endif 332 getActiveEditor, 333 platformUtils, 334 openSetting, 335 openAttributePanel, 336 saveLayout, 337 globalCommand, 338 expandDocTree 339};