diff --git a/app/src/boot/globalShortcut.ts b/app/src/boot/globalShortcut.ts index 1691fb802..e5c031df3 100644 --- a/app/src/boot/globalShortcut.ts +++ b/app/src/boot/globalShortcut.ts @@ -53,6 +53,7 @@ import {reloadProtyle} from "../protyle/util/reload"; import {fullscreen} from "../protyle/breadcrumb/action"; import {setPadding} from "../protyle/ui/initUI"; import {openRecentDocs} from "../business/openRecentDocs"; +import {App} from "../index"; const getRightBlock = (element: HTMLElement, x: number, y: number) => { let index = 1; @@ -95,7 +96,7 @@ const switchDialogEvent = (event: MouseEvent, switchDialog: Dialog) => { } }; -export const globalShortcut = () => { +export const globalShortcut = (app: App) => { document.body.addEventListener("mouseleave", () => { if (window.siyuan.layout.leftDock) { window.siyuan.layout.leftDock.hideDock(); @@ -630,7 +631,7 @@ export const globalShortcut = () => { return; } if (!isTabWindow && !window.siyuan.config.readonly && matchHotKey(window.siyuan.config.keymap.general.config.custom, event)) { - openSetting(); + openSetting(app); event.preventDefault(); return; } diff --git a/app/src/boot/onGetConfig.ts b/app/src/boot/onGetConfig.ts index 0721aa883..a6b4d640f 100644 --- a/app/src/boot/onGetConfig.ts +++ b/app/src/boot/onGetConfig.ts @@ -26,6 +26,7 @@ import {initBar} from "../layout/topBar"; import {setProxy} from "../config/util/setProxy"; import {openChangelog} from "./openChangelog"; import {getIdFromSYProtocol, isSYProtocol} from "../util/pathName"; +import {App} from "../index"; const matchKeymap = (keymap: Record, key1: "general" | "editor", key2?: "general" | "insert" | "heading" | "list" | "table") => { if (key1 === "general") { @@ -84,7 +85,7 @@ const hasKeymap = (keymap: Record, key1: "general" | "edito return match; }; -export const onGetConfig = (isStart: boolean) => { +export const onGetConfig = (isStart: boolean, app:App) => { const matchKeymap1 = matchKeymap(Constants.SIYUAN_KEYMAP.general, "general"); const matchKeymap2 = matchKeymap(Constants.SIYUAN_KEYMAP.editor.general, "editor", "general"); const matchKeymap3 = matchKeymap(Constants.SIYUAN_KEYMAP.editor.insert, "editor", "insert"); @@ -130,7 +131,7 @@ export const onGetConfig = (isStart: boolean) => { if (!window.siyuan.config.uiLayout || (window.siyuan.config.uiLayout && !window.siyuan.config.uiLayout.left)) { window.siyuan.config.uiLayout = Constants.SIYUAN_EMPTY_LAYOUT; } - globalShortcut(); + globalShortcut(app); fetchPost("/api/system/getEmojiConf", {}, response => { window.siyuan.emojis = response.data as IEmoji[]; try { @@ -140,7 +141,7 @@ export const onGetConfig = (isStart: boolean) => { resetLayout(); } }); - initBar(); + initBar(app); setProxy(); initStatus(); initWindow(); diff --git a/app/src/config/bazaar.ts b/app/src/config/bazaar.ts index a2dadc9db..1b8e0d63d 100644 --- a/app/src/config/bazaar.ts +++ b/app/src/config/bazaar.ts @@ -12,6 +12,8 @@ import * as path from "path"; import {isBrowser} from "../util/functions"; import {setStorageVal} from "../protyle/util/compatibility"; import {hasClosestByAttribute, hasClosestByClassName} from "../protyle/util/hasClosest"; +import {Plugin} from "../plugin"; +import {App} from "../index"; export const bazaar = { element: undefined as Element, @@ -209,7 +211,7 @@ export const bazaar = { `; }, - _genMyHTML(bazaarType: TBazaarType) { + _genMyHTML(bazaarType: TBazaarType, app: App) { let url = "/api/bazaar/getInstalledTheme"; if (bazaarType === "icons") { url = "/api/bazaar/getInstalledIcon"; @@ -236,6 +238,16 @@ export const bazaar = { repoHash: item.repoHash, downloaded: true }; + let hasSetting = false; + if (bazaarType === "plugins") { + app.plugins.find((item: Plugin) => { + if (item.name === dataObj.name) { + // @ts-ignore + hasSetting = item.__proto__.hasOwnProperty("openSetting"); + return true; + } + }) + } html += `
@@ -247,6 +259,10 @@ export const bazaar = {
${item.preferredFunding ? `` : ""}
+ + + + @@ -327,7 +343,7 @@ export const bazaar = {
${data.preferredFunding ? - `` : + `` : `` } @@ -396,8 +412,8 @@ export const bazaar = { } readmeElement.classList.add("config-bazaar__readme--show"); }, - bindEvent() { - this._genMyHTML("themes"); + bindEvent(app: App) { + this._genMyHTML("themes", app); bazaar.element.firstElementChild.addEventListener("click", (event) => { let target = event.target as HTMLElement; const dataElement = hasClosestByAttribute(target, "data-obj", null); @@ -429,7 +445,7 @@ export const bazaar = { } }); target.classList.remove("b3-button--outline"); - this._genMyHTML(type.replace("my", "").toLowerCase() + "s" as TBazaarType); + this._genMyHTML(type.replace("my", "").toLowerCase() + "s" as TBazaarType, app); } event.preventDefault(); event.stopPropagation(); @@ -462,7 +478,7 @@ export const bazaar = { exportLayout(true); return; } - bazaar._genMyHTML(bazaarType); + bazaar._genMyHTML(bazaarType, app); bazaar._onBazaar(response, bazaarType, ["themes", "icons"].includes(bazaarType)); }); } @@ -494,7 +510,7 @@ export const bazaar = { update: true, }, response => { // 更新主题后不需要对该主题进行切换 https://github.com/siyuan-note/siyuan/issues/4966 - this._genMyHTML(bazaarType); + this._genMyHTML(bazaarType, app); bazaar._onBazaar(response, bazaarType, ["icons"].includes(bazaarType)); // https://github.com/siyuan-note/siyuan/issues/5411 if (bazaarType === "themes" && ( @@ -536,7 +552,7 @@ export const bazaar = { fetchPost(url, { packageName }, response => { - this._genMyHTML(bazaarType); + this._genMyHTML(bazaarType, app); bazaar._onBazaar(response, bazaarType, ["themes", "icons"].includes(bazaarType)); // TODO destroy plugin if (bazaarType === "plugins") { @@ -555,7 +571,7 @@ export const bazaar = { fetchPost("/api/setting/setAppearance", Object.assign({}, window.siyuan.config.appearance, { icon: packageName, }), (appearanceResponse) => { - this._genMyHTML(bazaarType); + this._genMyHTML(bazaarType, app); fetchPost("/api/bazaar/getBazaarIcon", {}, response => { response.data.appearance = appearanceResponse.data; bazaar._onBazaar(response, "icons", true); @@ -575,7 +591,7 @@ export const bazaar = { window.siyuan.config.appearance.themeJS) { exportLayout(true); } else { - this._genMyHTML("themes"); + this._genMyHTML("themes", app); fetchPost("/api/bazaar/getBazaarTheme", {}, response => { response.data.appearance = appearanceResponse.data; bazaar._onBazaar(response, "themes", true); @@ -587,6 +603,16 @@ export const bazaar = { event.preventDefault(); event.stopPropagation(); break; + } else if (type === "setting") { + app.plugins.find((item: Plugin) => { + if (item.name === dataObj.name) { + item.openSetting() + return true + } + }) + event.preventDefault(); + event.stopPropagation(); + break; } else if (type === "plugin-enable") { const itemElement = hasClosestByClassName(target, "b3-card"); if (itemElement) { diff --git a/app/src/config/index.ts b/app/src/config/index.ts index 7f5523424..3cf546dd8 100644 --- a/app/src/config/index.ts +++ b/app/src/config/index.ts @@ -13,8 +13,9 @@ import {query} from "./query"; import {Dialog} from "../dialog"; import {ai} from "./ai"; import {flashcard} from "./flashcard"; +import {App} from "../index"; -export const genItemPanel = (type: string, containerElement:Element) => { +export const genItemPanel = (type: string, containerElement:Element, app: App) => { switch (type) { case "filetree": containerElement.innerHTML = fileTree.genHTML(); @@ -54,7 +55,7 @@ export const genItemPanel = (type: string, containerElement:Element) => { case "bazaar": bazaar.element = containerElement; containerElement.innerHTML = bazaar.genHTML(); - bazaar.bindEvent(); + bazaar.bindEvent(app); break; case "account": containerElement.innerHTML = account.genHTML(); @@ -81,7 +82,7 @@ export const genItemPanel = (type: string, containerElement:Element) => { } }; -export const openSetting = () => { +export const openSetting = (app:App) => { const exitDialog = window.siyuan.dialogs.find((item) => { if (item.element.querySelector(".config__tab-container")) { item.destroy(); @@ -126,7 +127,7 @@ export const openSetting = () => { width: "90vw", }); - initConfigSearch(dialog.element); + initConfigSearch(dialog.element, app); (dialog.element.querySelector(".b3-dialog__container") as HTMLElement).style.maxWidth = "1280px"; dialog.element.querySelectorAll(".b3-tab-bar .b3-list-item").forEach(item => { item.addEventListener("click", () => { @@ -139,7 +140,7 @@ export const openSetting = () => { item.classList.add("b3-list-item--focus"); containerElement.classList.remove("fn__none"); if (containerElement.innerHTML === "" || type === "repos" || type === "bazaar") { - genItemPanel(type, containerElement); + genItemPanel(type, containerElement, app); } }); }); diff --git a/app/src/config/search.ts b/app/src/config/search.ts index 963fa9d10..7d4da8ecb 100644 --- a/app/src/config/search.ts +++ b/app/src/config/search.ts @@ -1,6 +1,7 @@ import {Constants} from "../constants"; import {genItemPanel} from "./index"; import {keymap} from "./keymap"; +import {App} from "../index"; const getLang = (keys: string[]) => { const langArray: string[] = []; @@ -10,7 +11,7 @@ const getLang = (keys: string[]) => { return langArray; }; -export const initConfigSearch = (element: HTMLElement) => { +export const initConfigSearch = (element: HTMLElement, app: App) => { const configIndex = [ // 编辑器 getLang(["config", "fullWidth", @@ -115,7 +116,7 @@ export const initConfigSearch = (element: HTMLElement) => { // 右侧面板过滤 const panelElement = element.querySelector(`.config__tab-container[data-name="${type}"]`); if (panelElement.innerHTML === "") { - genItemPanel(type, panelElement); + genItemPanel(type, panelElement, app); } if (type === "keymap") { const searchElement = keymap.element.querySelector("#keymapInput") as HTMLInputElement; diff --git a/app/src/index.ts b/app/src/index.ts index 5c1d1d9ae..1f928ffa5 100644 --- a/app/src/index.ts +++ b/app/src/index.ts @@ -171,7 +171,7 @@ export class App { fetchPost("/api/setting/getCloudUser", {}, userResponse => { window.siyuan.user = userResponse.data; loadPlugins(siyuanApp); - onGetConfig(response.data.start); + onGetConfig(response.data.start, siyuanApp); account.onSetaccount(); resizeDrag(); setTitle(window.siyuan.languages.siyuanNote); diff --git a/app/src/layout/topBar.ts b/app/src/layout/topBar.ts index e81301e60..26be5946b 100644 --- a/app/src/layout/topBar.ts +++ b/app/src/layout/topBar.ts @@ -9,6 +9,7 @@ import {MenuItem} from "../menus/Menu"; import {setMode} from "../util/assets"; import {openSetting} from "../config"; import {openSearch} from "../search/spread"; +import {App} from "../index"; export const updateEditModeElement = () => { const target = document.querySelector("#barReadonly"); @@ -23,7 +24,7 @@ export const updateEditModeElement = () => { } }; -export const initBar = () => { +export const initBar = (app: App) => { const toolbarElement = document.getElementById("toolbar"); toolbarElement.innerHTML = `
@@ -68,7 +69,7 @@ export const initBar = () => { event.stopPropagation(); break; } else if (target.id === "barWorkspace") { - workspaceMenu(target.getBoundingClientRect()); + workspaceMenu(app, target.getBoundingClientRect()); event.stopPropagation(); break; } else if (target.id === "barReadonly") { @@ -113,7 +114,7 @@ export const initBar = () => { break; } else if (target.id === "toolbarVIP") { if (!window.siyuan.config.readonly) { - const dialogSetting = openSetting(); + const dialogSetting = openSetting(app); dialogSetting.element.querySelector('.b3-tab-bar [data-name="account"]').dispatchEvent(new CustomEvent("click")); } event.stopPropagation(); diff --git a/app/src/menus/workspace.ts b/app/src/menus/workspace.ts index 4d7dd6f32..b59810e9f 100644 --- a/app/src/menus/workspace.ts +++ b/app/src/menus/workspace.ts @@ -22,6 +22,7 @@ import {viewCards} from "../card/viewCards"; import {Dialog} from "../dialog"; import {hasClosestByClassName} from "../protyle/util/hasClosest"; import {confirmDialog} from "../dialog/confirmDialog"; +import {App} from "../index"; const togglePinDock = (dock: Dock, icon: string) => { return { @@ -34,7 +35,7 @@ const togglePinDock = (dock: Dock, icon: string) => { }; }; -export const workspaceMenu = (rect: DOMRect) => { +export const workspaceMenu = (app:App, rect: DOMRect) => { if (!window.siyuan.menus.menu.element.classList.contains("fn__none") && window.siyuan.menus.menu.element.getAttribute("data-name") === "barWorkspace") { window.siyuan.menus.menu.remove(); @@ -49,7 +50,7 @@ export const workspaceMenu = (rect: DOMRect) => { icon: "iconSettings", accelerator: window.siyuan.config.keymap.general.config.custom, click: () => { - openSetting(); + openSetting(app); } }).element); } diff --git a/app/src/plugin/index.ts b/app/src/plugin/index.ts index f01bacdd2..ec5b6b5b9 100644 --- a/app/src/plugin/index.ts +++ b/app/src/plugin/index.ts @@ -1,20 +1,26 @@ import {App} from "../index"; import {EventBus} from "./EventBus"; +import {fetchPost} from "../util/fetch"; export class Plugin { public i18n: IObject; public eventBus: EventBus; + public data: any = {}; + public name: string; constructor(options: { app: App, - id: string, name: string, i18n: IObject }) { this.i18n = options.i18n; + this.name = options.name; this.eventBus = new EventBus(options.name); } + public onload() { + } + public addTopBar(options: { icon: string, title: string, @@ -31,7 +37,36 @@ export class Plugin { return iconElement; } - public onload() { - console.log("Hello, world!"); + public openSetting() { + } + + public loadData(storageName: string) { + if (typeof this.data[storageName] === "undefined") { + this.data[storageName] = ""; + } + return new Promise((resolve) => { + fetchPost("/api/file/getFile", {path: `/data/storage/petal/${this.name}/${storageName}`}, (response) => { + if (response.code === 404) { + this.data[storageName] = ""; + } else { + this.data[storageName] = response; + } + resolve(this.data[storageName]); + }); + }); + } + + public saveData(storageName: string, data: any) { + return new Promise((resolve) => { + const pathString = `/data/storage/petal/${this.name}/${storageName}`; + const file = new File([new Blob([data])], pathString.split('/').pop()); + const formData = new FormData(); + formData.append('path', pathString); + formData.append('file', file); + formData.append('isDir', "false"); + fetchPost("/api/file/putFile", formData, (response) => { + resolve(response); + }); + }); } } diff --git a/app/src/plugin/loader.ts b/app/src/plugin/loader.ts index d83166081..8453711d1 100644 --- a/app/src/plugin/loader.ts +++ b/app/src/plugin/loader.ts @@ -18,7 +18,7 @@ const runCode = (code: string, sourceURL: string) => { export const loadPlugins = (app: App) => { fetchPost("/api/petal/loadPetals", {}, response => { let css = ""; - response.data.forEach((item: { id: string, name: string, js: string, css: string, i18n: IObject }) => { + response.data.forEach((item: { name: string, js: string, css: string, i18n: IObject }) => { const exportsObj: { [key: string]: any } = {}; const moduleObj = {exports: exportsObj}; try { @@ -39,7 +39,6 @@ export const loadPlugins = (app: App) => { const plugin = new pluginClass({ app, name: item.name, - id: item.id, i18n: item.i18n }); app.plugins.push(plugin); diff --git a/app/src/types/index.d.ts b/app/src/types/index.d.ts index d75d03144..b90f74263 100644 --- a/app/src/types/index.d.ts +++ b/app/src/types/index.d.ts @@ -388,12 +388,12 @@ declare interface IEditor { } declare interface IWebSocketData { - cmd: string + cmd?: string callback?: string - data: any + data?: any msg: string code: number - sid: string + sid?: string } declare interface IAppearance { diff --git a/app/src/util/fetch.ts b/app/src/util/fetch.ts index c15612903..42430c61c 100644 --- a/app/src/util/fetch.ts +++ b/app/src/util/fetch.ts @@ -28,8 +28,23 @@ export const fetchPost = (url: string, data?: any, cb?: (response: IWebSocketDat init.headers = headers; } fetch(url, init).then((response) => { - return response.json(); + if (response.status === 404) { + cb({ + data: null, + msg: response.statusText, + code: response.status, + }); + } else { + if (response.headers.get("content-type").indexOf("application/json") > -1) { + return response.json(); + } else { + return response.text(); + } + } }).then((response: IWebSocketData) => { + if (!response) { + return; + } if (["/api/search/searchRefBlock", "/api/graph/getGraph", "/api/graph/getLocalGraph"].includes(url)) { if (response.data.reqId && window.siyuan.reqIds[url] && window.siyuan.reqIds[url] > response.data.reqId) { return; diff --git a/app/src/window/index.ts b/app/src/window/index.ts index 09fc9ac37..d6520ce8d 100644 --- a/app/src/window/index.ts +++ b/app/src/window/index.ts @@ -135,7 +135,7 @@ class App { fetchPost("/api/setting/getCloudUser", {}, userResponse => { window.siyuan.user = userResponse.data; loadPlugins(siyuanApp); - init(); + init(siyuanApp); setTitle(window.siyuan.languages.siyuanNote); initMessage(); }); diff --git a/app/src/window/init.ts b/app/src/window/init.ts index e11b81da8..f1eb1da40 100644 --- a/app/src/window/init.ts +++ b/app/src/window/init.ts @@ -9,10 +9,11 @@ import {initAssets, setInlineStyle} from "../util/assets"; import {renderSnippet} from "../config/util/snippets"; import {getSearch} from "../util/functions"; import {initWindow} from "../boot/onGetConfig"; +import {App} from "../index"; -export const init = () => { +export const init = (app:App) => { webFrame.setZoomFactor(window.siyuan.storage[Constants.LOCAL_ZOOM]); - globalShortcut(); + globalShortcut(app); fetchPost("/api/system/getEmojiConf", {}, response => { window.siyuan.emojis = response.data as IEmoji[];