diff --git a/app/appearance/icons/ant/icon.js b/app/appearance/icons/ant/icon.js index 33715c1bf..d9d700dca 100644 --- a/app/appearance/icons/ant/icon.js +++ b/app/appearance/icons/ant/icon.js @@ -1,5 +1,8 @@ document.body.insertAdjacentHTML('afterBegin', ` + + + diff --git a/app/appearance/icons/ant/icon.json b/app/appearance/icons/ant/icon.json index f5bf7aeaf..f90fa6105 100644 --- a/app/appearance/icons/ant/icon.json +++ b/app/appearance/icons/ant/icon.json @@ -2,5 +2,5 @@ "name": "ant", "author": "Vanessa", "url": "https://github.com/Vanessa219", - "version": "1.15.1" + "version": "1.16.1" } diff --git a/app/appearance/icons/index.html b/app/appearance/icons/index.html index 67d7cee3e..52d9f0801 100644 --- a/app/appearance/icons/index.html +++ b/app/appearance/icons/index.html @@ -28,6 +28,12 @@

SiYuan

+
+ + + + iconLayout +
diff --git a/app/appearance/icons/material/icon.js b/app/appearance/icons/material/icon.js index ef3b53e0d..c6471ee91 100644 --- a/app/appearance/icons/material/icon.js +++ b/app/appearance/icons/material/icon.js @@ -1,5 +1,8 @@ document.body.insertAdjacentHTML('afterbegin', ` + + + diff --git a/app/appearance/icons/material/icon.json b/app/appearance/icons/material/icon.json index f45e77749..15629a592 100644 --- a/app/appearance/icons/material/icon.json +++ b/app/appearance/icons/material/icon.json @@ -2,5 +2,5 @@ "name": "material", "author": "Vanessa", "url": "https://github.com/Vanessa219", - "version": "1.15.1" + "version": "1.16.1" } diff --git a/app/appearance/langs/en_US.json b/app/appearance/langs/en_US.json index 65929f856..6508158b0 100644 --- a/app/appearance/langs/en_US.json +++ b/app/appearance/langs/en_US.json @@ -1,4 +1,5 @@ { + "saveLayout": "Save Layout", "ai": "Artificial Intelligence", "aiContinueWrite": "Continue writing", "aiTranslate": "Translate", diff --git a/app/appearance/langs/es_ES.json b/app/appearance/langs/es_ES.json index d45901647..7d1973111 100644 --- a/app/appearance/langs/es_ES.json +++ b/app/appearance/langs/es_ES.json @@ -1,4 +1,5 @@ { + "saveLayout": "Guardar diseño", "ai": "Inteligencia Artificial", "aiContinueWrite": "Continuar escribiendo", "aiTranslate": "Traducir", diff --git a/app/appearance/langs/fr_FR.json b/app/appearance/langs/fr_FR.json index a6f6f2060..b5abc7131 100644 --- a/app/appearance/langs/fr_FR.json +++ b/app/appearance/langs/fr_FR.json @@ -1,4 +1,5 @@ { + "saveLayout": "Enregistrer la mise en page", "ai": "Intelligence Artificielle", "aiContinueWrite": "Continuer à écrire", "aiTranslate": "Traduire", diff --git a/app/appearance/langs/zh_CHT.json b/app/appearance/langs/zh_CHT.json index b31d9443f..5ba5564f8 100644 --- a/app/appearance/langs/zh_CHT.json +++ b/app/appearance/langs/zh_CHT.json @@ -1,4 +1,5 @@ { + "saveLayout": "保存佈局", "ai": "人工智能", "aiContinueWrite": "續寫", "aiTranslate": "翻譯", diff --git a/app/appearance/langs/zh_CN.json b/app/appearance/langs/zh_CN.json index 18d1002ea..08aa9b205 100644 --- a/app/appearance/langs/zh_CN.json +++ b/app/appearance/langs/zh_CN.json @@ -1,4 +1,5 @@ { + "saveLayout": "保存布局", "ai": "人工智能", "aiContinueWrite": "续写", "aiTranslate": "翻译", diff --git a/app/src/constants.ts b/app/src/constants.ts index 6432d2d17..6f2241e1d 100644 --- a/app/src/constants.ts +++ b/app/src/constants.ts @@ -78,6 +78,7 @@ export abstract class Constants { public static readonly LOCAL_EXPORTIMG = "local-exportimg"; public static readonly LOCAL_BAZAAR = "local-bazaar"; public static readonly LOCAL_PDFTHEME = "local-pdftheme"; + public static readonly LOCAL_LAYOUTS = "local-layouts"; // timeout public static readonly TIMEOUT_DBLCLICK = 190; diff --git a/app/src/layout/util.ts b/app/src/layout/util.ts index ca9fa11bb..39dc0470c 100644 --- a/app/src/layout/util.ts +++ b/app/src/layout/util.ts @@ -145,7 +145,7 @@ export const resetLayout = () => { }); }; -export const exportLayout = (reload: boolean, cb?: () => void) => { +export const exportLayout = (reload: boolean, cb?: () => void, onlyData = false) => { const useElement = document.querySelector("#barDock use"); if (!useElement) { return; @@ -158,7 +158,10 @@ export const exportLayout = (reload: boolean, cb?: () => void) => { right: dockToJSON(window.siyuan.layout.rightDock), }; layoutToJSON(window.siyuan.layout.layout, layoutJSON.layout); - fetchPost("/api/system/setUILayout", {layout: layoutJSON, exit: typeof cb !== "undefined"}, () => { + if (onlyData) { + return layoutJSON; + } + fetchPost("/api/system/setUILayout", {layout: layoutJSON}, () => { if (reload) { window.location.reload(); } else if (cb) { diff --git a/app/src/menus/workspace.ts b/app/src/menus/workspace.ts index 34c7f7a8a..8c77afbdc 100644 --- a/app/src/menus/workspace.ts +++ b/app/src/menus/workspace.ts @@ -12,13 +12,15 @@ import {setStorageVal, writeText} from "../protyle/util/compatibility"; import {openCard} from "../card/openCard"; import {openSetting} from "../config"; import {getAllDocks} from "../layout/getAll"; -import {getDockByType} from "../layout/util"; +import {exportLayout, getDockByType} from "../layout/util"; import {lockScreen} from "../dialog/processSystem"; import {showMessage} from "../dialog/message"; import {unicode2Emoji} from "../emoji"; import {Dock} from "../layout/dock"; import {escapeHtml} from "../util/escape"; import {viewCards} from "../card/viewCards"; +import {Dialog} from "../dialog"; +import {hasClosestByClassName} from "../protyle/util/hasClosest"; const togglePinDock = (dock: Dock, icon: string) => { return { @@ -101,6 +103,88 @@ export const workspaceMenu = (rect: DOMRect) => { }).element); } /// #endif + const layoutSubMenu: IMenu[] = [{ + iconHTML: Constants.ZWSP, + label: window.siyuan.languages.saveLayout, + click() { + const saveDialog = new Dialog({ + title: window.siyuan.languages.saveLayout, + content: `
+ +
+
+
+ +
`, + width: "520px", + }); + const btnsElement = saveDialog.element.querySelectorAll(".b3-button"); + saveDialog.bindInput(saveDialog.element.querySelector("input"), () => { + btnsElement[1].dispatchEvent(new CustomEvent("click")); + }); + btnsElement[0].addEventListener("click", () => { + saveDialog.destroy(); + }); + btnsElement[1].addEventListener("click", () => { + const value = saveDialog.element.querySelector("input").value; + if (!value) { + showMessage(window.siyuan.languages["_kernel"]["142"]); + return; + } + const hadName = window.siyuan.storage[Constants.LOCAL_LAYOUTS].find((item: ISaveLayout) => { + if (item.name === value) { + showMessage(window.siyuan.languages.duplicate); + return true; + } + }) + if (hadName) { + return; + } + window.siyuan.storage[Constants.LOCAL_LAYOUTS].push({ + name: value, + layout: exportLayout(false, undefined, true) + }) + setStorageVal(Constants.LOCAL_LAYOUTS, window.siyuan.storage[Constants.LOCAL_LAYOUTS]); + saveDialog.destroy(); + }); + } + }]; + window.siyuan.storage[Constants.LOCAL_LAYOUTS].forEach((item: ISaveLayout) => { + layoutSubMenu.push({ + iconHTML: Constants.ZWSP, + label: `
+ ${item.name} + + +
`, + bind(menuElement) { + menuElement.addEventListener("click", (event) => { + if (hasClosestByClassName(event.target as HTMLElement, "fn__a")) { + event.preventDefault(); + event.stopPropagation(); + window.siyuan.storage[Constants.LOCAL_LAYOUTS].find((layoutItem: ISaveLayout, index: number) => { + if (layoutItem.name === item.name) { + menuElement.remove(); + window.siyuan.storage[Constants.LOCAL_LAYOUTS].splice(index, 1); + setStorageVal(Constants.LOCAL_LAYOUTS, window.siyuan.storage[Constants.LOCAL_LAYOUTS]); + return true; + } + }) + return; + } + fetchPost("/api/system/setUILayout", {layout: item.layout}, () => { + window.location.reload(); + }); + }); + } + }); + }); + window.siyuan.menus.menu.append(new MenuItem({ + label: window.siyuan.languages.layout, + icon: "iconLayout", + type: "submenu", + submenu: layoutSubMenu + }).element); window.siyuan.menus.menu.append(new MenuItem({type: "separator"}).element); if (!window.siyuan.config.readonly) { if (getOpenNotebookCount() < 2) { diff --git a/app/src/protyle/util/compatibility.ts b/app/src/protyle/util/compatibility.ts index 1420b5739..5ddae370d 100644 --- a/app/src/protyle/util/compatibility.ts +++ b/app/src/protyle/util/compatibility.ts @@ -155,6 +155,7 @@ export const getLocalStorage = (cb: () => void) => { layoutTab: 0 }; defaultStorage[Constants.LOCAL_PDFTHEME] = {light: "light", dark: "dark", annoColor: "var(--b3-pdf-background1)"}; + defaultStorage[Constants.LOCAL_LAYOUTS] = []; // {name: "", layout:{}} defaultStorage[Constants.LOCAL_BAZAAR] = { theme: "0", template: "0", @@ -208,7 +209,7 @@ export const getLocalStorage = (cb: () => void) => { [Constants.LOCAL_EXPORTIMG, Constants.LOCAL_SEARCHEKEYS, Constants.LOCAL_PDFTHEME, Constants.LOCAL_BAZAAR, Constants.LOCAL_EXPORTWORD, Constants.LOCAL_EXPORTPDF, Constants.LOCAL_DOCINFO, Constants.LOCAL_FONTSTYLES, Constants.LOCAL_SEARCHEDATA, - Constants.LOCAL_ZOOM, Constants.LOCAL_SEARCHEKEY].forEach((key) => { + Constants.LOCAL_ZOOM, Constants.LOCAL_SEARCHEKEY, Constants.LOCAL_LAYOUTS].forEach((key) => { if (typeof response.data[key] === "string") { try { window.siyuan.storage[key] = Object.assign(defaultStorage[key], JSON.parse(response.data[key])); diff --git a/app/src/types/index.d.ts b/app/src/types/index.d.ts index 62102a1d2..b5e3b8664 100644 --- a/app/src/types/index.d.ts +++ b/app/src/types/index.d.ts @@ -56,6 +56,11 @@ interface Window { openFileByURL(URL: string): boolean } +interface ISaveLayout { + name: string, + layout: IObject +} + interface IWorkspace { path: string closed: boolean