import { Plugin, showMessage, confirm, Dialog, Menu, openTab, adaptHotkey, getFrontend, getBackend, IModel, Setting, fetchPost, Protyle, openWindow, IOperation } from "siyuan"; import "@/index.scss"; import HelloExample from "@/hello.svelte"; import SettingPannel from "@/libs/setting-panel.svelte"; import { SettingUtils } from "./libs/setting-utils"; const STORAGE_NAME = "menu-config"; const TAB_TYPE = "custom_tab"; const DOCK_TYPE = "dock_tab"; export default class PluginSample extends Plugin { private customTab: () => IModel; private isMobile: boolean; private blockIconEventBindThis = this.blockIconEvent.bind(this); private settingUtils: SettingUtils; async onload() { this.data[STORAGE_NAME] = { readonlyText: "Readonly" }; console.log("loading plugin-sample", this.i18n); const frontEnd = getFrontend(); this.isMobile = frontEnd === "mobile" || frontEnd === "browser-mobile"; // 图标的制作参见帮助文档 this.addIcons(` `); const topBarElement = this.addTopBar({ icon: "iconFace", title: this.i18n.addTopBarIcon, position: "right", callback: () => { if (this.isMobile) { this.addMenu(); } else { let rect = topBarElement.getBoundingClientRect(); // 如果被隐藏,则使用更多按钮 if (rect.width === 0) { rect = document.querySelector("#barMore").getBoundingClientRect(); } if (rect.width === 0) { rect = document.querySelector("#barPlugins").getBoundingClientRect(); } this.addMenu(rect); } } }); const statusIconTemp = document.createElement("template"); statusIconTemp.innerHTML = `
`; statusIconTemp.content.firstElementChild.addEventListener("click", () => { confirm("⚠️", this.i18n.confirmRemove.replace("${name}", this.name), () => { this.removeData(STORAGE_NAME).then(() => { this.data[STORAGE_NAME] = { readonlyText: "Readonly" }; showMessage(`[${this.name}]: ${this.i18n.removedData}`); }); }); }); this.addStatusBar({ element: statusIconTemp.content.firstElementChild as HTMLElement, }); this.addCommand({ langKey: "showDialog", hotkey: "⇧⌘O", callback: () => { this.showDialog(); }, fileTreeCallback: (file: any) => { console.log(file, "fileTreeCallback"); }, editorCallback: (protyle: any) => { console.log(protyle, "editorCallback"); }, dockCallback: (element: HTMLElement) => { console.log(element, "dockCallback"); }, }); this.addCommand({ langKey: "getTab", hotkey: "⇧⌘M", globalCallback: () => { console.log(this.getOpenedTab()); }, }); this.addDock({ config: { position: "LeftBottom", size: { width: 200, height: 0 }, icon: "iconSaving", title: "Custom Dock", }, data: { text: "This is my custom dock" }, type: DOCK_TYPE, init() { this.element.innerHTML = `
${this.data.text}
`; }, destroy() { console.log("destroy dock:", DOCK_TYPE); } }); this.settingUtils = new SettingUtils(this, STORAGE_NAME); this.settingUtils.addItem({ key: "Input", value: "", type: "textinput", title: "Readonly text", description: "Input description", }); this.settingUtils.addItem({ key: "InputArea", value: "", type: "textarea", title: "Readonly text", description: "Input description", }); this.settingUtils.addItem({ key: "Check", value: true, type: "checkbox", title: "Checkbox text", description: "Check description", }); this.settingUtils.addItem({ key: "Select", value: 1, type: "select", title: "Readonly text", description: "Select description", select: { options: [ { val: 1, text: "Option 1" }, { val: 2, text: "Option 2" } ] } }); this.settingUtils.addItem({ key: "Slider", value: 50, type: "slider", title: "Slider text", description: "Slider description", slider: { min: 0, max: 100, step: 1, } }); this.settingUtils.addItem({ key: "Btn", value: "", type: "button", title: "Button", description: "Button description", button: { label: "Button", callback: () => { showMessage("Button clicked"); } } }); this.protyleSlash = [{ filter: ["insert emoji 😊", "插入表情 😊", "crbqwx"], html: `
${this.i18n.insertEmoji}😊
`, id: "insertEmoji", callback(protyle: Protyle) { protyle.insert("😊"); } }]; console.log(this.i18n.helloPlugin); } onLayoutReady() { // this.loadData(STORAGE_NAME); this.settingUtils.load(); console.log(`frontend: ${getFrontend()}; backend: ${getBackend()}`); let tabDiv = document.createElement("div"); new HelloExample({ target: tabDiv, props: { app: this.app, } }); this.customTab = this.addTab({ type: TAB_TYPE, init() { this.element.appendChild(tabDiv); console.log(this.element); }, beforeDestroy() { console.log("before destroy tab:", TAB_TYPE); }, destroy() { console.log("destroy tab:", TAB_TYPE); } }); } async onunload() { console.log(this.i18n.byePlugin); await this.settingUtils.save(); showMessage("Goodbye SiYuan Plugin"); console.log("onunload"); } /** * A custom setting pannel provided by svelte */ openDIYSetting(): void { let dialog = new Dialog({ title: "SettingPannel", content: `
`, width: "600px", destroyCallback: (options) => { console.log("destroyCallback", options); //You'd better destroy the component when the dialog is closed pannel.$destroy(); } }); let pannel = new SettingPannel({ target: dialog.element.querySelector("#SettingPanel"), }); } private eventBusPaste(event: any) { // 如果需异步处理请调用 preventDefault, 否则会进行默认处理 event.preventDefault(); // 如果使用了 preventDefault,必须调用 resolve,否则程序会卡死 event.detail.resolve({ textPlain: event.detail.textPlain.trim(), }); } private eventBusLog({ detail }: any) { console.log(detail); } private blockIconEvent({ detail }: any) { detail.menu.addItem({ iconHTML: "", label: this.i18n.removeSpace, click: () => { const doOperations: IOperation[] = []; detail.blockElements.forEach((item: HTMLElement) => { const editElement = item.querySelector('[contenteditable="true"]'); if (editElement) { editElement.textContent = editElement.textContent.replace(/ /g, ""); doOperations.push({ id: item.dataset.nodeId, data: item.outerHTML, action: "update" }); } }); detail.protyle.getInstance().transaction(doOperations); } }); } private showDialog() { let dialog = new Dialog({ title: "Hello World", content: `
`, width: this.isMobile ? "92vw" : "720px", destroyCallback(options) { // hello.$destroy(); }, }); new HelloExample({ target: dialog.element.querySelector("#helloPanel"), props: { app: this.app, } }); } private addMenu(rect?: DOMRect) { const menu = new Menu("topBarSample", () => { console.log(this.i18n.byeMenu); }); menu.addItem({ icon: "iconInfo", label: "Dialog(open help first)", accelerator: this.commands[0].customHotkey, click: () => { this.showDialog(); } }); if (!this.isMobile) { menu.addItem({ icon: "iconFace", label: "Open Custom Tab", click: () => { const tab = openTab({ app: this.app, custom: { icon: "iconFace", title: "Custom Tab", data: { text: "This is my custom tab", }, id: this.name + TAB_TYPE }, }); console.log(tab); } }); menu.addItem({ icon: "iconImage", label: "Open Asset Tab(open help first)", click: () => { const tab = openTab({ app: this.app, asset: { path: "assets/paragraph-20210512165953-ag1nib4.svg" } }); console.log(tab); } }); menu.addItem({ icon: "iconFile", label: "Open Doc Tab(open help first)", click: async () => { const tab = await openTab({ app: this.app, doc: { id: "20200812220555-lj3enxa", } }); console.log(tab); } }); menu.addItem({ icon: "iconSearch", label: "Open Search Tab", click: () => { const tab = openTab({ app: this.app, search: { k: "SiYuan" } }); console.log(tab); } }); menu.addItem({ icon: "iconRiffCard", label: "Open Card Tab", click: () => { const tab = openTab({ app: this.app, card: { type: "all" } }); console.log(tab); } }); menu.addItem({ icon: "iconLayout", label: "Open Float Layer(open help first)", click: () => { this.addFloatLayer({ ids: ["20210428212840-8rqwn5o", "20201225220955-l154bn4"], defIds: ["20230415111858-vgohvf3", "20200813131152-0wk5akh"], x: window.innerWidth - 768 - 120, y: 32 }); } }); menu.addItem({ icon: "iconOpenWindow", label: "Open Doc Window(open help first)", click: () => { openWindow({ doc: {id: "20200812220555-lj3enxa"} }); } }); } menu.addItem({ icon: "iconScrollHoriz", label: "Event Bus", type: "submenu", submenu: [{ icon: "iconSelect", label: "On ws-main", click: () => { this.eventBus.on("ws-main", this.eventBusLog); } }, { icon: "iconClose", label: "Off ws-main", click: () => { this.eventBus.off("ws-main", this.eventBusLog); } }, { icon: "iconSelect", label: "On click-blockicon", click: () => { this.eventBus.on("click-blockicon", this.blockIconEventBindThis); } }, { icon: "iconClose", label: "Off click-blockicon", click: () => { this.eventBus.off("click-blockicon", this.blockIconEventBindThis); } }, { icon: "iconSelect", label: "On click-pdf", click: () => { this.eventBus.on("click-pdf", this.eventBusLog); } }, { icon: "iconClose", label: "Off click-pdf", click: () => { this.eventBus.off("click-pdf", this.eventBusLog); } }, { icon: "iconSelect", label: "On click-editorcontent", click: () => { this.eventBus.on("click-editorcontent", this.eventBusLog); } }, { icon: "iconClose", label: "Off click-editorcontent", click: () => { this.eventBus.off("click-editorcontent", this.eventBusLog); } }, { icon: "iconSelect", label: "On click-editortitleicon", click: () => { this.eventBus.on("click-editortitleicon", this.eventBusLog); } }, { icon: "iconClose", label: "Off click-editortitleicon", click: () => { this.eventBus.off("click-editortitleicon", this.eventBusLog); } }, { icon: "iconSelect", label: "On open-noneditableblock", click: () => { this.eventBus.on("open-noneditableblock", this.eventBusLog); } }, { icon: "iconClose", label: "Off open-noneditableblock", click: () => { this.eventBus.off("open-noneditableblock", this.eventBusLog); } }, { icon: "iconSelect", label: "On loaded-protyle-static", click: () => { this.eventBus.on("loaded-protyle-static", this.eventBusLog); } }, { icon: "iconClose", label: "Off loaded-protyle-static", click: () => { this.eventBus.off("loaded-protyle-static", this.eventBusLog); } }, { icon: "iconSelect", label: "On loaded-protyle-dynamic", click: () => { this.eventBus.on("loaded-protyle-dynamic", this.eventBusLog); } }, { icon: "iconClose", label: "Off loaded-protyle-dynamic", click: () => { this.eventBus.off("loaded-protyle-dynamic", this.eventBusLog); } }, { icon: "iconSelect", label: "On destroy-protyle", click: () => { this.eventBus.on("destroy-protyle", this.eventBusLog); } }, { icon: "iconClose", label: "Off destroy-protyle", click: () => { this.eventBus.off("destroy-protyle", this.eventBusLog); } }, { icon: "iconSelect", label: "On open-menu-doctree", click: () => { this.eventBus.on("open-menu-doctree", this.eventBusLog); } }, { icon: "iconClose", label: "Off open-menu-doctree", click: () => { this.eventBus.off("open-menu-doctree", this.eventBusLog); } }, { icon: "iconSelect", label: "On open-menu-blockref", click: () => { this.eventBus.on("open-menu-blockref", this.eventBusLog); } }, { icon: "iconClose", label: "Off open-menu-blockref", click: () => { this.eventBus.off("open-menu-blockref", this.eventBusLog); } }, { icon: "iconSelect", label: "On open-menu-fileannotationref", click: () => { this.eventBus.on("open-menu-fileannotationref", this.eventBusLog); } }, { icon: "iconClose", label: "Off open-menu-fileannotationref", click: () => { this.eventBus.off("open-menu-fileannotationref", this.eventBusLog); } }, { icon: "iconSelect", label: "On open-menu-tag", click: () => { this.eventBus.on("open-menu-tag", this.eventBusLog); } }, { icon: "iconClose", label: "Off open-menu-tag", click: () => { this.eventBus.off("open-menu-tag", this.eventBusLog); } }, { icon: "iconSelect", label: "On open-menu-link", click: () => { this.eventBus.on("open-menu-link", this.eventBusLog); } }, { icon: "iconClose", label: "Off open-menu-link", click: () => { this.eventBus.off("open-menu-link", this.eventBusLog); } }, { icon: "iconSelect", label: "On open-menu-image", click: () => { this.eventBus.on("open-menu-image", this.eventBusLog); } }, { icon: "iconClose", label: "Off open-menu-image", click: () => { this.eventBus.off("open-menu-image", this.eventBusLog); } }, { icon: "iconSelect", label: "On open-menu-av", click: () => { this.eventBus.on("open-menu-av", this.eventBusLog); } }, { icon: "iconClose", label: "Off open-menu-av", click: () => { this.eventBus.off("open-menu-av", this.eventBusLog); } }, { icon: "iconSelect", label: "On open-menu-content", click: () => { this.eventBus.on("open-menu-content", this.eventBusLog); } }, { icon: "iconClose", label: "Off open-menu-content", click: () => { this.eventBus.off("open-menu-content", this.eventBusLog); } }, { icon: "iconSelect", label: "On open-menu-breadcrumbmore", click: () => { this.eventBus.on("open-menu-breadcrumbmore", this.eventBusLog); } }, { icon: "iconClose", label: "Off open-menu-breadcrumbmore", click: () => { this.eventBus.off("open-menu-breadcrumbmore", this.eventBusLog); } }, { icon: "iconSelect", label: "On input-search", click: () => { this.eventBus.on("input-search", this.eventBusLog); } }, { icon: "iconClose", label: "Off input-search", click: () => { this.eventBus.off("input-search", this.eventBusLog); } }, { icon: "iconSelect", label: "On paste", click: () => { this.eventBus.on("paste", this.eventBusPaste); } }, { icon: "iconClose", label: "Off paste", click: () => { this.eventBus.off("paste", this.eventBusPaste); } }, { icon: "iconSelect", label: "On open-siyuan-url-plugin", click: () => { this.eventBus.on("open-siyuan-url-plugin", this.eventBusLog); } }, { icon: "iconClose", label: "Off open-siyuan-url-plugin", click: () => { this.eventBus.off("open-siyuan-url-plugin", this.eventBusLog); } }, { icon: "iconSelect", label: "On open-siyuan-url-block", click: () => { this.eventBus.on("open-siyuan-url-block", this.eventBusLog); } }, { icon: "iconClose", label: "Off open-siyuan-url-block", click: () => { this.eventBus.off("open-siyuan-url-block", this.eventBusLog); } }] }); menu.addSeparator(); menu.addItem({ icon: "iconSettings", label: "Official Setting Dialog", click: () => { this.openSetting(); } }); menu.addItem({ icon: "iconSettings", label: "A custom setting dialog (by svelte)", click: () => { this.openDIYSetting(); } }); menu.addItem({ icon: "iconSparkles", label: this.data[STORAGE_NAME].readonlyText || "Readonly", type: "readonly", }); if (this.isMobile) { menu.fullscreen(); } else { menu.open({ x: rect.right, y: rect.bottom, isLeft: true, }); } } }