A privacy-first, self-hosted, fully open source personal knowledge management software, written in typescript and golang. (PERSONAL FORK)
1import {newDailyNote} from "../../../util/mount";
2import {openHistory} from "../../../history/history";
3import {Editor} from "../../../editor";
4/// #if MOBILE
5import {openDock} from "../../../mobile/dock/util";
6import {popMenu} from "../../../mobile/menu";
7import {popSearch} from "../../../mobile/menu/search";
8import {getRecentDocs} from "../../../mobile/menu/getRecentDocs";
9/// #else
10import {openNewWindow} from "../../../window/openNewWindow";
11import {openBacklink, openGraph, openOutline, selectOpenTab, toggleDockBar} from "../../../layout/dock/util";
12import {openGlobalSearch} from "../../../search/util";
13import {workspaceMenu} from "../../../menus/workspace";
14import {isWindow} from "../../../util/functions";
15import {openRecentDocs} from "../../../business/openRecentDocs";
16import {openSearch} from "../../../search/spread";
17import {goBack, goForward} from "../../../util/backForward";
18import {getAllTabs, getAllWnds} from "../../../layout/getAll";
19import {getInstanceById} from "../../../layout/util";
20import {
21 closeTabByType,
22 copyTab,
23 getActiveTab,
24 getDockByType,
25 resizeTabs,
26 switchTabByIndex
27} from "../../../layout/tabUtil";
28import {openSetting} from "../../../config";
29import {Tab} from "../../../layout/Tab";
30/// #endif
31/// #if !BROWSER
32import {ipcRenderer} from "electron";
33/// #endif
34import {App} from "../../../index";
35import {Constants} from "../../../constants";
36import {setReadOnly} from "../../../config/util/setReadOnly";
37import {lockScreen} from "../../../dialog/processSystem";
38import {newFile} from "../../../util/newFile";
39import {openCard} from "../../../card/openCard";
40import {syncGuide} from "../../../sync/syncGuide";
41import {Wnd} from "../../../layout/Wnd";
42import {unsplitWnd} from "../../../menus/tab";
43import {openFile} from "../../../editor/util";
44
45export const globalCommand = (command: string, app: App) => {
46 /// #if MOBILE
47 switch (command) {
48 case "fileTree":
49 openDock("file");
50 return true;
51 case "outline":
52 case "bookmark":
53 case "tag":
54 case "inbox":
55 openDock(command);
56 return true;
57 case "backlinks":
58 openDock("backlink");
59 return true;
60 case "mainMenu":
61 popMenu();
62 return true;
63 case "globalSearch":
64 popSearch(app);
65 return true;
66 case "recentDocs":
67 getRecentDocs(app);
68 return true;
69 }
70 /// #else
71 switch (command) {
72 case "fileTree":
73 getDockByType("file").toggleModel("file");
74 return true;
75 case "outline":
76 getDockByType("outline").toggleModel("outline");
77 return true;
78 case "bookmark":
79 case "tag":
80 case "inbox":
81 getDockByType(command).toggleModel(command);
82 return true;
83 case "backlinks":
84 getDockByType("backlink").toggleModel("backlink");
85 return true;
86 case "graphView":
87 getDockByType("graph").toggleModel("graph");
88 return true;
89 case "globalGraph":
90 getDockByType("globalGraph").toggleModel("globalGraph");
91 return true;
92 case "config":
93 openSetting(app);
94 return true;
95 case "globalSearch":
96 openSearch({
97 app,
98 hotkey: Constants.DIALOG_GLOBALSEARCH,
99 key: (getSelection().rangeCount > 0 ? getSelection().getRangeAt(0) : document.createRange()).toString()
100 });
101 return true;
102 case "stickSearch":
103 openGlobalSearch(app, (getSelection().rangeCount > 0 ? getSelection().getRangeAt(0) : document.createRange()).toString(), true);
104 return true;
105 case "goBack":
106 goBack(app);
107 return true;
108 case "goForward":
109 goForward(app);
110 return true;
111 case "goToTab1":
112 switchTabByIndex(0);
113 return true;
114 case "goToTab2":
115 switchTabByIndex(1);
116 return true;
117 case "goToTab3":
118 switchTabByIndex(2);
119 return true;
120 case "goToTab4":
121 switchTabByIndex(3);
122 return true;
123 case "goToTab5":
124 switchTabByIndex(4);
125 return true;
126 case "goToTab6":
127 switchTabByIndex(5);
128 return true;
129 case "goToTab7":
130 switchTabByIndex(6);
131 return true;
132 case "goToTab8":
133 switchTabByIndex(7);
134 return true;
135 case "goToTab9":
136 switchTabByIndex(-1);
137 return true;
138 case "goToTabNext":
139 switchTabByIndex(-3);
140 return true;
141 case "goToTabPrev":
142 switchTabByIndex(-2);
143 return true;
144 case "mainMenu":
145 if (!isWindow()) {
146 workspaceMenu(app, document.querySelector("#barWorkspace").getBoundingClientRect());
147 }
148 return true;
149 case "recentDocs":
150 openRecentDocs();
151 return true;
152 case "recentClosed":
153 if (window.siyuan.closedTabs.length > 0) {
154 const closeData = window.siyuan.closedTabs.pop();
155 const childData = closeData.children as ILayoutJSON;
156 if (childData.instance === "Editor") {
157 openFile({
158 app,
159 fileName: closeData.title,
160 id: childData.blockId,
161 rootID: childData.rootId,
162 mode: childData.mode,
163 action: [childData.action]
164 });
165 } else if (childData.instance === "Asset") {
166 openFile({
167 app,
168 assetPath: childData.path,
169 page: childData.page,
170 });
171 } else if (childData.instance === "Backlink") {
172 openBacklink({
173 app,
174 blockId: childData.blockId,
175 rootId: childData.rootId,
176 title: closeData.title,
177 });
178 } else if (childData.instance === "Graph") {
179 openGraph({
180 app,
181 blockId: childData.blockId,
182 rootId: childData.rootId,
183 title: closeData.title
184 });
185 } else if (childData.instance === "Outline") {
186 openOutline({
187 app,
188 rootId: childData.blockId,
189 title: closeData.title,
190 isPreview: childData.isPreview
191 });
192 } else if (childData.instance === "Search") {
193 openFile({
194 app,
195 searchData: childData.config,
196 });
197 } else if (childData.instance === "Custom") {
198 openFile({
199 app,
200 custom: {
201 icon: closeData.icon,
202 title: closeData.title,
203 data: childData.customModelData,
204 id: childData.customModelType
205 },
206 });
207 }
208 }
209 return true;
210 case "toggleDock":
211 toggleDockBar(document.querySelector("#barDock use"));
212 return true;
213 case "toggleWin":
214 /// #if !BROWSER
215 ipcRenderer.send(Constants.SIYUAN_CMD, "hide");
216 ipcRenderer.send(Constants.SIYUAN_CMD, "minimize");
217 /// #endif
218 return true;
219 }
220 if (command === "goToEditTabNext" || command === "goToEditTabPrev") {
221 let currentTabElement = document.querySelector(".layout__wnd--active ul.layout-tab-bar > .item--focus");
222 if (!currentTabElement) {
223 currentTabElement = document.querySelector("ul.layout-tab-bar > .item--focus");
224 }
225 if (!currentTabElement) {
226 return true;
227 }
228 const tabs = getAllTabs().sort((itemA, itemB) => {
229 return itemA.headElement.getAttribute("data-activetime") > itemB.headElement.getAttribute("data-activetime") ? -1 : 1;
230 });
231 const currentId = currentTabElement.getAttribute("data-id");
232 tabs.find((item, index) => {
233 if (currentId === item.id) {
234 let newItem: Tab;
235 if (command === "goToEditTabPrev") {
236 if (index === 0) {
237 newItem = tabs[tabs.length - 1];
238 } else {
239 newItem = tabs[index - 1];
240 }
241 } else {
242 if (index === tabs.length - 1) {
243 newItem = tabs[0];
244 } else {
245 newItem = tabs[index + 1];
246 }
247 }
248 const tab = getInstanceById(newItem.id) as Tab;
249 tab.parent.switchTab(newItem.headElement);
250 tab.parent.showHeading();
251 }
252 });
253 return true;
254 }
255 if (command === "closeUnmodified") {
256 const tab = getActiveTab(false);
257 if (tab) {
258 const unmodifiedTabs: Tab[] = [];
259 tab.parent.children.forEach((item: Tab) => {
260 const editor = item.model as Editor;
261 if (!editor || (editor.editor?.protyle && !editor.editor?.protyle.updated)) {
262 unmodifiedTabs.push(item);
263 }
264 });
265 if (unmodifiedTabs.length > 0) {
266 closeTabByType(tab, "other", unmodifiedTabs);
267 }
268 }
269 return true;
270 }
271 if (command === "unsplitAll") {
272 unsplitWnd(window.siyuan.layout.centerLayout, window.siyuan.layout.centerLayout, false);
273 return true;
274 }
275 if (command === "unsplit") {
276 const tab = getActiveTab(false);
277 if (tab) {
278 let wndsTemp: Wnd[] = [];
279 let layout = tab.parent.parent;
280 while (layout.id !== window.siyuan.layout.centerLayout.id) {
281 wndsTemp = [];
282 getAllWnds(layout, wndsTemp);
283 if (wndsTemp.length > 1) {
284 break;
285 } else {
286 layout = layout.parent;
287 }
288 }
289 unsplitWnd(tab.parent.parent.children[0], layout, true);
290 resizeTabs();
291 }
292 return true;
293 }
294 if (command === "closeTab") {
295 const activeTabElement = document.querySelector(".layout__tab--active");
296 if (activeTabElement && activeTabElement.getBoundingClientRect().width > 0) {
297 let type: TDock;
298 Array.from(activeTabElement.classList).find(item => {
299 if (item.startsWith("sy__")) {
300 type = item.replace("sy__", "") as TDock;
301 return true;
302 }
303 });
304 if (type) {
305 getDockByType(type)?.toggleModel(type, false, true);
306 }
307 return true;
308 }
309
310 const tab = getActiveTab();
311 if (tab) {
312 tab.parent.removeTab(tab.id);
313 return true;
314 }
315 // https://github.com/siyuan-note/siyuan/issues/14729
316 if (window.siyuan.blockPanels.length > 0) {
317 window.siyuan.blockPanels[window.siyuan.blockPanels.length - 1]?.destroy();
318 return true;
319 }
320 const noFocusTab = getActiveTab(false);
321 if (noFocusTab) {
322 noFocusTab.parent.removeTab(noFocusTab.id);
323 return true;
324 }
325 }
326 if (command === "closeOthers" || command === "closeAll") {
327 const tab = getActiveTab(false);
328 if (tab) {
329 closeTabByType(tab, command);
330 }
331 return true;
332 }
333 if (command === "closeLeft" || command === "closeRight") {
334 const tab = getActiveTab(false);
335 if (tab) {
336 const leftTabs: Tab[] = [];
337 const rightTabs: Tab[] = [];
338 let midIndex = -1;
339 tab.parent.children.forEach((item: Tab, index: number) => {
340 if (item.id === tab.id) {
341 midIndex = index;
342 }
343 if (midIndex === -1) {
344 leftTabs.push(item);
345 } else if (index > midIndex) {
346 rightTabs.push(item);
347 }
348 });
349 if (command === "closeLeft") {
350 if (leftTabs.length > 0) {
351 closeTabByType(tab, "other", leftTabs);
352 }
353 } else {
354 if (rightTabs.length > 0) {
355 closeTabByType(tab, "other", rightTabs);
356 }
357 }
358 }
359 return true;
360 }
361 if (command === "splitLR") {
362 const tab = getActiveTab(false);
363 if (tab) {
364 tab.parent.split("lr").addTab(copyTab(app, tab));
365 }
366 return true;
367 }
368 if (command === "splitTB") {
369 const tab = getActiveTab(false);
370 if (tab) {
371 tab.parent.split("tb").addTab(copyTab(app, tab));
372 }
373 return true;
374 }
375 if (command === "splitMoveB" || command === "splitMoveR") {
376 const tab = getActiveTab(false);
377 if (tab && tab.parent.children.length > 1) {
378 const newWnd = tab.parent.split(command === "splitMoveB" ? "tb" : "lr");
379 newWnd.headersElement.append(tab.headElement);
380 newWnd.headersElement.parentElement.classList.remove("fn__none");
381 newWnd.moveTab(tab);
382 resizeTabs();
383 }
384 return true;
385 }
386 if (command === "tabToWindow") {
387 const tab = getActiveTab(false);
388 if (tab) {
389 openNewWindow(tab);
390 }
391 return true;
392 }
393 /// #endif
394
395 switch (command) {
396 case "dailyNote":
397 newDailyNote(app);
398 return true;
399 case "dataHistory":
400 openHistory(app);
401 return true;
402 case "editReadonly":
403 setReadOnly(!window.siyuan.config.editor.readOnly);
404 return true;
405 case "lockScreen":
406 lockScreen(app);
407 return true;
408 case "newFile":
409 newFile({
410 app,
411 useSavePath: true
412 });
413 return true;
414 case "riffCard":
415 openCard(app);
416 return true;
417 case "selectOpen1":
418 selectOpenTab();
419 return true;
420 case "syncNow":
421 syncGuide(app);
422 return true;
423 }
424
425 return false;
426};