diff --git a/.github/workflows/release.yml b/.github/workflows/release.yml
index a6b0329..7bd6c4e 100644
--- a/.github/workflows/release.yml
+++ b/.github/workflows/release.yml
@@ -1,62 +1,62 @@
name: Create Release on Tag Push
on:
- push:
- tags:
- - "v*"
+ push:
+ tags:
+ - "v*"
jobs:
- build:
- runs-on: ubuntu-latest
- steps:
- # Checkout
- - name: Checkout
- uses: actions/checkout@v3
+ build:
+ runs-on: ubuntu-latest
+ steps:
+ # Checkout
+ - name: Checkout
+ uses: actions/checkout@v3
- # Install Node.js
- - name: Install Node.js
- uses: actions/setup-node@v3
- with:
- node-version: 18
- registry-url: "https://registry.npmjs.org"
+ # Install Node.js
+ - name: Install Node.js
+ uses: actions/setup-node@v3
+ with:
+ node-version: 18
+ registry-url: "https://registry.npmjs.org"
- # Install pnpm
- - name: Install pnpm
- uses: pnpm/action-setup@v2
- id: pnpm-install
- with:
- version: 8
- run_install: false
+ # Install pnpm
+ - name: Install pnpm
+ uses: pnpm/action-setup@v2
+ id: pnpm-install
+ with:
+ version: 8
+ run_install: false
- # Get pnpm store directory
- - name: Get pnpm store directory
- id: pnpm-cache
- shell: bash
- run: |
- echo "STORE_PATH=$(pnpm store path)" >> $GITHUB_OUTPUT
+ # Get pnpm store directory
+ - name: Get pnpm store directory
+ id: pnpm-cache
+ shell: bash
+ run: |
+ echo "STORE_PATH=$(pnpm store path)" >> $GITHUB_OUTPUT
- # Setup pnpm cache
- - name: Setup pnpm cache
- uses: actions/cache@v3
- with:
- path: ${{ steps.pnpm-cache.outputs.STORE_PATH }}
- key: ${{ runner.os }}-pnpm-store-${{ hashFiles('**/pnpm-lock.yaml') }}
- restore-keys: |
- ${{ runner.os }}-pnpm-store-
+ # Setup pnpm cache
+ - name: Setup pnpm cache
+ uses: actions/cache@v3
+ with:
+ path: ${{ steps.pnpm-cache.outputs.STORE_PATH }}
+ key: ${{ runner.os }}-pnpm-store-${{ hashFiles('**/pnpm-lock.yaml') }}
+ restore-keys: |
+ ${{ runner.os }}-pnpm-store-
- # Install dependencies
- - name: Install dependencies
- run: pnpm install
+ # Install dependencies
+ - name: Install dependencies
+ run: pnpm install
- # Build for production, 这一步会生成一个 package.zip
- - name: Build for production
- run: pnpm build
+ # Build for production, 这一步会生成一个 package.zip
+ - name: Build for production
+ run: pnpm build
- - name: Release
- uses: ncipollo/release-action@v1
- with:
- allowUpdates: true
- artifactErrorsFailBuild: true
- artifacts: 'package.zip'
- token: ${{ secrets.GITHUB_TOKEN }}
- prerelease: true
+ - name: Release
+ uses: ncipollo/release-action@v1
+ with:
+ allowUpdates: true
+ artifactErrorsFailBuild: true
+ artifacts: "package.zip"
+ token: ${{ secrets.GITHUB_TOKEN }}
+ prerelease: true
diff --git a/.gitignore b/.gitignore
index aa00657..764e2d4 100644
--- a/.gitignore
+++ b/.gitignore
@@ -7,3 +7,4 @@ node_modules
dev
dist
build
+tmp
diff --git a/README.md b/README.md
index 352ada8..c0a6958 100644
--- a/README.md
+++ b/README.md
@@ -3,7 +3,8 @@
[中文版](./README_zh_CN.md)
-> Consistent with [siyuan/plugin-sample](https://github.com/siyuan-note/plugin-sample) [v0.0.6](https://github.com/siyuan-note/plugin-sample/tree/v0.0.6).
+> Consistent with [siyuan/plugin-sample](https://github.com/siyuan-note/plugin-sample) [v0.1.3](https://github.com/siyuan-note/plugin-sample/tree/v0.1.3).
+
1. Using vite for packaging
@@ -62,7 +63,6 @@ complete the following tasks:
* Text used in the plugin, such as button text and tooltips
* src/i18n/*.json language configuration files
* Use `this.i18.key` to get the text in the code
-* Finally, declare the language supported by the plugin in the `i18n` field in plugin.json
It is recommended that the plugin supports at least English and Simplified Chinese, so that more people can use it more
conveniently.
@@ -74,8 +74,10 @@ conveniently.
"name": "plugin-sample-vite-svelte",
"author": "frostime",
"url": "https://github.com/siyuan-note/plugin-sample-vite-svelte",
- "version": "0.0.1",
- "minAppVersion": "2.9.0",
+ "version": "0.1.3",
+ "minAppVersion": "2.8.8",
+ "backends": ["windows", "linux", "darwin"],
+ "frontends": ["desktop"],
"displayName": {
"en_US": "Plugin sample with vite and svelte",
"zh_CN": "插件样例 vite + svelte 版"
@@ -89,8 +91,11 @@ conveniently.
"zh_CN": "README.md"
},
"funding": {
+ "openCollective": "",
+ "patreon": "",
+ "github": "",
"custom": [
- ""
+ "https://ld246.com/sponsor"
]
}
}
@@ -102,6 +107,21 @@ conveniently.
* `url`: Plugin repo URL
* `version`: Plugin version number, it is recommended to follow the [semver](https://semver.org/) specification
* `minAppVersion`: Minimum version number of SiYuan required to use this plugin
+* `backends`: Backend environment required by the plugin, optional values are `windows`, `linux`, `darwin`, `docker`, `android`, `ios` and `all`
+ * `windows`: Windows desktop
+ * `linux`: Linux desktop
+ * `darwin`: macOS desktop
+ * `docker`: Docker
+ * `android`: Android APP
+ * `ios`: iOS APP
+ * `all`: All environments
+* `frontends`: Frontend environment required by the plugin, optional values are `desktop`, `desktop-window`, `mobile`, `browser-desktop`, `browser-mobile` and `all`
+ * `desktop`: Desktop
+ * `desktop-window`: Desktop window converted from tab
+ * `mobile`: Mobile APP
+ * `browser-desktop`: Desktop browser
+ * `browser-mobile`: Mobile browser
+ * `all`: All environments
* `displayName`: Template display name, mainly used for display in the marketplace list, supports multiple languages
* `default`: Default language, must exist
* `zh_CN`, `en_US` and other languages: optional, it is recommended to provide at least Chinese and English
@@ -122,13 +142,13 @@ conveniently.
No matter which method is used to compile and package, we finally need to generate a package.zip, which contains at
least the following files:
-* icon.png
+* i18n/*
+* icon.png (160*160)
+* index.css
* index.js
* plugin.json
-* preview.png
+* preview.png (1024*768)
* README*.md
-* index.css (optional)
-* i18n/* (optional)
## List on the marketplace
diff --git a/README_zh_CN.md b/README_zh_CN.md
index f1b02db..55ec0a7 100644
--- a/README_zh_CN.md
+++ b/README_zh_CN.md
@@ -4,7 +4,7 @@
[English](./README.md)
-> 本例和 [siyuan/plugin-sample](https://github.com/siyuan-note/plugin-sample) [v0.0.6](https://github.com/siyuan-note/plugin-sample/tree/v0.0.6) 基本保持一致。
+> 本例和 [siyuan/plugin-sample](https://github.com/siyuan-note/plugin-sample) [v0.1.3](https://github.com/siyuan-note/plugin-sample/tree/v0.1.3) 基本保持一致。
1. 使用 vite 打包
2. 使用符号链接、而不是把项目放到插件目录下的模式进行开发
@@ -59,7 +59,6 @@
* 插件中使用的文本,比如按钮文字和提示信息
* src/i18n/*.json 语言配置文件
* 代码中使用 `this.i18.key` 获取文本
-* 最后在 plugin.json 中的 `i18n` 字段中声明该插件支持的语言
建议插件至少支持英文和简体中文,这样可以方便更多人使用。
@@ -70,8 +69,10 @@
"name": "plugin-sample-vite-svelte",
"author": "frostime",
"url": "https://github.com/siyuan-note/plugin-sample-vite-svelte",
- "version": "0.0.1",
- "minAppVersion": "2.9.0",
+ "version": "0.1.3",
+ "minAppVersion": "2.8.8",
+ "backends": ["windows", "linux", "darwin"],
+ "frontends": ["desktop"],
"displayName": {
"en_US": "Plugin sample with vite and svelte",
"zh_CN": "插件样例 vite + svelte 版"
@@ -85,8 +86,11 @@
"zh_CN": "README.md"
},
"funding": {
+ "openCollective": "",
+ "patreon": "",
+ "github": "",
"custom": [
- ""
+ "https://ld246.com/sponsor"
]
}
}
@@ -97,32 +101,47 @@
* `url`:插件仓库地址
* `version`:插件版本号,建议遵循 [semver](https://semver.org/lang/zh-CN/) 规范
* `minAppVersion`:插件支持的最低思源笔记版本号
+* `backends`:插件需要的后端环境,可选值为 `windows`, `linux`, `darwin`, `docker`, `android`, `ios` and `all`
+ * `windows`:Windows 桌面端
+ * `linux`:Linux 桌面端
+ * `darwin`:macOS 桌面端
+ * `docker`:Docker 端
+ * `android`:Android 端
+ * `ios`:iOS 端
+ * `all`:所有环境
+* `frontends`:插件需要的前端环境,可选值为 `desktop`, `desktop-window`, `mobile`, `browser-desktop`, `browser-mobile` and `all`
+ * `desktop`:桌面端
+ * `desktop-window`:桌面端页签转换的独立窗口
+ * `mobile`:移动端
+ * `browser-desktop`:桌面端浏览器
+ * `browser-mobile`:移动端浏览器
+ * `all`:所有环境
* `displayName`:模板显示名称,主要用于模板集市列表中显示,支持多语言
- * `default`:默认语言,必须存在
- * `zh_CN`、`en_US` 等其他语言:可选,建议至少提供中文和英文
+ * `default`:默认语言,必须存在
+ * `zh_CN`、`en_US` 等其他语言:可选,建议至少提供中文和英文
* `description`:插件描述,主要用于插件集市列表中显示,支持多语言
- * `default`:默认语言,必须存在
- * `zh_CN`、`en_US` 等其他语言:可选,建议至少提供中文和英文
+ * `default`:默认语言,必须存在
+ * `zh_CN`、`en_US` 等其他语言:可选,建议至少提供中文和英文
* `readme`:自述文件名,主要用于插件集市详情页中显示,支持多语言
- * `default`:默认语言,必须存在
- * `zh_CN`、`en_US` 等其他语言:可选,建议至少提供中文和英文
+ * `default`:默认语言,必须存在
+ * `zh_CN`、`en_US` 等其他语言:可选,建议至少提供中文和英文
* `funding`:插件赞助信息
- * `openCollective`:Open Collective 名称
- * `patreon`:Patreon 名称
- * `github`:GitHub 登录名
- * `custom`:自定义赞助链接列表
+ * `openCollective`:Open Collective 名称
+ * `patreon`:Patreon 名称
+ * `github`:GitHub 登录名
+ * `custom`:自定义赞助链接列表
## 打包
无论使用何种方式编译打包,我们最终需要生成一个 package.zip,它至少包含如下文件:
-* icon.png
+* i18n/*
+* icon.png (160*160)
+* index.css
* index.js
* plugin.json
-* preview.png
+* preview.png (1024*768)
* README*.md
-* index.css (optional)
-* i18n/* (optional)
## 上架集市
diff --git a/package.json b/package.json
index 20ff17f..db69246 100644
--- a/package.json
+++ b/package.json
@@ -21,7 +21,7 @@
"minimist": "^1.2.8",
"rollup-plugin-livereload": "^2.0.5",
"sass": "^1.62.1",
- "siyuan": "^0.7.1",
+ "siyuan": "^0.7.2",
"svelte": "^3.57.0",
"ts-node": "^10.9.1",
"typescript": "^5.0.4",
diff --git a/plugin.json b/plugin.json
index 8558256..311de7b 100644
--- a/plugin.json
+++ b/plugin.json
@@ -2,8 +2,10 @@
"name": "plugin-sample-vite-svelte",
"author": "frostime",
"url": "https://github.com/siyuan-note/plugin-sample-vite-svelte",
- "version": "0.0.6",
+ "version": "0.1.3",
"minAppVersion": "2.9.0",
+ "backends": ["windows", "linux", "darwin"],
+ "frontends": ["desktop"],
"displayName": {
"en_US": "Plugin sample with vite and svelte",
"zh_CN": "插件样例 vite + svelte 版"
diff --git a/src/i18n/en_US.json b/src/i18n/en_US.json
index 7e9425a..c336270 100644
--- a/src/i18n/en_US.json
+++ b/src/i18n/en_US.json
@@ -5,6 +5,9 @@
"byeMenu": "Bye, Menu!",
"helloPlugin": "Hello, Plugin!",
"byePlugin": "Bye, Plugin!",
+ "showDialog": "Show dialog",
+ "removedData": "Data deleted",
+ "confirmRemove": "Confirm to delete the data in ${name}?",
"name": "SiYuan",
"hello": {
"makesure": "Before using this template, please read the offical sample, make sure that you've known about the pipeline for plugin developing."
diff --git a/src/i18n/zh_CN.json b/src/i18n/zh_CN.json
index 7eaa3eb..568cccd 100644
--- a/src/i18n/zh_CN.json
+++ b/src/i18n/zh_CN.json
@@ -5,6 +5,9 @@
"byeMenu": "再见,菜单!",
"helloPlugin": "你好,插件!",
"byePlugin": "再见,插件!",
+ "showDialog": "弹出一个对话框",
+ "removedData": "数据已删除",
+ "confirmRemove": "确认删除 ${name} 中的数据?",
"name": "思源",
"hello": {
"makesure": "使用这个模板之前,请阅读官方教程, 确保自己已经理解了插件的基本开发流程。"
diff --git a/src/index.ts b/src/index.ts
index e2f27cd..8947cf8 100644
--- a/src/index.ts
+++ b/src/index.ts
@@ -1,4 +1,15 @@
-import { Plugin, showMessage, confirm, Dialog, Menu, isMobile, openTab, adaptHotkey } from "siyuan";
+import {
+ Plugin,
+ showMessage,
+ confirm,
+ Dialog,
+ Menu,
+ openTab,
+ adaptHotkey,
+ getFrontend,
+ getBackend,
+ IModel
+} from "siyuan";
import "./index.scss";
import HelloExample from "./hello.svelte";
@@ -8,26 +19,61 @@ const STORAGE_NAME = "menu-config";
const TAB_TYPE = "custom_tab";
const DOCK_TYPE = "dock_tab";
-export default class SamplePlugin extends Plugin {
+export default class PluginSample extends Plugin {
- private customTab: () => any;
+ private customTab: () => IModel;
+ private isMobile: boolean;
async onload() {
- showMessage("Hello SiYuan Plugin");
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: "iconEmoji",
+ icon: "iconFace",
title: this.i18n.addTopBarIcon,
- position: "left",
+ position: "right",
callback: () => {
- this.addMenu(topBarElement.getBoundingClientRect());
+ let rect = topBarElement.getBoundingClientRect();
+ // 如果被隐藏,则使用更多按钮
+ if (rect.width === 0) {
+ rect = document.querySelector("#barMore").getBoundingClientRect();
+ }
+ this.addMenu(rect);
}
});
- let div = document.createElement("div");
+ 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,
+ });
+
+ let tabDiv = document.createElement("div");
new HelloExample({
- target: div,
+ target: tabDiv,
props: {
name: this.i18n.name,
i18n: this.i18n.hello
@@ -36,7 +82,7 @@ export default class SamplePlugin extends Plugin {
this.customTab = this.addTab({
type: TAB_TYPE,
init() {
- this.element.appendChild(div);
+ this.element.appendChild(tabDiv);
console.log(this.element);
},
destroy() {
@@ -44,11 +90,19 @@ export default class SamplePlugin extends Plugin {
}
});
+ this.addCommand({
+ langKey: "showDialog",
+ hotkey: "⇧⌘M",
+ callback: () => {
+ this.showDialog();
+ }
+ });
+
this.addDock({
config: {
position: "LeftBottom",
size: {width: 200, height: 0},
- icon: "iconEmoji",
+ icon: "iconSaving",
title: "Custom Dock",
},
data: {
@@ -75,10 +129,12 @@ export default class SamplePlugin extends Plugin {
}
});
+ console.log(this.i18n.helloPlugin);
}
onLayoutReady() {
this.loadData(STORAGE_NAME);
+ console.log(`frontend: ${getFrontend()}; backend: ${getBackend()}`);
}
onunload() {
@@ -87,160 +143,6 @@ export default class SamplePlugin extends Plugin {
console.log("onunload");
}
- private wsEvent({ detail }: any) {
- console.log(detail);
- }
-
- private blockIconEvent({detail}: any) {
- console.log(detail);
- detail.menu.addSeparator(0);
- const ids: string[] = [];
- detail.blockElements.forEach((item: HTMLElement) => {
- ids.push(item.getAttribute("data-node-id"));
- });
- detail.menu.addItem({
- index: 1,
- iconHTML: "",
- type: "readonly",
- label: "IDs
" + ids.join("
"),
- });
- }
-
- private async addMenu(rect: DOMRect) {
- const menu = new Menu("topBarSample", () => {
- console.log(this.i18n.byeMenu);
- });
- menu.addItem({
- icon: "iconHelp",
- label: "Confirm",
- click() {
- confirm("Confirm", "Is this a confirm?", () => {
- showMessage("confirm");
- }, () => {
- showMessage("cancel");
- });
- }
- });
- menu.addItem({
- icon: "iconFeedback",
- label: "Message",
- click: () => {
- showMessage(this.i18n.helloPlugin);
- }
- });
- menu.addItem({
- icon: "iconInfo",
- label: "Dialog",
- click: () => this.openHelloInDialog()
- });
- menu.addItem({
- icon: "iconLayoutBottom",
- label: "Open Tab",
- click: () => {
- openTab({
- custom: {
- icon: "iconEmoji",
- title: "Custom Tab",
- data: {
- text: "This is my custom tab",
- },
- fn: this.customTab
- },
- });
- }
- });
- menu.addItem({
- icon: "iconLayout",
- label: "Open Float Layer(open help)",
- click: () => {
- this.addFloatLayer({
- ids: ["20230523173319-xj1l3qu", "20230523173321-55o0w2n"],
- defIds: ["20230523173323-imgm9tp", "20230523173324-cxu98t3"],
- x: window.innerWidth - 768 - 120,
- y: 32
- });
- }
- });
- menu.addItem({
- icon: "iconTrashcan",
- label: "Remove Data",
- click: () => {
- this.removeData(STORAGE_NAME).then(() => {
- this.data[STORAGE_NAME] = {readonlyText: "Readonly"};
- });
- }
- });
- menu.addItem({
- icon: "iconScrollHoriz",
- label: "Event Bus",
- type: "submenu",
- submenu: [{
- icon: "iconSelect",
- label: "On ws-main",
- click: () => {
- this.eventBus.on("ws-main", this.wsEvent);
- }
- }, {
- icon: "iconClose",
- label: "Off ws-main",
- click: () => {
- this.eventBus.off("ws-main", this.wsEvent);
- }
- }, {
- icon: "iconSelect",
- label: "On click-blockicon",
- click: () => {
- this.eventBus.on("click-blockicon", this.blockIconEvent);
- }
- }, {
- icon: "iconClose",
- label: "Off click-blockicon",
- click: () => {
- this.eventBus.off("click-blockicon", this.blockIconEvent);
- }
- }, {
- icon: "iconSelect",
- label: "On click-pdf",
- click: () => {
- this.eventBus.on("click-pdf", this.wsEvent);
- }
- }, {
- icon: "iconClose",
- label: "Off click-pdf",
- click: () => {
- this.eventBus.off("click-pdf", this.wsEvent);
- }
- }, {
- icon: "iconSelect",
- label: "On click-editorcontent",
- click: () => {
- this.eventBus.on("click-editorcontent", this.wsEvent);
- }
- }, {
- icon: "iconClose",
- label: "Off click-editorcontent",
- click: () => {
- this.eventBus.off("click-editorcontent", this.wsEvent);
- }
- }]
- });
- menu.addSeparator();
- menu.addItem({
- icon: "iconSparkles",
- label: this.data[STORAGE_NAME] || "Readonly",
- type: "readonly",
- });
- if (isMobile()) {
- menu.fullscreen();
- } else {
- menu.open({
- x: rect.right,
- y: rect.bottom,
- isLeft: true,
- });
- }
- }
-
openSetting(): void {
let dialog = new Dialog({
title: "SettingPannel",
@@ -257,13 +159,30 @@ export default class SamplePlugin extends Plugin {
});
}
- private openHelloInDialog() {
+ private eventBusLog({detail}: any) {
+ console.log(detail);
+ }
+
+ private blockIconEvent({detail}: any) {
+ const ids: string[] = [];
+ detail.blockElements.forEach((item: HTMLElement) => {
+ ids.push(item.getAttribute("data-node-id"));
+ });
+ detail.menu.addItem({
+ iconHTML: "",
+ type: "readonly",
+ label: "IDs
" + ids.join("
"),
+ });
+ }
+
+ private showDialog() {
let dialog = new Dialog({
title: "Hello World",
- content: ``,
+ content: ``,
+ width: this.isMobile ? "92vw" : "720px",
destroyCallback(options) {
- //You must destroy the component when the dialog is closed
- hello.$destroy();
+ //Destroy the component when the dialog is closed
+ // hello.$destroy();
},
});
let hello = new HelloExample({
@@ -274,4 +193,193 @@ export default class SamplePlugin extends Plugin {
}
});
}
+
+ private addMenu(rect: DOMRect) {
+ const menu = new Menu("topBarSample", () => {
+ console.log(this.i18n.byeMenu);
+ });
+ menu.addItem({
+ icon: "iconInfo",
+ label: "Dialog",
+ accelerator: this.commands[0].customHotkey,
+ click: () => this.showDialog()
+ });
+ if (!this.isMobile) {
+ menu.addItem({
+ icon: "iconLayoutBottom",
+ label: "Open Custom Tab",
+ click: () => {
+ const tab = openTab({
+ app: this.app,
+ custom: {
+ icon: "iconFace",
+ title: "Custom Tab",
+ data: {
+ text: "This is my custom tab",
+ },
+ fn: this.customTab
+ },
+ });
+ console.log(tab)
+ }
+ });
+ menu.addItem({
+ icon: "iconLayoutBottom",
+ 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: "iconLayoutBottom",
+ 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: "iconLayoutBottom",
+ label: "Open Search Tab",
+ click: () => {
+ const tab = openTab({
+ app: this.app,
+ search: {
+ k: "SiYuan"
+ }
+ });
+ console.log(tab)
+ }
+ });
+ menu.addItem({
+ icon: "iconLayoutBottom",
+ 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: "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.blockIconEvent);
+ }
+ }, {
+ icon: "iconClose",
+ label: "Off click-blockicon",
+ click: () => {
+ this.eventBus.off("click-blockicon", this.blockIconEvent);
+ }
+ }, {
+ 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);
+ }
+ }]
+ });
+ menu.addSeparator();
+ 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,
+ });
+ }
+ }
}
diff --git a/src/siyuan.d.ts b/src/siyuan.d.ts
index 1d5b0a9..4c74dc5 100644
--- a/src/siyuan.d.ts
+++ b/src/siyuan.d.ts
@@ -1,314 +1,424 @@
-declare module "siyuan" {
- type TEventBus = "ws-main" | "click-blockicon" | "click-editorcontent" | "click-pdf" | "click-editortitleicon"
+type TEventBus = "ws-main" | "click-blockicon" | "click-editorcontent" | "click-pdf" |
+ "click-editortitleicon" | "open-noneditableblock"
- declare global {
- interface Window {
- Lute: Lute
- }
- }
+type TCardType = "doc" | "notebook" | "all"
-
- interface IObject {
- [key: string]: string;
- }
-
- interface ILuteNode {
- TokensStr: () => string;
- __internal_object__: {
- Parent: {
- Type: number,
- },
- HeadingLevel: string,
- };
- }
-
- interface IWebSocketData {
- cmd: string
- callback?: string
- data: any
- msg: string
- code: number
- sid: string
- }
-
- interface IPluginDockTab {
- position: "LeftTop" | "LeftBottom" | "RightTop" | "RightBottom" | "BottomLeft" | "BottomRight",
- size: { width: number, height: number },
- icon: string,
- hotkey?: string,
- title: string,
- }
-
- interface IMenuItemOption {
- label?: string,
- click?: (element: HTMLElement) => void,
- type?: "separator" | "submenu" | "readonly",
- accelerator?: string,
- action?: string,
- id?: string,
- submenu?: IMenuItemOption[]
- disabled?: boolean
- icon?: string
- iconHTML?: string
- current?: boolean
- bind?: (element: HTMLElement) => void
- }
-
- export function fetchPost(url: string, data?: any, cb?: (response: IWebSocketData) => void, headers?: IObject): void;
-
- export function fetchSyncPost(url: string, data?: any): Promise;
-
- export function fetchGet(url: string, cb: (response: IWebSocketData) => void): void;
-
- export function openTab(options: {
- custom?: {
- title: string,
- icon: string,
- data?: any
- fn?: () => any,
- } // card 和自定义页签 必填
- position?: "right" | "bottom",
- keepCursor?: boolean // 是否跳转到新 tab 上
- removeCurrentTab?: boolean // 在当前页签打开时需移除原有页签
- afterOpen?: () => void // 打开后回调
- }): void
-
- export function isMobile(): boolean;
-
- export function adaptHotkey(hotkey: string): string;
-
- export function confirm(title: string, text: string, confirmCB?: () => void, cancelCB?: () => void): void;
-
- /**
- * @param timeout - ms. 0: manual close;-1: always show; 6000: default
- * @param {string} [type=info]
- */
- export function showMessage(text: string, timeout?: number, type?: "info" | "error", id?: string): void;
-
- export class App {
- plugins: Plugin[];
- }
-
- export abstract class Plugin {
- eventBus: EventBus;
- i18n: IObject;
- data: any;
- name: string;
-
- constructor(options: {
- app: App,
- id: string,
- name: string,
- i18n: IObject
- })
-
- onload(): void;
-
- onunload(): void;
-
- onLayoutReady(): void;
-
- /*
- * @param {string} [options.position=right]
- */
- addTopBar(options: {
- icon: string,
- title: string,
- callback: (evt: MouseEvent) => void
- position?: "right" | "left"
- }): HTMLDivElement;
-
- openSetting(): void
-
- // registerCommand(command: IPluginCommand): void;
-
- // registerSettingRender(settingRender: SettingRender): void;
-
- loadData(storageName: string): Promise;
-
- saveData(storageName: string, content: any): Promise;
-
- removeData(storageName: string): Promise;
-
- addTab(options: {
- type: string,
- destroy?: () => void,
- resize?: () => void,
- update?: () => void,
- init: () => void
- }): () => any
-
- addDock(options: {
- config: IPluginDockTab,
- data: any,
- type: string,
- destroy?: () => void,
- resize?: () => void,
- update?: () => void,
- init: () => void
- }): any
-
- addFloatLayer(options: {
- ids: string[],
- defIds?: string[],
- x?: number,
- y?: number,
- targetElement?: HTMLElement
- }): void
- }
-
- export class EventBus {
- on(type: TEventBus, listener: (event: CustomEvent) => void): void;
-
- once(type: TEventBus, listener: (event: CustomEvent) => void): void;
-
- off(type: TEventBus, listener: (event: CustomEvent) => void): void;
-
- emit(type: TEventBus, detail?: any): boolean;
- }
-
- export class Dialog {
-
- element: HTMLElement;
-
- constructor(options: {
- title?: string,
- transparent?: boolean,
- content: string,
- width?: string
- height?: string,
- destroyCallback?: (options?: IObject) => void
- disableClose?: boolean
- disableAnimation?: boolean
- });
-
- destroy(options?: IObject): void;
-
- bindInput(inputElement: HTMLInputElement | HTMLTextAreaElement, enterEvent?: () => void): void;
- }
-
- export class Menu {
- constructor(id?: string, closeCB?: () => void);
-
- showSubMenu(subMenuElement: HTMLElement): void;
-
- addItem(options: IMenuItemOption): HTMLElement;
-
- addSeparator(): void;
-
- open(options: { x: number, y: number, h?: number, w?: number, isLeft?: boolean }): void;
-
- /*
- * @param {string} [position=all]
- */
- fullscreen(position?: "bottom" | "all"): void;
-
- close(): void;
- }
-
- declare class Lute {
- public static WalkStop: number;
- public static WalkSkipChildren: number;
- public static WalkContinue: number;
- public static Version: string;
- public static Caret: string;
-
- public static New(): Lute;
-
- public static EChartsMindmapStr(text: string): string;
-
- public static NewNodeID(): string;
-
- public static Sanitize(html: string): string;
-
- public static EscapeHTMLStr(str: string): string;
-
- public static UnEscapeHTMLStr(str: string): string;
-
- public static GetHeadingID(node: ILuteNode): string;
-
- public static BlockDOM2Content(html: string): string;
-
- private constructor();
-
- public BlockDOM2Content(text: string): string;
-
- public BlockDOM2EscapeMarkerContent(text: string): string;
-
- public SetTextMark(enable: boolean): void;
-
- public SetHeadingID(enable: boolean): void;
-
- public SetProtyleMarkNetImg(enable: boolean): void;
-
- public SetSpellcheck(enable: boolean): void;
-
- public SetFileAnnotationRef(enable: boolean): void;
-
- public SetSetext(enable: boolean): void;
-
- public SetYamlFrontMatter(enable: boolean): void;
-
- public SetChineseParagraphBeginningSpace(enable: boolean): void;
-
- public SetRenderListStyle(enable: boolean): void;
-
- public SetImgPathAllowSpace(enable: boolean): void;
-
- public SetKramdownIAL(enable: boolean): void;
-
- public BlockDOM2Md(html: string): string;
-
- public BlockDOM2StdMd(html: string): string;
-
- public SetGitConflict(enable: boolean): void;
-
- public SetSuperBlock(enable: boolean): void;
-
- public SetTag(enable: boolean): void;
-
- public SetMark(enable: boolean): void;
-
- public SetSub(enable: boolean): void;
-
- public SetSup(enable: boolean): void;
-
- public SetBlockRef(enable: boolean): void;
-
- public SetSanitize(enable: boolean): void;
-
- public SetHeadingAnchor(enable: boolean): void;
-
- public SetImageLazyLoading(imagePath: string): void;
-
- public SetInlineMathAllowDigitAfterOpenMarker(enable: boolean): void;
-
- public SetToC(enable: boolean): void;
-
- public SetIndentCodeBlock(enable: boolean): void;
-
- public SetParagraphBeginningSpace(enable: boolean): void;
-
- public SetFootnotes(enable: boolean): void;
-
- public SetLinkRef(enalbe: boolean): void;
-
- public SetEmojiSite(emojiSite: string): void;
-
- public PutEmojis(emojis: IObject): void;
-
- public SpinBlockDOM(html: string): string;
-
- public Md2BlockDOM(html: string): string;
-
- public SetProtyleWYSIWYG(wysiwyg: boolean): void;
-
- public MarkdownStr(name: string, md: string): string;
-
- public IsValidLinkDest(text: string): boolean;
-
- public BlockDOM2InlineBlockDOM(html: string): string;
-
- public BlockDOM2HTML(html: string): string;
+declare global {
+ interface Window {
+ Lute: Lute
}
}
+
+interface ITab {
+ id: string;
+ headElement: HTMLElement;
+ panelElement: HTMLElement;
+ model: IModel;
+ title: string;
+ icon: string;
+ docIcon: string;
+ updateTitle: (title: string) => void;
+ pin: () => void;
+ unpin: () => void;
+ setDocIcon: (icon: string) => void;
+ close: () => void;
+}
+
+interface IModel {
+ element: Element;
+ tab: ITab;
+ data: any;
+ type: string;
+}
+
+interface IObject {
+ [key: string]: string;
+}
+
+interface ILuteNode {
+ TokensStr: () => string;
+ __internal_object__: {
+ Parent: {
+ Type: number,
+ },
+ HeadingLevel: string,
+ };
+}
+
+interface ISearchOption {
+ page?: number
+ group?: number, // 0:不分组,1:按文档分组
+ hasReplace?: boolean,
+ method?: number // 0:文本,1:查询语法,2:SQL,3:正则表达式
+ hPath?: string
+ idPath?: string[]
+ k: string
+ r?: string
+ types?: {
+ mathBlock: boolean
+ table: boolean
+ blockquote: boolean
+ superBlock: boolean
+ paragraph: boolean
+ document: boolean
+ heading: boolean
+ list: boolean
+ listItem: boolean
+ codeBlock: boolean
+ htmlBlock: boolean
+ embedBlock: boolean
+ }
+}
+
+interface IWebSocketData {
+ cmd: string
+ callback?: string
+ data: any
+ msg: string
+ code: number
+ sid: string
+}
+
+declare interface IPluginDockTab {
+ position: "LeftTop" | "LeftBottom" | "RightTop" | "RightBottom" | "BottomLeft" | "BottomRight",
+ size: { width: number, height: number },
+ icon: string,
+ hotkey?: string,
+ title: string,
+ index?: number,
+ show?: boolean
+}
+
+interface IMenuItemOption {
+ label?: string,
+ click?: (element: HTMLElement) => void,
+ type?: "separator" | "submenu" | "readonly",
+ accelerator?: string,
+ action?: string,
+ id?: string,
+ submenu?: IMenuItemOption[]
+ disabled?: boolean
+ icon?: string
+ iconHTML?: string
+ current?: boolean
+ bind?: (element: HTMLElement) => void
+ index?: number
+ element?: HTMLElement
+}
+
+interface ICommandOption {
+ langKey: string, // 多语言 key
+ /**
+ * 目前需使用 MacOS 符号标识,顺序按照 ⌥⇧⌘,入 ⌥⇧⌘A
+ * "Ctrl": "⌘",
+ * "Shift": "⇧",
+ * "Alt": "⌥",
+ * "Tab": "⇥",
+ * "Backspace": "⌫",
+ * "Delete": "⌦",
+ * "Enter": "↩",
+ */
+ hotkey: string,
+ customHotkey?: string,
+ callback?: () => void
+ fileTreeCallback?: (file: any) => void
+ editorCallback?: (protyle: any) => void
+ dockCallback?: (element: HTMLElement) => void
+}
+
+export function fetchPost(url: string, data?: any, callback?: (response: IWebSocketData) => void, headers?: IObject): void;
+
+export function fetchSyncPost(url: string, data?: any): Promise;
+
+export function fetchGet(url: string, callback: (response: IWebSocketData) => void): void;
+
+export function openTab(options: {
+ app: App,
+ doc?: {
+ id: string, // 块 id
+ action?: string [] // cb-get-all:获取所有内容;cb-get-focus:打开后光标定位在 id 所在的块;cb-get-hl: 打开后 id 块高亮
+ zoomIn?: boolean // 是否缩放
+ },
+ pdf?: {
+ path: string,
+ page?: number, // pdf 页码
+ id?: string, // File Annotation id
+ },
+ asset?: {
+ path: string,
+ },
+ search?: ISearchOption
+ card?: {
+ type: TCardType,
+ id?: string, // cardType 为 all 时不传,否则传文档或笔记本 id
+ title?: string // cardType 为 all 时不传,否则传文档或笔记本名称
+ },
+ custom?: {
+ title: string,
+ icon: string,
+ data?: any
+ fn?: () => IModel,
+ }
+ position?: "right" | "bottom",
+ keepCursor?: boolean // 是否跳转到新 tab 上
+ removeCurrentTab?: boolean // 在当前页签打开时需移除原有页签
+ afterOpen?: () => void // 打开后回调
+}): ITab
+
+export function getFrontend(): "desktop" | "desktop-window" | "mobile" | "browser-desktop" | "browser-mobile";
+
+export function getBackend(): "windows" | "linux" | "darwin" | "docker" | "android" | "ios"
+
+export function adaptHotkey(hotkey: string): string;
+
+export function confirm(title: string, text: string, confirmCallback?: () => void, cancelCallback?: () => void): void;
+
+/**
+ * @param timeout - ms. 0: manual close;-1: always show; 6000: default
+ * @param {string} [type=info]
+ */
+export function showMessage(text: string, timeout?: number, type?: "info" | "error", id?: string): void;
+
+export class App {
+ plugins: Plugin[];
+}
+
+export abstract class Plugin {
+ eventBus: EventBus;
+ i18n: IObject;
+ data: any;
+ name: string;
+ app: App;
+ commands: ICommandOption[];
+
+ constructor(options: {
+ app: App,
+ name: string,
+ i18n: IObject
+ })
+
+ onload(): void;
+
+ onunload(): void;
+
+ onLayoutReady(): void;
+
+ /**
+ * Must be executed before the synchronous function.
+ * @param {string} [options.position=right]
+ */
+ addTopBar(options: {
+ icon: string,
+ title: string,
+ callback: (event: MouseEvent) => void
+ position?: "right" | "left"
+ }): HTMLElement;
+
+ /**
+ * Must be executed before the synchronous function.
+ * @param {string} [options.position=right]
+ */
+ addStatusBar(options: {
+ element: HTMLElement,
+ position?: "right" | "left"
+ }): HTMLElement
+
+ openSetting(): void
+
+ loadData(storageName: string): Promise;
+
+ saveData(storageName: string, content: any): Promise;
+
+ removeData(storageName: string): Promise;
+
+ addIcons(svg: string): void;
+
+ /**
+ * Must be executed before the synchronous function.
+ */
+ addTab(options: {
+ type: string,
+ destroy?: () => void,
+ resize?: () => void,
+ update?: () => void,
+ init: () => void
+ }): () => IModel
+
+ /**
+ * Must be executed before the synchronous function.
+ */
+ addDock(options: {
+ config: IPluginDockTab,
+ data: any,
+ type: string,
+ destroy?: () => void,
+ resize?: () => void,
+ update?: () => void,
+ init: () => void
+ }): { config: IPluginDockTab, model: IModel }
+
+ addCommand(options: ICommandOption): void
+
+ addFloatLayer(options: {
+ ids: string[],
+ defIds?: string[],
+ x?: number,
+ y?: number,
+ targetElement?: HTMLElement
+ }): void
+}
+
+export class EventBus {
+ on(type: TEventBus, listener: (event: CustomEvent) => void): void;
+
+ once(type: TEventBus, listener: (event: CustomEvent) => void): void;
+
+ off(type: TEventBus, listener: (event: CustomEvent) => void): void;
+
+ emit(type: TEventBus, detail?: any): boolean;
+}
+
+export class Dialog {
+
+ element: HTMLElement;
+
+ constructor(options: {
+ title?: string,
+ transparent?: boolean,
+ content: string,
+ width?: string
+ height?: string,
+ destroyCallback?: (options?: IObject) => void
+ disableClose?: boolean
+ disableAnimation?: boolean
+ });
+
+ destroy(options?: IObject): void;
+
+ bindInput(inputElement: HTMLInputElement | HTMLTextAreaElement, enterEvent?: () => void): void;
+}
+
+export class Menu {
+ constructor(id?: string, closeCallback?: () => void);
+
+ showSubMenu(subMenuElement: HTMLElement): void;
+
+ addItem(options: IMenuItemOption): HTMLElement;
+
+ addSeparator(index?: number): void;
+
+ open(options: { x: number, y: number, h?: number, w?: number, isLeft?: boolean }): void;
+
+ /**
+ * @param {string} [position=all]
+ */
+ fullscreen(position?: "bottom" | "all"): void;
+
+ close(): void;
+}
+
+declare class Lute {
+ public static WalkStop: number;
+ public static WalkSkipChildren: number;
+ public static WalkContinue: number;
+ public static Version: string;
+ public static Caret: string;
+
+ public static New(): Lute;
+
+ public static EChartsMindmapStr(text: string): string;
+
+ public static NewNodeID(): string;
+
+ public static Sanitize(html: string): string;
+
+ public static EscapeHTMLStr(str: string): string;
+
+ public static UnEscapeHTMLStr(str: string): string;
+
+ public static GetHeadingID(node: ILuteNode): string;
+
+ public static BlockDOM2Content(html: string): string;
+
+ private constructor();
+
+ public BlockDOM2Content(text: string): string;
+
+ public BlockDOM2EscapeMarkerContent(text: string): string;
+
+ public SetTextMark(enable: boolean): void;
+
+ public SetHeadingID(enable: boolean): void;
+
+ public SetProtyleMarkNetImg(enable: boolean): void;
+
+ public SetSpellcheck(enable: boolean): void;
+
+ public SetFileAnnotationRef(enable: boolean): void;
+
+ public SetSetext(enable: boolean): void;
+
+ public SetYamlFrontMatter(enable: boolean): void;
+
+ public SetChineseParagraphBeginningSpace(enable: boolean): void;
+
+ public SetRenderListStyle(enable: boolean): void;
+
+ public SetImgPathAllowSpace(enable: boolean): void;
+
+ public SetKramdownIAL(enable: boolean): void;
+
+ public BlockDOM2Md(html: string): string;
+
+ public BlockDOM2StdMd(html: string): string;
+
+ public SetGitConflict(enable: boolean): void;
+
+ public SetSuperBlock(enable: boolean): void;
+
+ public SetTag(enable: boolean): void;
+
+ public SetMark(enable: boolean): void;
+
+ public SetSub(enable: boolean): void;
+
+ public SetSup(enable: boolean): void;
+
+ public SetBlockRef(enable: boolean): void;
+
+ public SetSanitize(enable: boolean): void;
+
+ public SetHeadingAnchor(enable: boolean): void;
+
+ public SetImageLazyLoading(imagePath: string): void;
+
+ public SetInlineMathAllowDigitAfterOpenMarker(enable: boolean): void;
+
+ public SetToC(enable: boolean): void;
+
+ public SetIndentCodeBlock(enable: boolean): void;
+
+ public SetParagraphBeginningSpace(enable: boolean): void;
+
+ public SetFootnotes(enable: boolean): void;
+
+ public SetLinkRef(enalbe: boolean): void;
+
+ public SetEmojiSite(emojiSite: string): void;
+
+ public PutEmojis(emojis: IObject): void;
+
+ public SpinBlockDOM(html: string): string;
+
+ public Md2BlockDOM(html: string): string;
+
+ public SetProtyleWYSIWYG(wysiwyg: boolean): void;
+
+ public MarkdownStr(name: string, md: string): string;
+
+ public IsValidLinkDest(text: string): boolean;
+
+ public BlockDOM2InlineBlockDOM(html: string): string;
+
+ public BlockDOM2HTML(html: string): string;
+}
\ No newline at end of file