From c580fb7a0040ecc6987bd00205927af76d790404 Mon Sep 17 00:00:00 2001
From: frostime <frostime@foxmail.com>
Date: Fri, 27 Oct 2023 22:11:43 +0800
Subject: [PATCH 01/82] update setting utils

---
 src/libs/setting-utils.ts | 10 +++++++---
 1 file changed, 7 insertions(+), 3 deletions(-)

diff --git a/src/libs/setting-utils.ts b/src/libs/setting-utils.ts
index ff78af8..17cc555 100644
--- a/src/libs/setting-utils.ts
+++ b/src/libs/setting-utils.ts
@@ -17,7 +17,7 @@ export class SettingUtils {
     settings: Map<string, ISettingItem> = new Map();
     elements: Map<string, HTMLElement> = new Map();
 
-    constructor(plugin: Plugin, name?: string, width?: string, height?: string) {
+    constructor(plugin: Plugin, name?: string, width?: string, height?: string, callback?: (data: any) => void) {
         this.name = name ?? 'settings';
         this.plugin = plugin;
         this.file = this.name.endsWith('.json') ? this.name : `${this.name}.json`;
@@ -29,8 +29,12 @@ export class SettingUtils {
                     this.updateValue(key);
                 }
                 let data = this.dump();
-                this.plugin.data[this.name] = data;
-                this.save();
+                if (callback !== undefined) {
+                    callback(data);
+                } else {
+                    this.plugin.data[this.name] = data;
+                    this.save();
+                }
             }
         });
     }

From 3343eb7e5c57c9bed51aa199bca6299e74f70585 Mon Sep 17 00:00:00 2001
From: frostime <frostime@foxmail.com>
Date: Fri, 27 Oct 2023 22:12:43 +0800
Subject: [PATCH 02/82] update

---
 src/libs/setting-utils.ts | 4 ++--
 1 file changed, 2 insertions(+), 2 deletions(-)

diff --git a/src/libs/setting-utils.ts b/src/libs/setting-utils.ts
index 17cc555..79f35f4 100644
--- a/src/libs/setting-utils.ts
+++ b/src/libs/setting-utils.ts
@@ -3,7 +3,7 @@
  * @Author       : frostime
  * @Date         : 2023-09-16 18:05:00
  * @FilePath     : /src/libs/setting-utils.ts
- * @LastEditTime : 2023-09-16 18:17:03
+ * @LastEditTime : 2023-10-27 22:12:37
  * @Description  : A utility for siyuan plugin settings
  */
 
@@ -17,7 +17,7 @@ export class SettingUtils {
     settings: Map<string, ISettingItem> = new Map();
     elements: Map<string, HTMLElement> = new Map();
 
-    constructor(plugin: Plugin, name?: string, width?: string, height?: string, callback?: (data: any) => void) {
+    constructor(plugin: Plugin, name?: string, callback?: (data: any) => void, width?: string, height?: string) {
         this.name = name ?? 'settings';
         this.plugin = plugin;
         this.file = this.name.endsWith('.json') ? this.name : `${this.name}.json`;

From 7d95423d24ba8154b0ab27e75619419680af6842 Mon Sep 17 00:00:00 2001
From: frostime <frostime@foxmail.com>
Date: Sat, 28 Oct 2023 16:52:03 +0800
Subject: [PATCH 03/82] update

---
 src/libs/setting-utils.ts | 9 +++++----
 1 file changed, 5 insertions(+), 4 deletions(-)

diff --git a/src/libs/setting-utils.ts b/src/libs/setting-utils.ts
index 79f35f4..b092a27 100644
--- a/src/libs/setting-utils.ts
+++ b/src/libs/setting-utils.ts
@@ -3,7 +3,7 @@
  * @Author       : frostime
  * @Date         : 2023-09-16 18:05:00
  * @FilePath     : /src/libs/setting-utils.ts
- * @LastEditTime : 2023-10-27 22:12:37
+ * @LastEditTime : 2023-10-28 16:52:01
  * @Description  : A utility for siyuan plugin settings
  */
 
@@ -41,13 +41,13 @@ export class SettingUtils {
 
     async load() {
         let data = await this.plugin.loadData(this.file);
+        console.debug('Load config:', data);
         if (data) {
             for (let [key, item] of this.settings) {
                 item.value = data?.[key] ?? item.value;
             }
         }
         this.plugin.data[this.name] = this.dump();
-        console.log(data);
         return data;
     }
 
@@ -159,6 +159,7 @@ export class SettingUtils {
                 break;
             case 'slider':
                 element.value = item.value;
+                element.ariaLabel = item.value;
                 break;
             case 'textinput':
                 element.value = item.value;
@@ -173,7 +174,7 @@ export class SettingUtils {
     private updateValue(key: string) {
         let item = this.settings.get(key);
         let element = this.elements.get(key) as any;
-        console.log(element, element?.value);
+        // console.debug(element, element?.value);
         switch (item.type) {
             case 'checkbox':
                 item.value = element.checked;
@@ -182,7 +183,7 @@ export class SettingUtils {
                 item.value = element.value;
                 break;
             case 'slider':
-                item.value = parseInt(element.value);
+                item.value = element.value;
                 break;
             case 'textinput':
                 item.value = element.value;

From 5f59ffefe42923a655b8381a9cf12c0bc6907fb6 Mon Sep 17 00:00:00 2001
From: frostime <frostime@foxmail.com>
Date: Sat, 28 Oct 2023 18:22:56 +0800
Subject: [PATCH 04/82] update keywords

---
 plugin.json | 5 ++++-
 1 file changed, 4 insertions(+), 1 deletion(-)

diff --git a/plugin.json b/plugin.json
index 04236da..f3e2682 100644
--- a/plugin.json
+++ b/plugin.json
@@ -29,5 +29,8 @@
     "custom": [
       "https://afdian.net/a/frostime"
     ]
-  }
+  },
+  "keywords": [
+    "plugin", "sample", "插件样例"
+  ]
 }

From 3de8bc61344ec06257d10c5b2c435ca2df831b52 Mon Sep 17 00:00:00 2001
From: frostime <frostime@foxmail.com>
Date: Sun, 19 Nov 2023 11:49:25 +0800
Subject: [PATCH 05/82] =?UTF-8?q?=F0=9F=90=B3=20chore(update):=20=E6=9B=B4?=
 =?UTF-8?q?=E6=96=B0=E6=A8=A1=E6=9D=BF=E7=9A=84=E4=BE=9D=E8=B5=96=E3=80=81?=
 =?UTF-8?q?=E6=96=87=E6=A1=A3=E7=AD=89=E9=83=A8=E5=88=86?=
MIME-Version: 1.0
Content-Type: text/plain; charset=UTF-8
Content-Transfer-Encoding: 8bit

---
 README.md       | 8 ++++++--
 README_zh_CN.md | 8 ++++++--
 package.json    | 2 +-
 plugin.json     | 4 ++--
 4 files changed, 15 insertions(+), 7 deletions(-)

diff --git a/README.md b/README.md
index 4a2b21b..4f79c10 100644
--- a/README.md
+++ b/README.md
@@ -3,7 +3,7 @@
 
 [中文版](./README_zh_CN.md)
 
-> Consistent with [siyuan/plugin-sample](https://github.com/siyuan-note/plugin-sample) [v0.2.6](https://github.com/siyuan-note/plugin-sample/tree/v0.2.6)
+> Consistent with [siyuan/plugin-sample](https://github.com/siyuan-note/plugin-sample) [v0.2.8](https://github.com/siyuan-note/plugin-sample/tree/v0.2.8)
 
 
 
@@ -98,7 +98,10 @@ conveniently.
     "custom": [
       "https://ld246.com/sponsor"
     ]
-  }
+  },
+  "keywords": [
+    "sample", "示例"
+  ]
 }
 ```
 
@@ -137,6 +140,7 @@ conveniently.
     * `patreon`: Patreon name
     * `github`: GitHub login name
     * `custom`: Custom sponsorship link list
+* `keywords`: Search keyword list, used for marketplace search function
 
 ## Package
 
diff --git a/README_zh_CN.md b/README_zh_CN.md
index bf72da3..5b3e6e8 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.2.6](https://github.com/siyuan-note/plugin-sample/tree/v0.2.6)
+> 本例同 [siyuan/plugin-sample](https://github.com/siyuan-note/plugin-sample) [v0.2.8](https://github.com/siyuan-note/plugin-sample/tree/v0.2.8)
 
 1. 使用 vite 打包
 2. 使用符号链接、而不是把项目放到插件目录下的模式进行开发
@@ -95,7 +95,10 @@
     "custom": [
       "https://ld246.com/sponsor"
     ]
-  }
+  },
+  "keywords": [
+    "sample", "示例"
+  ]
 }
 ```
 
@@ -133,6 +136,7 @@
   * `patreon`:Patreon 名称
   * `github`:GitHub 登录名
   * `custom`:自定义赞助链接列表
+* `keywords`:搜索关键字列表,用于集市搜索功能
 
 ## 打包
 
diff --git a/package.json b/package.json
index bfe4eb0..e4c06a2 100644
--- a/package.json
+++ b/package.json
@@ -22,7 +22,7 @@
     "minimist": "^1.2.8",
     "rollup-plugin-livereload": "^2.0.5",
     "sass": "^1.62.1",
-    "siyuan": "0.8.7",
+    "siyuan": "0.8.8",
     "svelte": "^3.57.0",
     "ts-node": "^10.9.1",
     "typescript": "^5.0.4",
diff --git a/plugin.json b/plugin.json
index f3e2682..7bb3bea 100644
--- a/plugin.json
+++ b/plugin.json
@@ -2,8 +2,8 @@
   "name": "plugin-sample-vite-svelte",
   "author": "frostime",
   "url": "https://github.com/siyuan-note/plugin-sample-vite-svelte",
-  "version": "0.2.6",
-  "minAppVersion": "2.10.12",
+  "version": "0.2.8",
+  "minAppVersion": "2.10.14",
   "backends": [
     "windows",
     "linux",

From 8e841a4a905f16c9accf2150e1e89b905fab0e5d Mon Sep 17 00:00:00 2001
From: frostime <frostime@foxmail.com>
Date: Sun, 19 Nov 2023 12:17:42 +0800
Subject: [PATCH 06/82] =?UTF-8?q?=E2=9C=A8=20feat(update):=20=E6=9B=B4?=
 =?UTF-8?q?=E6=96=B0=E6=8F=92=E4=BB=B6=E6=A8=A1=E6=9D=BF=E5=89=8D=E7=AB=AF?=
 =?UTF-8?q?=E9=83=A8=E5=88=86?=
MIME-Version: 1.0
Content-Type: text/plain; charset=UTF-8
Content-Transfer-Encoding: 8bit

---
 CHANGELOG.md     | 10 +++++++++-
 src/api.ts       |  1 -
 src/hello.svelte |  5 +++++
 src/index.ts     | 24 ++++++++++++++++++++----
 4 files changed, 34 insertions(+), 6 deletions(-)

diff --git a/CHANGELOG.md b/CHANGELOG.md
index aadd6ee..4411dd8 100644
--- a/CHANGELOG.md
+++ b/CHANGELOG.md
@@ -1,6 +1,14 @@
 # Changelog
 
-## 0.2.7 2023-10
+## 0.2.8 2023-11-15
+
+* [`resize` cannot be triggered after dragging to unpin the dock](https://github.com/siyuan-note/siyuan/issues/9640)
+
+## 0.2.7 2023-10-31
+
+* [Export `Constants` to plugin](https://github.com/siyuan-note/siyuan/issues/9555)
+* [Add plugin `app.appId`](https://github.com/siyuan-note/siyuan/issues/9538)
+* [Add plugin event bus `switch-protyle`](https://github.com/siyuan-note/siyuan/issues/9454)
 
 ## 0.2.6 2023-10-24
 
diff --git a/src/api.ts b/src/api.ts
index 4b12999..7202000 100644
--- a/src/api.ts
+++ b/src/api.ts
@@ -6,7 +6,6 @@
  * API 文档见 [API_zh_CN.md](https://github.com/siyuan-note/siyuan/blob/master/API_zh_CN.md)
  */
 
-import { time } from "console";
 import { fetchSyncPost, IWebSocketData } from "siyuan";
 
 
diff --git a/src/hello.svelte b/src/hello.svelte
index 63a54a2..cc9f0dd 100644
--- a/src/hello.svelte
+++ b/src/hello.svelte
@@ -36,6 +36,11 @@
 </script>
 
 <div class="b3-dialog__content">
+    <div>appId:</div>
+    <div class="fn__hr"></div>
+    <div class="plugin-sample__time">${app?.appId}</div>
+    <div class="fn__hr"></div>
+    <div class="fn__hr"></div>
     <div>API demo:</div>
     <div class="fn__hr" />
     <div class="plugin-sample__time">
diff --git a/src/index.ts b/src/index.ts
index aa7bd75..6fb4f0a 100644
--- a/src/index.ts
+++ b/src/index.ts
@@ -9,9 +9,10 @@ import {
     getFrontend,
     getBackend,
     IModel,
-    Setting,
-    fetchPost,
-    Protyle, openWindow, IOperation
+    Protyle,
+    openWindow,
+    IOperation,
+    Constants
 } from "siyuan";
 import "@/index.scss";
 
@@ -120,6 +121,9 @@ export default class PluginSample extends Plugin {
                 text: "This is my custom dock"
             },
             type: DOCK_TYPE,
+            resize() {
+                console.log(DOCK_TYPE + " resize");
+            },
             init() {
                 this.element.innerHTML = `<div class="fn__flex-1 fn__flex-column">
     <div class="block__icons">
@@ -308,7 +312,7 @@ export default class PluginSample extends Plugin {
 
     private showDialog() {
         let dialog = new Dialog({
-            title: "Hello World",
+            title: `SiYuan ${Constants.SIYUAN_VERSION}`,
             content: `<div id="helloPanel" class="b3-dialog__content"></div>`,
             width: this.isMobile ? "92vw" : "720px",
             destroyCallback(options) {
@@ -528,6 +532,18 @@ export default class PluginSample extends Plugin {
                 click: () => {
                     this.eventBus.off("loaded-protyle-dynamic", this.eventBusLog);
                 }
+            }, {
+                icon: "iconSelect",
+                label: "On switch-protyle",
+                click: () => {
+                    this.eventBus.on("switch-protyle", this.eventBusLog);
+                }
+            }, {
+                icon: "iconClose",
+                label: "Off switch-protyle",
+                click: () => {
+                    this.eventBus.off("switch-protyle", this.eventBusLog);
+                }
             }, {
                 icon: "iconSelect",
                 label: "On destroy-protyle",

From 962f879fa25d0e682f6da3d78f91467109846b49 Mon Sep 17 00:00:00 2001
From: frostime <frostime@foxmail.com>
Date: Sun, 19 Nov 2023 12:30:11 +0800
Subject: [PATCH 07/82] =?UTF-8?q?=E2=9C=A8=20feat(api):=20=E6=9B=B4?=
 =?UTF-8?q?=E6=96=B0=E5=90=8E=E7=AB=AFAPI=20`getIDsByHPath`?=
MIME-Version: 1.0
Content-Type: text/plain; charset=UTF-8
Content-Transfer-Encoding: 8bit

---
 src/api.ts | 10 ++++++++++
 1 file changed, 10 insertions(+)

diff --git a/src/api.ts b/src/api.ts
index 7202000..9a4ff3f 100644
--- a/src/api.ts
+++ b/src/api.ts
@@ -131,6 +131,16 @@ export async function getHPathByID(id: BlockId): Promise<string> {
     return request(url, data);
 }
 
+
+export async function getIDsByHPath(notebook: NotebookId, path: string): Promise<BlockId[]> {
+    let data = {
+        notebook: notebook,
+        path: path
+    };
+    let url = '/api/filetree/getIDsByHPath';
+    return request(url, data);
+}
+
 // **************************************** Asset Files ****************************************
 
 export async function upload(assetsDirPath: string, files: any[]): Promise<IResUpload> {

From 7f57c40db532464a53e9699de38ef24da5fd9a4d Mon Sep 17 00:00:00 2001
From: Frostime <frostime@foxmail.com>
Date: Tue, 21 Nov 2023 19:55:28 +0800
Subject: [PATCH 08/82] =?UTF-8?q?doc:=20=E5=AE=A3=E4=BC=A0=E4=B8=80?=
 =?UTF-8?q?=E4=B8=8B=E6=97=A0=20svelte=20=E7=89=88=E6=9C=AC=E7=9A=84?=
 =?UTF-8?q?=E6=A8=A1=E6=9D=BF?=
MIME-Version: 1.0
Content-Type: text/plain; charset=UTF-8
Content-Transfer-Encoding: 8bit

---
 README.md       | 3 +++
 README_zh_CN.md | 3 +++
 2 files changed, 6 insertions(+)

diff --git a/README.md b/README.md
index 4f79c10..b7da17d 100644
--- a/README.md
+++ b/README.md
@@ -10,6 +10,9 @@
 1. Using vite for packaging
 2. Use symbolic linking instead of putting the project into the plugins directory program development
 3. Built-in support for the svelte framework
+
+     > If don't want svelte, turn to this template: [frostime/plugin-sample-vite](https://github.com/frostime/plugin-sample-vite)
+
 4. Provides a github action template to automatically generate package.zip and upload to new release
 
 
diff --git a/README_zh_CN.md b/README_zh_CN.md
index 5b3e6e8..950f11c 100644
--- a/README_zh_CN.md
+++ b/README_zh_CN.md
@@ -9,6 +9,9 @@
 1. 使用 vite 打包
 2. 使用符号链接、而不是把项目放到插件目录下的模式进行开发
 3. 内置对 svelte 框架的支持
+  
+   > 如果不想要 svelte,请移步 [frostime/plugin-sample-vite](https://github.com/frostime/plugin-sample-vite)
+
 4. 提供一个github action 模板,能自动生成package.zip并上传到新版本中
 
 ## 开始

From ec8d768d2d9deb448f6a7932472e6f7ce308a89c Mon Sep 17 00:00:00 2001
From: frostime <frostime@foxmail.com>
Date: Mon, 27 Nov 2023 09:28:36 +0800
Subject: [PATCH 09/82] =?UTF-8?q?:construction=5Fworker:=20ci:=20=E4=BC=98?=
 =?UTF-8?q?=E5=8C=96=20vite=20=E9=85=8D=E7=BD=AE?=
MIME-Version: 1.0
Content-Type: text/plain; charset=UTF-8
Content-Transfer-Encoding: 8bit

---
 vite.config.ts | 1 +
 1 file changed, 1 insertion(+)

diff --git a/vite.config.ts b/vite.config.ts
index 2af9f2f..f83f26c 100644
--- a/vite.config.ts
+++ b/vite.config.ts
@@ -57,6 +57,7 @@ export default defineConfig({
     // 在这里自定义变量
     define: {
         "process.env.DEV_MODE": `"${isWatch}"`,
+        "process.env.NODE_ENV": JSON.stringify(process.env.NODE_ENV)
     },
 
     build: {

From b0d28e2513d6edc2e221ce1ccdfdea101ba450fa Mon Sep 17 00:00:00 2001
From: frostime <frostime@foxmail.com>
Date: Tue, 28 Nov 2023 21:24:19 +0800
Subject: [PATCH 10/82] =?UTF-8?q?:hammer:=20refactor(setting):=20=E9=87=8D?=
 =?UTF-8?q?=E6=9E=84=E4=BA=86Svelte=E8=AE=BE=E7=BD=AE=E7=9A=84=E6=A8=A1?=
 =?UTF-8?q?=E6=9D=BF?=
MIME-Version: 1.0
Content-Type: text/plain; charset=UTF-8
Content-Transfer-Encoding: 8bit

- 将 SettingPanel 作为基本模板
- 更改了 settingitem 的类型定义
---
 src/index.ts                  |  18 ++---
 src/libs/index.d.ts           |   5 +-
 src/libs/setting-item.svelte  |  14 +++-
 src/libs/setting-panel.svelte | 139 +++++++++++-----------------------
 src/libs/setting-utils.ts     |  10 ++-
 src/setting-example.svelte    |  99 ++++++++++++++++++++++++
 6 files changed, 169 insertions(+), 116 deletions(-)
 create mode 100644 src/setting-example.svelte

diff --git a/src/index.ts b/src/index.ts
index 6fb4f0a..349aca6 100644
--- a/src/index.ts
+++ b/src/index.ts
@@ -17,7 +17,7 @@ import {
 import "@/index.scss";
 
 import HelloExample from "@/hello.svelte";
-import SettingPannel from "@/libs/setting-panel.svelte";
+import SettingExample from "@/setting-example.svelte";
 
 import { SettingUtils } from "./libs/setting-utils";
 
@@ -173,16 +173,10 @@ export default class PluginSample extends Plugin {
             title: "Readonly text",
             description: "Select description",
             select: {
-                options: [
-                    {
-                        val: 1,
-                        text: "Option 1"
-                    },
-                    {
-                        val: 2,
-                        text: "Option 2"
-                    }
-                ]
+                options: {
+                    1: "Option 1",
+                    2: "Option 2"
+                }
             }
         });
         this.settingUtils.addItem({
@@ -270,7 +264,7 @@ export default class PluginSample extends Plugin {
                 pannel.$destroy();
             }
         });
-        let pannel = new SettingPannel({
+        let pannel = new SettingExample({
             target: dialog.element.querySelector("#SettingPanel"),
         });
     }
diff --git a/src/libs/index.d.ts b/src/libs/index.d.ts
index b46f655..379d841 100644
--- a/src/libs/index.d.ts
+++ b/src/libs/index.d.ts
@@ -5,13 +5,16 @@ interface ISettingItem {
     type: TSettingItemType;
     title: string;
     description?: string;
+    text?: {
+        placeholder?: string;
+    };
     slider?: {
         min: number;
         max: number;
         step: number;
     };
     select?: {
-        options: {val: any; text: string}[];
+        options: { [key: string | number]: string };
     };
     button?: {
         label: string;
diff --git a/src/libs/setting-item.svelte b/src/libs/setting-item.svelte
index b728cf4..6f17890 100644
--- a/src/libs/setting-item.svelte
+++ b/src/libs/setting-item.svelte
@@ -2,13 +2,13 @@
     import { createEventDispatcher } from "svelte";
     export let type: string; // Setting Type
     export let title: string; // Displayint Setting Title
-    export let text: string; // Displaying Setting Text
+    export let description: string; // Displaying Setting Text
     export let settingKey: string;
     export let settingValue: any;
 
     //Optional
     export let placeholder: string = ""; // Use it if type is input
-    export let options: { [key: string]: string } = {}; // Use it if type is select
+    export let options: { [key: string | number]: string } = {}; // Use it if type is select
     export let slider: {
         min: number;
         max: number;
@@ -30,7 +30,7 @@
     <div class="fn__flex-1">
         {title}
         <div class="b3-label__text">
-            {text}
+            {@html description}
         </div>
     </div>
     <span class="fn__space" />
@@ -53,6 +53,14 @@
             bind:value={settingValue}
             on:change={changed}
         />
+    {:else if type === "number"}
+        <input
+            class="b3-text-field fn__flex-center fn__size200"
+            id={settingKey}
+            type="number"
+            bind:value={settingValue}
+            on:change={changed}
+        />
     {:else if type === "button"}
         <!-- Button Input -->
         <button
diff --git a/src/libs/setting-panel.svelte b/src/libs/setting-panel.svelte
index ccf424e..f630c3a 100644
--- a/src/libs/setting-panel.svelte
+++ b/src/libs/setting-panel.svelte
@@ -1,99 +1,46 @@
-<script>
+<!--
+ Copyright (c) 2023 by frostime All Rights Reserved.
+ Author       : frostime
+ Date         : 2023-07-01 19:23:50
+ FilePath     : /src/libs/setting-panel.svelte
+ LastEditTime : 2023-11-28 21:23:56
+ Description  : 
+-->
+<script lang="ts">
+    import { createEventDispatcher } from "svelte";
     import SettingItem from "./setting-item.svelte";
-    import { showMessage } from "siyuan";
-    import { onMount, onDestroy } from 'svelte';
-    onMount(() => {
-        showMessage("Setting panel opened");
-    });
-    onDestroy(() => {
-        showMessage("Setting panel closed");
-    });
+
+    export let group: string;
+    export let settingItems: ISettingItem[];
+    export let display: boolean = true;
+
+    const dispatch = createEventDispatcher();
+
+    function onClick( {detail}) {
+        dispatch("click", { key: detail.key });
+    }
+    function onChanged( {detail}) {
+        dispatch("changed", {group: group, ...detail});
+    }
+
+    $: fn__none = display ? "" : "fn__none";
+
 </script>
 
-<!--
-You can use this template to quickly create a setting panel,
-with the same UI style in SiYuan
--->
-
-<div class="config__tab-container">
-    <div data-type="Header" class="fn__flex b3-label">
-        <div class="fn_flex-1">
-            <h4>This setting panel is provided by a svelte component</h4>
-            <div class="b3-label__text">
-                <span class="fn__flex-1">
-                    See:
-                    <pre style="display: inline">/lib/setting-pannel.svelte</pre>
-                </span>
-            </div>
-        </div>
-    </div>
-    <SettingItem
-        type="checkbox"
-        title="Checkbox"
-        text="This is a checkbox"
-        settingKey="Checkbox"
-        settingValue={true}
-        on:changed={(event) => {
-            showMessage(
-                `Checkbox changed: ${event.detail.key} = ${event.detail.value}`
-            );
-        }}
-    />
-    <SettingItem
-        type="input"
-        title="Input"
-        text="This is an input"
-        settingKey="Input"
-        settingValue=""
-        placeholder="Input something"
-        on:changed={(event) => {
-            showMessage(
-                `Input changed: ${event.detail.key} = ${event.detail.value}`
-            );
-        }}
-    />
-    <SettingItem
-        type="button"
-        title="Button"
-        text="This is a button"
-        settingKey="Button"
-        settingValue="Click me"
-        on:clicked={() => {
-            showMessage("Button clicked");
-        }}
-    />
-    <SettingItem
-        type="select"
-        title="Select"
-        text="This is a select"
-        settingKey="Select"
-        settingValue="left"
-        options={{
-            left: "Left",
-            center: "Center",
-            right: "Right",
-        }}
-        on:changed={(event) => {
-            showMessage(
-                `Select changed: ${event.detail.key} = ${event.detail.value}`
-            );
-        }}
-    />
-    <SettingItem
-        type="slider"
-        title="Slide"
-        text="This is a slide"
-        settingKey="Slide"
-        settingValue={50}
-        slider={{
-            min: 0,
-            max: 100,
-            step: 1,
-        }}
-        on:changed={(event) => {
-            showMessage(
-                `Slide changed: ${event.detail.key} = ${event.detail.value}`
-            );
-        }}
-    />
-</div>
+<div class="config__tab-container {fn__none}" data-name={group}>
+    <slot />
+    {#each settingItems as item (item.key)}
+        <SettingItem
+            type={item.type}
+            title={item.title}
+            description={item.description}
+            settingKey={item.key}
+            settingValue={item.value}
+            placeholder={item?.text.placeholder}
+            options={item?.select.options}
+            slider={item?.slider}
+            on:click={onClick}
+            on:changed={onChanged}
+        />
+    {/each}
+</div>
\ No newline at end of file
diff --git a/src/libs/setting-utils.ts b/src/libs/setting-utils.ts
index b092a27..23a7853 100644
--- a/src/libs/setting-utils.ts
+++ b/src/libs/setting-utils.ts
@@ -3,7 +3,7 @@
  * @Author       : frostime
  * @Date         : 2023-09-16 18:05:00
  * @FilePath     : /src/libs/setting-utils.ts
- * @LastEditTime : 2023-10-28 16:52:01
+ * @LastEditTime : 2023-11-28 21:16:36
  * @Description  : A utility for siyuan plugin settings
  */
 
@@ -93,10 +93,12 @@ export class SettingUtils {
             case 'select':
                 let selectElement: HTMLSelectElement = document.createElement('select');
                 selectElement.className = "b3-select fn__flex-center fn__size200";
-                for (let option of item.select?.options ?? []) {
+                let options = item.select?.options ?? {};
+                for (let val in options) {
                     let optionElement = document.createElement('option');
-                    optionElement.value = option.val;
-                    optionElement.text = option.text;
+                    let text = options[val];
+                    optionElement.value = val;
+                    optionElement.text = text;
                     selectElement.appendChild(optionElement);
                 }
                 selectElement.value = item.value;
diff --git a/src/setting-example.svelte b/src/setting-example.svelte
new file mode 100644
index 0000000..3843520
--- /dev/null
+++ b/src/setting-example.svelte
@@ -0,0 +1,99 @@
+<script>
+    import SettingItem from "@/libs/setting-item.svelte";
+    import { showMessage } from "siyuan";
+    import { onMount, onDestroy } from 'svelte';
+    onMount(() => {
+        showMessage("Setting panel opened");
+    });
+    onDestroy(() => {
+        showMessage("Setting panel closed");
+    });
+</script>
+
+<!--
+You can use this template to quickly create a setting panel,
+with the same UI style in SiYuan
+-->
+
+<div class="config__tab-container">
+    <div data-type="Header" class="fn__flex b3-label">
+        <div class="fn_flex-1">
+            <h4>This setting panel is provided by a svelte component</h4>
+            <div class="b3-label__text">
+                <span class="fn__flex-1">
+                    See:
+                    <pre style="display: inline">/lib/setting-pannel.svelte</pre>
+                </span>
+            </div>
+        </div>
+    </div>
+    <SettingItem
+        type="checkbox"
+        title="Checkbox"
+        description="This is a <b>checkbox</b>"
+        settingKey="Checkbox"
+        settingValue={true}
+        on:changed={(event) => {
+            showMessage(
+                `Checkbox changed: ${event.detail.key} = ${event.detail.value}`
+            );
+        }}
+    />
+    <SettingItem
+        type="input"
+        title="Input"
+        description="This is an input"
+        settingKey="Input"
+        settingValue=""
+        placeholder="Input something"
+        on:changed={(event) => {
+            showMessage(
+                `Input changed: ${event.detail.key} = ${event.detail.value}`
+            );
+        }}
+    />
+    <SettingItem
+        type="button"
+        title="Button"
+        description="This is a button"
+        settingKey="Button"
+        settingValue="Click me"
+        on:clicked={() => {
+            showMessage("Button clicked");
+        }}
+    />
+    <SettingItem
+        type="select"
+        title="Select"
+        description="This is a select"
+        settingKey="Select"
+        settingValue="left"
+        options={{
+            left: "Left",
+            center: "Center",
+            right: "Right",
+        }}
+        on:changed={(event) => {
+            showMessage(
+                `Select changed: ${event.detail.key} = ${event.detail.value}`
+            );
+        }}
+    />
+    <SettingItem
+        type="slider"
+        title="Slide"
+        description="This is a slide"
+        settingKey="Slide"
+        settingValue={50}
+        slider={{
+            min: 0,
+            max: 100,
+            step: 1,
+        }}
+        on:changed={(event) => {
+            showMessage(
+                `Slide changed: ${event.detail.key} = ${event.detail.value}`
+            );
+        }}
+    />
+</div>

From 2051a2a7d6c863d5df0905a9caba4f0b8876b63a Mon Sep 17 00:00:00 2001
From: frostime <frostime@foxmail.com>
Date: Tue, 28 Nov 2023 21:49:25 +0800
Subject: [PATCH 11/82] =?UTF-8?q?=E2=9C=85=20test:=20setting=20panel?=
MIME-Version: 1.0
Content-Type: text/plain; charset=UTF-8
Content-Transfer-Encoding: 8bit

---
 src/index.ts                  |  10 +-
 src/libs/index.d.ts           |   8 +-
 src/libs/setting-panel.svelte |   6 +-
 src/libs/setting-utils.ts     |   4 +-
 src/setting-example.svelte    | 190 +++++++++++++++++-----------------
 5 files changed, 107 insertions(+), 111 deletions(-)

diff --git a/src/index.ts b/src/index.ts
index 349aca6..14ba467 100644
--- a/src/index.ts
+++ b/src/index.ts
@@ -172,11 +172,9 @@ export default class PluginSample extends Plugin {
             type: "select",
             title: "Readonly text",
             description: "Select description",
-            select: {
-                options: {
-                    1: "Option 1",
-                    2: "Option 2"
-                }
+            options: {
+                1: "Option 1",
+                2: "Option 2"
             }
         });
         this.settingUtils.addItem({
@@ -256,7 +254,7 @@ export default class PluginSample extends Plugin {
     openDIYSetting(): void {
         let dialog = new Dialog({
             title: "SettingPannel",
-            content: `<div id="SettingPanel"></div>`,
+            content: `<div id="SettingPanel" style="height: 100%;"></div>`,
             width: "600px",
             destroyCallback: (options) => {
                 console.log("destroyCallback", options);
diff --git a/src/libs/index.d.ts b/src/libs/index.d.ts
index 379d841..7b0e81a 100644
--- a/src/libs/index.d.ts
+++ b/src/libs/index.d.ts
@@ -5,17 +5,13 @@ interface ISettingItem {
     type: TSettingItemType;
     title: string;
     description?: string;
-    text?: {
-        placeholder?: string;
-    };
+    placeholder?: string;
     slider?: {
         min: number;
         max: number;
         step: number;
     };
-    select?: {
-        options: { [key: string | number]: string };
-    };
+    options?: { [key: string | number]: string };
     button?: {
         label: string;
         callback: () => void;
diff --git a/src/libs/setting-panel.svelte b/src/libs/setting-panel.svelte
index f630c3a..fc5e508 100644
--- a/src/libs/setting-panel.svelte
+++ b/src/libs/setting-panel.svelte
@@ -3,7 +3,7 @@
  Author       : frostime
  Date         : 2023-07-01 19:23:50
  FilePath     : /src/libs/setting-panel.svelte
- LastEditTime : 2023-11-28 21:23:56
+ LastEditTime : 2023-11-28 21:45:10
  Description  : 
 -->
 <script lang="ts">
@@ -36,8 +36,8 @@
             description={item.description}
             settingKey={item.key}
             settingValue={item.value}
-            placeholder={item?.text.placeholder}
-            options={item?.select.options}
+            placeholder={item?.placeholder}
+            options={item?.options}
             slider={item?.slider}
             on:click={onClick}
             on:changed={onChanged}
diff --git a/src/libs/setting-utils.ts b/src/libs/setting-utils.ts
index 23a7853..98ca798 100644
--- a/src/libs/setting-utils.ts
+++ b/src/libs/setting-utils.ts
@@ -3,7 +3,7 @@
  * @Author       : frostime
  * @Date         : 2023-09-16 18:05:00
  * @FilePath     : /src/libs/setting-utils.ts
- * @LastEditTime : 2023-11-28 21:16:36
+ * @LastEditTime : 2023-11-28 21:46:29
  * @Description  : A utility for siyuan plugin settings
  */
 
@@ -93,7 +93,7 @@ export class SettingUtils {
             case 'select':
                 let selectElement: HTMLSelectElement = document.createElement('select');
                 selectElement.className = "b3-select fn__flex-center fn__size200";
-                let options = item.select?.options ?? {};
+                let options = item?.options ?? {};
                 for (let val in options) {
                     let optionElement = document.createElement('option');
                     let text = options[val];
diff --git a/src/setting-example.svelte b/src/setting-example.svelte
index 3843520..3ae156a 100644
--- a/src/setting-example.svelte
+++ b/src/setting-example.svelte
@@ -1,99 +1,101 @@
-<script>
-    import SettingItem from "@/libs/setting-item.svelte";
-    import { showMessage } from "siyuan";
-    import { onMount, onDestroy } from 'svelte';
-    onMount(() => {
-        showMessage("Setting panel opened");
-    });
-    onDestroy(() => {
-        showMessage("Setting panel closed");
-    });
+<script lang="ts">
+    import SettingPanel from "./libs/setting-panel.svelte";
+
+    let groups: string[] = ["🌈 Default"];
+    let focusGroup = groups[0];
+
+    const SettingItems: ISettingItem[] = [
+        {
+            type: 'checkbox',
+            title: 'checkbox',
+            description: 'checkbox',
+            key: 'a',
+            value: true
+        },
+        {
+            type: 'textinput',
+            title: 'text',
+            description: 'This is a text',
+            key: 'b',
+            value: 'This is a text',
+            placeholder: 'placeholder'
+        },
+        {
+            type: 'select',
+            title: 'select',
+            description: 'select',
+            key: 'c',
+            value: 'x',
+            options: {
+                x: 'x',
+                y: 'y',
+                z: 'z'
+            }
+        },
+        {
+            type: 'slider',
+            title: 'slider',
+            description: 'slider',
+            key: 'd',
+            value: 50,
+            slider: {
+                min: 0,
+                max: 100,
+                step: 1
+            }
+        }
+    ];
+
+    /********** Events **********/
+    interface ChangeEvent {
+        group: string;
+        key: string;
+        value: any;
+    }
+
+    const onChanged = ({ detail }: CustomEvent<ChangeEvent>) => {
+        if (detail.group === groups[0]) {
+            // setting.set(detail.key, detail.value);
+        }
+    };
 </script>
 
-<!--
-You can use this template to quickly create a setting panel,
-with the same UI style in SiYuan
--->
-
-<div class="config__tab-container">
-    <div data-type="Header" class="fn__flex b3-label">
-        <div class="fn_flex-1">
-            <h4>This setting panel is provided by a svelte component</h4>
-            <div class="b3-label__text">
-                <span class="fn__flex-1">
-                    See:
-                    <pre style="display: inline">/lib/setting-pannel.svelte</pre>
-                </span>
+<div class="fn__flex-1 fn__flex config__panel">
+    <ul class="b3-tab-bar b3-list b3-list--background">
+        {#each groups as group}
+            <li
+                data-name="editor"
+                class:b3-list-item--focus={group === focusGroup}
+                class="b3-list-item"
+                on:click={() => {
+                    focusGroup = group;
+                }}
+                on:keydown={() => {}}
+            >
+                <span class="b3-list-item__text">{group}</span>
+            </li>
+        {/each}
+    </ul>
+    <div class="config__tab-wrap">
+        <SettingPanel
+            group={groups[0]}
+            settingItems={SettingItems}
+            display={focusGroup === groups[0]}
+            on:changed={onChanged}
+        >
+            <div class="fn__flex b3-label">
+                💡 This is our default settings.
             </div>
-        </div>
+        </SettingPanel>
     </div>
-    <SettingItem
-        type="checkbox"
-        title="Checkbox"
-        description="This is a <b>checkbox</b>"
-        settingKey="Checkbox"
-        settingValue={true}
-        on:changed={(event) => {
-            showMessage(
-                `Checkbox changed: ${event.detail.key} = ${event.detail.value}`
-            );
-        }}
-    />
-    <SettingItem
-        type="input"
-        title="Input"
-        description="This is an input"
-        settingKey="Input"
-        settingValue=""
-        placeholder="Input something"
-        on:changed={(event) => {
-            showMessage(
-                `Input changed: ${event.detail.key} = ${event.detail.value}`
-            );
-        }}
-    />
-    <SettingItem
-        type="button"
-        title="Button"
-        description="This is a button"
-        settingKey="Button"
-        settingValue="Click me"
-        on:clicked={() => {
-            showMessage("Button clicked");
-        }}
-    />
-    <SettingItem
-        type="select"
-        title="Select"
-        description="This is a select"
-        settingKey="Select"
-        settingValue="left"
-        options={{
-            left: "Left",
-            center: "Center",
-            right: "Right",
-        }}
-        on:changed={(event) => {
-            showMessage(
-                `Select changed: ${event.detail.key} = ${event.detail.value}`
-            );
-        }}
-    />
-    <SettingItem
-        type="slider"
-        title="Slide"
-        description="This is a slide"
-        settingKey="Slide"
-        settingValue={50}
-        slider={{
-            min: 0,
-            max: 100,
-            step: 1,
-        }}
-        on:changed={(event) => {
-            showMessage(
-                `Slide changed: ${event.detail.key} = ${event.detail.value}`
-            );
-        }}
-    />
 </div>
+
+<style lang="scss">
+    .config__panel {
+        height: 100%;
+    }
+    .config__panel > ul > li {
+        padding-left: 1rem;
+    }
+</style>
+

From 2a082980604fa57268ad51705d4eec199cd79147 Mon Sep 17 00:00:00 2001
From: zxkmm <zxkmm@hotmail.com>
Date: Thu, 28 Dec 2023 17:07:43 +0800
Subject: [PATCH 12/82] fix wrong type def in lib to match header

---
 src/libs/setting-item.svelte | 2 +-
 1 file changed, 1 insertion(+), 1 deletion(-)

diff --git a/src/libs/setting-item.svelte b/src/libs/setting-item.svelte
index 6f17890..19ad92e 100644
--- a/src/libs/setting-item.svelte
+++ b/src/libs/setting-item.svelte
@@ -44,7 +44,7 @@
             bind:checked={settingValue}
             on:change={changed}
         />
-    {:else if type === "input"}
+    {:else if type === "textinput"}
         <!-- Text Input -->
         <input
             class="b3-text-field fn__flex-center fn__size200"

From c179519f953cd22e180bd03e7ffac25bc1c1b8dc Mon Sep 17 00:00:00 2001
From: frostime <frostime@foxmail.com>
Date: Thu, 28 Dec 2023 18:11:21 +0800
Subject: [PATCH 13/82] =?UTF-8?q?=E2=9C=A8=20feat:=20Add=20input=20number?=
 =?UTF-8?q?=20element=20in=20setting-utils?=
MIME-Version: 1.0
Content-Type: text/plain; charset=UTF-8
Content-Transfer-Encoding: 8bit

---
 src/libs/index.d.ts       | 2 +-
 src/libs/setting-utils.ts | 9 ++++++++-
 2 files changed, 9 insertions(+), 2 deletions(-)

diff --git a/src/libs/index.d.ts b/src/libs/index.d.ts
index 7b0e81a..b900c9f 100644
--- a/src/libs/index.d.ts
+++ b/src/libs/index.d.ts
@@ -1,4 +1,4 @@
-type TSettingItemType = "checkbox" | "select" | "textinput" | "textarea" | "slider" | "button";
+type TSettingItemType = "checkbox" | "select" | "textinput" | "textarea" | "number" | "slider" | "button";
 interface ISettingItem {
     key: string;
     value: any;
diff --git a/src/libs/setting-utils.ts b/src/libs/setting-utils.ts
index 98ca798..0446020 100644
--- a/src/libs/setting-utils.ts
+++ b/src/libs/setting-utils.ts
@@ -3,7 +3,7 @@
  * @Author       : frostime
  * @Date         : 2023-09-16 18:05:00
  * @FilePath     : /src/libs/setting-utils.ts
- * @LastEditTime : 2023-11-28 21:46:29
+ * @LastEditTime : 2023-12-28 18:10:12
  * @Description  : A utility for siyuan plugin settings
  */
 
@@ -130,6 +130,13 @@ export class SettingUtils {
                 textareaElement.value = item.value;
                 itemElement = textareaElement;
                 break;
+            case 'number':
+                let numberElement: HTMLInputElement = document.createElement('input');
+                numberElement.type = 'number';
+                numberElement.className = 'b3-text-field fn__flex-center fn__size200';
+                numberElement.value = item.value;
+                itemElement = numberElement;
+                break;
             case 'button':
                 let buttonElement: HTMLButtonElement = document.createElement('button');
                 buttonElement.className = "b3-button b3-button--outline fn__flex-center fn__size200";

From a2a615aae2531e4c170ead4829c5a88d028eefd2 Mon Sep 17 00:00:00 2001
From: frostime <frostime@foxmail.com>
Date: Thu, 28 Dec 2023 22:37:37 +0800
Subject: [PATCH 14/82] =?UTF-8?q?=E2=AC=86=EF=B8=8F=20update=20with=20siyu?=
 =?UTF-8?q?an=20petal?=
MIME-Version: 1.0
Content-Type: text/plain; charset=UTF-8
Content-Transfer-Encoding: 8bit

---
 CHANGELOG.md    | 16 +++++++++++++++
 README.md       |  2 +-
 README_zh_CN.md |  2 +-
 package.json    |  4 ++--
 plugin.json     | 11 +++++++---
 src/index.ts    | 53 +++++++++++++++++++++++++++++++++++--------------
 6 files changed, 66 insertions(+), 22 deletions(-)

diff --git a/CHANGELOG.md b/CHANGELOG.md
index 4411dd8..c5116c8 100644
--- a/CHANGELOG.md
+++ b/CHANGELOG.md
@@ -1,5 +1,21 @@
 # Changelog
 
+## 0.3.2 2024-01
+
+## 0.3.1 2023-12-06
+
+* [Support `Dock Plugin` and `Command Palette` on mobile](https://github.com/siyuan-note/siyuan/issues/9926)
+
+## 0.3.0 2023-12-05
+
+* Upgrade Siyuan to 0.9.0
+* Support more platforms
+
+## 0.2.9 2023-11-28
+
+* [Add plugin method `openMobileFileById`](https://github.com/siyuan-note/siyuan/issues/9738)
+
+
 ## 0.2.8 2023-11-15
 
 * [`resize` cannot be triggered after dragging to unpin the dock](https://github.com/siyuan-note/siyuan/issues/9640)
diff --git a/README.md b/README.md
index b7da17d..4d2e0be 100644
--- a/README.md
+++ b/README.md
@@ -3,7 +3,7 @@
 
 [中文版](./README_zh_CN.md)
 
-> Consistent with [siyuan/plugin-sample](https://github.com/siyuan-note/plugin-sample) [v0.2.8](https://github.com/siyuan-note/plugin-sample/tree/v0.2.8)
+> Consistent with [siyuan/plugin-sample](https://github.com/siyuan-note/plugin-sample) [v0.3.1](https://github.com/siyuan-note/plugin-sample/tree/v0.3.1)
 
 
 
diff --git a/README_zh_CN.md b/README_zh_CN.md
index 950f11c..817c11e 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.2.8](https://github.com/siyuan-note/plugin-sample/tree/v0.2.8)
+> 本例同 [siyuan/plugin-sample](https://github.com/siyuan-note/plugin-sample) [v0.3.1](https://github.com/siyuan-note/plugin-sample/tree/v0.3.1)
 
 1. 使用 vite 打包
 2. 使用符号链接、而不是把项目放到插件目录下的模式进行开发
diff --git a/package.json b/package.json
index e4c06a2..7a6045b 100644
--- a/package.json
+++ b/package.json
@@ -1,6 +1,6 @@
 {
   "name": "plugin-sample-vite-svelte",
-  "version": "0.2.6",
+  "version": "0.3.1",
   "type": "module",
   "description": "This is a sample plugin based on vite and svelte for Siyuan (https://b3log.org/siyuan)",
   "repository": "",
@@ -22,7 +22,7 @@
     "minimist": "^1.2.8",
     "rollup-plugin-livereload": "^2.0.5",
     "sass": "^1.62.1",
-    "siyuan": "0.8.8",
+    "siyuan": "0.9.1",
     "svelte": "^3.57.0",
     "ts-node": "^10.9.1",
     "typescript": "^5.0.4",
diff --git a/plugin.json b/plugin.json
index 7bb3bea..2385760 100644
--- a/plugin.json
+++ b/plugin.json
@@ -2,15 +2,20 @@
   "name": "plugin-sample-vite-svelte",
   "author": "frostime",
   "url": "https://github.com/siyuan-note/plugin-sample-vite-svelte",
-  "version": "0.2.8",
-  "minAppVersion": "2.10.14",
+  "version": "0.3.1",
+  "minAppVersion": "2.11.4",
   "backends": [
     "windows",
     "linux",
-    "darwin"
+    "darwin",
+    "ios",
+    "android"
   ],
   "frontends": [
     "desktop",
+    "mobile",
+    "browser-desktop",
+    "browser-mobile",
     "desktop-window"
   ],
   "displayName": {
diff --git a/src/index.ts b/src/index.ts
index 14ba467..d70da65 100644
--- a/src/index.ts
+++ b/src/index.ts
@@ -12,7 +12,8 @@ import {
     Protyle,
     openWindow,
     IOperation,
-    Constants
+    Constants,
+    openMobileFileById
 } from "siyuan";
 import "@/index.scss";
 
@@ -124,20 +125,34 @@ export default class PluginSample extends Plugin {
             resize() {
                 console.log(DOCK_TYPE + " resize");
             },
-            init() {
-                this.element.innerHTML = `<div class="fn__flex-1 fn__flex-column">
-    <div class="block__icons">
-        <div class="block__logo">
-            <svg><use xlink:href="#iconEmoji"></use></svg>
-            Custom Dock
-        </div>
-        <span class="fn__flex-1 fn__space"></span>
-        <span data-type="min" class="block__icon b3-tooltips b3-tooltips__sw" aria-label="Min ${adaptHotkey("⌘W")}"><svg><use xlink:href="#iconMin"></use></svg></span>
-    </div>
-    <div class="fn__flex-1 plugin-sample__custom-dock">
-        ${this.data.text}
-    </div>
-</div>`;
+            update() {
+                console.log(DOCK_TYPE + " update");
+            },
+            init: (dock) => {
+                if (this.isMobile) {
+                    dock.element.innerHTML = `<div class="toolbar toolbar--border toolbar--dark">
+                    <svg class="toolbar__icon"><use xlink:href="#iconEmoji"></use></svg>
+                        <div class="toolbar__text">Custom Dock</div>
+                    </div>
+                    <div class="fn__flex-1 plugin-sample__custom-dock">
+                        ${dock.data.text}
+                    </div>
+                    </div>`;
+                } else {
+                    dock.element.innerHTML = `<div class="fn__flex-1 fn__flex-column">
+                    <div class="block__icons">
+                        <div class="block__logo">
+                            <svg><use xlink:href="#iconEmoji"></use></svg>
+                            Custom Dock
+                        </div>
+                        <span class="fn__flex-1 fn__space"></span>
+                        <span data-type="min" class="block__icon b3-tooltips b3-tooltips__sw" aria-label="Min ${adaptHotkey("⌘W")}"><svg><use xlink:href="#iconMin"></use></svg></span>
+                    </div>
+                    <div class="fn__flex-1 plugin-sample__custom-dock">
+                        ${dock.data.text}
+                    </div>
+                    </div>`;
+                }
             },
             destroy() {
                 console.log("destroy dock:", DOCK_TYPE);
@@ -423,6 +438,14 @@ export default class PluginSample extends Plugin {
                     });
                 }
             });
+        } else {
+            menu.addItem({
+                icon: "iconFile",
+                label: "Open Doc(open help first)",
+                click: () => {
+                    openMobileFileById(this.app, "20200812220555-lj3enxa");
+                }
+            });
         }
         menu.addItem({
             icon: "iconScrollHoriz",

From 74d62ac1aab544af8cd2fea49852d1a136c69e64 Mon Sep 17 00:00:00 2001
From: frostime <frostime@foxmail.com>
Date: Thu, 28 Dec 2023 22:42:58 +0800
Subject: [PATCH 15/82] =?UTF-8?q?=E2=9C=A8=20feat:=20add=20`foldBlock`=20a?=
 =?UTF-8?q?pi?=
MIME-Version: 1.0
Content-Type: text/plain; charset=UTF-8
Content-Transfer-Encoding: 8bit

---
 src/api.ts | 18 ++++++++++++++++++
 1 file changed, 18 insertions(+)

diff --git a/src/api.ts b/src/api.ts
index 9a4ff3f..95c2816 100644
--- a/src/api.ts
+++ b/src/api.ts
@@ -224,6 +224,24 @@ export async function moveBlock(id: BlockId, previousID?: PreviousID, parentID?:
 }
 
 
+export async function foldBlock(id: BlockId) {
+    let data = {
+        id: id
+    }
+    let url = '/api/block/foldBlock';
+    return request(url, data);
+}
+
+
+export async function unfoldBlock(id: BlockId) {
+    let data = {
+        id: id
+    }
+    let url = '/api/block/unfoldBlock';
+    return request(url, data);
+}
+
+
 export async function getBlockKramdown(id: BlockId): Promise<IResGetBlockKramdown> {
     let data = {
         id: id

From 1735ec4bce08cd36bc7700417fdcd270ff852fe3 Mon Sep 17 00:00:00 2001
From: frostime <frostime@foxmail.com>
Date: Sat, 13 Jan 2024 21:40:22 +0800
Subject: [PATCH 16/82] =?UTF-8?q?=F0=9F=9A=80=20update=20to=20v0.3.2?=
MIME-Version: 1.0
Content-Type: text/plain; charset=UTF-8
Content-Transfer-Encoding: 8bit

---
 README.md       |  2 +-
 README_zh_CN.md |  2 +-
 package.json    |  4 +--
 plugin.json     |  4 +--
 src/api.ts      |  2 +-
 src/index.ts    | 73 ++++++++++++++++++++++++++++++++++++++++++++++++-
 6 files changed, 79 insertions(+), 8 deletions(-)

diff --git a/README.md b/README.md
index 4d2e0be..1f4ea7a 100644
--- a/README.md
+++ b/README.md
@@ -3,7 +3,7 @@
 
 [中文版](./README_zh_CN.md)
 
-> Consistent with [siyuan/plugin-sample](https://github.com/siyuan-note/plugin-sample) [v0.3.1](https://github.com/siyuan-note/plugin-sample/tree/v0.3.1)
+> Consistent with [siyuan/plugin-sample](https://github.com/siyuan-note/plugin-sample) [v0.3.2](https://github.com/siyuan-note/plugin-sample/tree/v0.3.2)
 
 
 
diff --git a/README_zh_CN.md b/README_zh_CN.md
index 817c11e..a0f9538 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.3.1](https://github.com/siyuan-note/plugin-sample/tree/v0.3.1)
+> 本例同 [siyuan/plugin-sample](https://github.com/siyuan-note/plugin-sample) [v0.3.2](https://github.com/siyuan-note/plugin-sample/tree/v0.3.2)
 
 1. 使用 vite 打包
 2. 使用符号链接、而不是把项目放到插件目录下的模式进行开发
diff --git a/package.json b/package.json
index 7a6045b..ed76517 100644
--- a/package.json
+++ b/package.json
@@ -1,6 +1,6 @@
 {
   "name": "plugin-sample-vite-svelte",
-  "version": "0.3.1",
+  "version": "0.3.2",
   "type": "module",
   "description": "This is a sample plugin based on vite and svelte for Siyuan (https://b3log.org/siyuan)",
   "repository": "",
@@ -22,7 +22,7 @@
     "minimist": "^1.2.8",
     "rollup-plugin-livereload": "^2.0.5",
     "sass": "^1.62.1",
-    "siyuan": "0.9.1",
+    "siyuan": "0.9.2",
     "svelte": "^3.57.0",
     "ts-node": "^10.9.1",
     "typescript": "^5.0.4",
diff --git a/plugin.json b/plugin.json
index 2385760..c9be70d 100644
--- a/plugin.json
+++ b/plugin.json
@@ -2,8 +2,8 @@
   "name": "plugin-sample-vite-svelte",
   "author": "frostime",
   "url": "https://github.com/siyuan-note/plugin-sample-vite-svelte",
-  "version": "0.3.1",
-  "minAppVersion": "2.11.4",
+  "version": "0.3.2",
+  "minAppVersion": "2.12.1",
   "backends": [
     "windows",
     "linux",
diff --git a/src/api.ts b/src/api.ts
index 95c2816..c227518 100644
--- a/src/api.ts
+++ b/src/api.ts
@@ -9,7 +9,7 @@
 import { fetchSyncPost, IWebSocketData } from "siyuan";
 
 
-async function request(url: string, data: any) {
+export async function request(url: string, data: any) {
     let response: IWebSocketData = await fetchSyncPost(url, data);
     let res = response.code === 0 ? response.data : null;
     return res;
diff --git a/src/index.ts b/src/index.ts
index d70da65..d434c5f 100644
--- a/src/index.ts
+++ b/src/index.ts
@@ -13,7 +13,10 @@ import {
     openWindow,
     IOperation,
     Constants,
-    openMobileFileById
+    openMobileFileById,
+    lockScreen,
+    ICard,
+    ICardData
 } from "siyuan";
 import "@/index.scss";
 
@@ -227,6 +230,38 @@ export default class PluginSample extends Plugin {
             }
         }];
 
+        this.protyleOptions = {
+            toolbar: ["block-ref",
+                "a",
+                "|",
+                "text",
+                "strong",
+                "em",
+                "u",
+                "s",
+                "mark",
+                "sup",
+                "sub",
+                "clear",
+                "|",
+                "code",
+                "kbd",
+                "tag",
+                "inline-math",
+                "inline-memo",
+                "|",
+                {
+                    name: "insert-smail-emoji",
+                    icon: "iconEmoji",
+                    hotkey: "⇧⌘I",
+                    tipPosition: "n",
+                    tip: this.i18n.insertEmoji,
+                    click(protyle: Protyle) {
+                        protyle.insert("😊");
+                    }
+                }],
+        };
+
         console.log(this.i18n.helloPlugin);
     }
 
@@ -263,6 +298,23 @@ export default class PluginSample extends Plugin {
         console.log("onunload");
     }
 
+    uninstall() {
+        console.log("uninstall");
+    }
+
+    async updateCards(options: ICardData) {
+        options.cards.sort((a: ICard, b: ICard) => {
+            if (a.blockID < b.blockID) {
+                return -1;
+            }
+            if (a.blockID > b.blockID) {
+                return 1;
+            }
+            return 0;
+        });
+        return options;
+    }
+
     /**
      * A custom setting pannel provided by svelte
      */
@@ -447,6 +499,13 @@ export default class PluginSample extends Plugin {
                 }
             });
         }
+        menu.addItem({
+            icon: "iconLock",
+            label: "Lockscreen",
+            click: () => {
+                lockScreen(this.app);
+            }
+        });
         menu.addItem({
             icon: "iconScrollHoriz",
             label: "Event Bus",
@@ -679,6 +738,18 @@ export default class PluginSample extends Plugin {
                 click: () => {
                     this.eventBus.off("open-menu-breadcrumbmore", this.eventBusLog);
                 }
+            }, {
+                icon: "iconSelect",
+                label: "On open-menu-inbox",
+                click: () => {
+                    this.eventBus.on("open-menu-inbox", this.eventBusLog);
+                }
+            }, {
+                icon: "iconClose",
+                label: "Off open-menu-inbox",
+                click: () => {
+                    this.eventBus.off("open-menu-inbox", this.eventBusLog);
+                }
             }, {
                 icon: "iconSelect",
                 label: "On input-search",

From f84497f6da331111b1f6a1b05ab1fee00a49cac9 Mon Sep 17 00:00:00 2001
From: zxkmm <zxkmm@hotmail.com>
Date: Sat, 13 Jan 2024 22:07:41 +0800
Subject: [PATCH 17/82] add hint type to settingUtil

---
 scripts/make_dev_link.js  |  2 +-
 src/i18n/en_US.json       |  4 +++-
 src/i18n/zh_CN.json       |  5 ++++-
 src/index.ts              | 22 ++++++++++++++++++++++
 src/libs/index.d.ts       |  2 +-
 src/libs/setting-utils.ts |  5 +++++
 6 files changed, 36 insertions(+), 4 deletions(-)

diff --git a/scripts/make_dev_link.js b/scripts/make_dev_link.js
index 845d298..3de72c1 100644
--- a/scripts/make_dev_link.js
+++ b/scripts/make_dev_link.js
@@ -7,7 +7,7 @@ import readline  from 'node:readline';
 
 //Please write the "workspace/data/plugins" directory here
 //请在这里填写你的 "workspace/data/plugins" 目录
-let targetDir = '';
+let targetDir = '/home/zxkmm/文档/siyuan_dev/data/plugins';
 //Like this
 // let targetDir = `H:\\SiYuanDevSpace\\data\\plugins`;
 //********************************************************************************************
diff --git a/src/i18n/en_US.json b/src/i18n/en_US.json
index c7ed102..7a11256 100644
--- a/src/i18n/en_US.json
+++ b/src/i18n/en_US.json
@@ -14,5 +14,7 @@
     "name": "SiYuan",
     "hello": {
         "makesure": "Before using this template, please read the <a href=\"https://github.com/siyuan-note/plugin-sample\">offical sample</a>, make sure that you've known about the pipeline for plugin developing."
-    }
+    },
+    "hintTitle":"About",
+    "hintDesc":"<a href='https://github.com/siyuan-note/plugin-sample-vite-svelte'>plugin-sample-vite-svelte</a><br>@frostime<br>@88250<br>@zxkmm"
 }
\ No newline at end of file
diff --git a/src/i18n/zh_CN.json b/src/i18n/zh_CN.json
index 49a7af8..9b6231e 100644
--- a/src/i18n/zh_CN.json
+++ b/src/i18n/zh_CN.json
@@ -14,5 +14,8 @@
     "name": "思源",
     "hello": {
         "makesure": "使用这个模板之前,请阅读<a href=\"https://github.com/siyuan-note/plugin-sample\">官方教程</a>, 确保自己已经理解了插件的基本开发流程。"
-    }
+    },
+    "hintTitle":"关于",
+    "hintDesc":"<a href='https://github.com/siyuan-note/plugin-sample-vite-svelte'>🔗 plugin-sample-vite-svelte</a><br>💻 @frostime<br>💻 @88250<br>💻 @zxkmm"
+
 }
\ No newline at end of file
diff --git a/src/index.ts b/src/index.ts
index d70da65..40c6da9 100644
--- a/src/index.ts
+++ b/src/index.ts
@@ -160,6 +160,13 @@ export default class PluginSample extends Plugin {
         });
 
         this.settingUtils = new SettingUtils(this, STORAGE_NAME);
+
+        try {
+            this.settingUtils.load();
+        } catch (error) {
+            console.error("Error loading settings storage, probably empty config json:", error);
+        }
+
         this.settingUtils.addItem({
             key: "Input",
             value: "",
@@ -217,6 +224,13 @@ export default class PluginSample extends Plugin {
                 }
             }
         });
+        this.settingUtils.addItem({
+            key: "Hint",
+            value: "",
+            type: "hint",
+            title: this.i18n.hintTitle,
+            description: this.i18n.hintDesc,
+        });
 
         this.protyleSlash = [{
             filter: ["insert emoji 😊", "插入表情 😊", "crbqwx"],
@@ -234,6 +248,14 @@ export default class PluginSample extends Plugin {
         // this.loadData(STORAGE_NAME);
         this.settingUtils.load();
         console.log(`frontend: ${getFrontend()}; backend: ${getBackend()}`);
+
+        console.log(
+            "Official settings value calling example:\n" +
+            this.settingUtils.get("InputArea") + "\n" +
+            this.settingUtils.get("Slider") + "\n" +
+            this.settingUtils.get("Select") + "\n"
+        );
+
         let tabDiv = document.createElement("div");
         new HelloExample({
             target: tabDiv,
diff --git a/src/libs/index.d.ts b/src/libs/index.d.ts
index b900c9f..04b53f0 100644
--- a/src/libs/index.d.ts
+++ b/src/libs/index.d.ts
@@ -1,4 +1,4 @@
-type TSettingItemType = "checkbox" | "select" | "textinput" | "textarea" | "number" | "slider" | "button";
+type TSettingItemType = "checkbox" | "select" | "textinput" | "textarea" | "number" | "slider" | "button" | "hint";
 interface ISettingItem {
     key: string;
     value: any;
diff --git a/src/libs/setting-utils.ts b/src/libs/setting-utils.ts
index 0446020..d0ae912 100644
--- a/src/libs/setting-utils.ts
+++ b/src/libs/setting-utils.ts
@@ -144,6 +144,11 @@ export class SettingUtils {
                 buttonElement.onclick = item.button?.callback ?? (() => {});
                 itemElement = buttonElement;
                 break;
+            case 'hint':
+                let hintElement: HTMLElement = document.createElement('div');
+                hintElement.className = 'b3-label fn__flex-center';
+                itemElement = hintElement;
+                break;
         }
         this.elements.set(item.key, itemElement);
         this.plugin.setting.addItem({

From 2ab75b2bf3f2e183bdfc3c3a3758229f7ba612b5 Mon Sep 17 00:00:00 2001
From: zxkmm <zxkmm@hotmail.com>
Date: Sat, 13 Jan 2024 22:17:09 +0800
Subject: [PATCH 18/82] clean up

---
 scripts/make_dev_link.js | 2 +-
 1 file changed, 1 insertion(+), 1 deletion(-)

diff --git a/scripts/make_dev_link.js b/scripts/make_dev_link.js
index 3de72c1..845d298 100644
--- a/scripts/make_dev_link.js
+++ b/scripts/make_dev_link.js
@@ -7,7 +7,7 @@ import readline  from 'node:readline';
 
 //Please write the "workspace/data/plugins" directory here
 //请在这里填写你的 "workspace/data/plugins" 目录
-let targetDir = '/home/zxkmm/文档/siyuan_dev/data/plugins';
+let targetDir = '';
 //Like this
 // let targetDir = `H:\\SiYuanDevSpace\\data\\plugins`;
 //********************************************************************************************

From e6a7277f8a673a010068cc5770e7a0d2d1f1e226 Mon Sep 17 00:00:00 2001
From: Seven Chord <hjlcarl@163.com>
Date: Sun, 14 Jan 2024 14:52:04 +0800
Subject: [PATCH 19/82] =?UTF-8?q?=E4=BD=BF=E7=94=A8public=E7=9B=AE?=
 =?UTF-8?q?=E5=BD=95=E6=9B=BF=E4=BB=A3=E5=A4=8D=E5=88=B6=E9=9D=99=E6=80=81?=
 =?UTF-8?q?=E8=B5=84=E6=BA=90=E7=9A=84=E9=85=8D=E7=BD=AE?=
MIME-Version: 1.0
Content-Type: text/plain; charset=UTF-8
Content-Transfer-Encoding: 8bit

---
 {src => public}/i18n/en_US.json   |   0
 {src => public}/i18n/zh_CN.json   |   0
 icon.png => public/icon.png       | Bin
 plugin.json => public/plugin.json |   0
 preview.png => public/preview.png | Bin
 vite.config.ts                    |  21 ++-------------------
 6 files changed, 2 insertions(+), 19 deletions(-)
 rename {src => public}/i18n/en_US.json (100%)
 rename {src => public}/i18n/zh_CN.json (100%)
 rename icon.png => public/icon.png (100%)
 rename plugin.json => public/plugin.json (100%)
 rename preview.png => public/preview.png (100%)

diff --git a/src/i18n/en_US.json b/public/i18n/en_US.json
similarity index 100%
rename from src/i18n/en_US.json
rename to public/i18n/en_US.json
diff --git a/src/i18n/zh_CN.json b/public/i18n/zh_CN.json
similarity index 100%
rename from src/i18n/zh_CN.json
rename to public/i18n/zh_CN.json
diff --git a/icon.png b/public/icon.png
similarity index 100%
rename from icon.png
rename to public/icon.png
diff --git a/plugin.json b/public/plugin.json
similarity index 100%
rename from plugin.json
rename to public/plugin.json
diff --git a/preview.png b/public/preview.png
similarity index 100%
rename from preview.png
rename to public/preview.png
diff --git a/vite.config.ts b/vite.config.ts
index f83f26c..91e3989 100644
--- a/vite.config.ts
+++ b/vite.config.ts
@@ -30,23 +30,7 @@ export default defineConfig({
                 {
                     src: "./README*.md",
                     dest: "./",
-                },
-                {
-                    src: "./icon.png",
-                    dest: "./",
-                },
-                {
-                    src: "./preview.png",
-                    dest: "./",
-                },
-                {
-                    src: "./plugin.json",
-                    dest: "./",
-                },
-                {
-                    src: "./src/i18n/**",
-                    dest: "./i18n/",
-                },
+                }
             ],
         }),
     ],
@@ -91,9 +75,8 @@ export default defineConfig({
                             name: 'watch-external',
                             async buildStart() {
                                 const files = await fg([
-                                    'src/i18n/*.json',
+                                    'public/**',
                                     './README*.md',
-                                    './plugin.json'
                                 ]);
                                 for (let file of files) {
                                     this.addWatchFile(file);

From bf21514b999ed905e580265f8072803b41168c4e Mon Sep 17 00:00:00 2001
From: frostime <frostime@foxmail.com>
Date: Mon, 15 Jan 2024 20:21:53 +0800
Subject: [PATCH 20/82] =?UTF-8?q?=F0=9F=94=A8=20Add=20plugin.json=20back?=
 =?UTF-8?q?=20to=20top?=
MIME-Version: 1.0
Content-Type: text/plain; charset=UTF-8
Content-Transfer-Encoding: 8bit

---
 public/plugin.json => plugin.json | 0
 vite.config.ts                    | 5 +++++
 2 files changed, 5 insertions(+)
 rename public/plugin.json => plugin.json (100%)

diff --git a/public/plugin.json b/plugin.json
similarity index 100%
rename from public/plugin.json
rename to plugin.json
diff --git a/vite.config.ts b/vite.config.ts
index 91e3989..96dd0d5 100644
--- a/vite.config.ts
+++ b/vite.config.ts
@@ -30,6 +30,10 @@ export default defineConfig({
                 {
                     src: "./README*.md",
                     dest: "./",
+                },
+                {
+                    src: "./plugin.json",
+                    dest: "./",
                 }
             ],
         }),
@@ -77,6 +81,7 @@ export default defineConfig({
                                 const files = await fg([
                                     'public/**',
                                     './README*.md',
+                                    './plugin.json'
                                 ]);
                                 for (let file of files) {
                                     this.addWatchFile(file);

From 953d15c66111592121a4f2a2eeb4f1f6b904fc90 Mon Sep 17 00:00:00 2001
From: frostime <frostime@foxmail.com>
Date: Mon, 15 Jan 2024 20:45:34 +0800
Subject: [PATCH 21/82] =?UTF-8?q?=E2=9C=A8=20feat:=20Update=20setting=20ut?=
 =?UTF-8?q?ils?=
MIME-Version: 1.0
Content-Type: text/plain; charset=UTF-8
Content-Transfer-Encoding: 8bit

---
 src/libs/setting-utils.ts | 49 ++++++++++++++++++++++++++++++++++-----
 1 file changed, 43 insertions(+), 6 deletions(-)

diff --git a/src/libs/setting-utils.ts b/src/libs/setting-utils.ts
index d0ae912..7112212 100644
--- a/src/libs/setting-utils.ts
+++ b/src/libs/setting-utils.ts
@@ -1,10 +1,10 @@
 /*
  * Copyright (c) 2023 by frostime. All Rights Reserved.
  * @Author       : frostime
- * @Date         : 2023-09-16 18:05:00
+ * @Date         : 2023-12-17 18:28:19
  * @FilePath     : /src/libs/setting-utils.ts
- * @LastEditTime : 2023-12-28 18:10:12
- * @Description  : A utility for siyuan plugin settings
+ * @LastEditTime : 2024-01-15 20:45:16
+ * @Description  : 
  */
 
 import { Plugin, Setting } from 'siyuan';
@@ -26,7 +26,7 @@ export class SettingUtils {
             height: height,
             confirmCallback: () => {
                 for (let key of this.settings.keys()) {
-                    this.updateValue(key);
+                    this.updateValueFromElement(key);
                 }
                 let data = this.dump();
                 if (callback !== undefined) {
@@ -35,6 +35,12 @@ export class SettingUtils {
                     this.plugin.data[this.name] = data;
                     this.save();
                 }
+            },
+            destroyCallback: () => {
+                //从值恢复元素
+                for (let key of this.settings.keys()) {
+                    this.updateElementFromValue(key);
+                }
             }
         });
     }
@@ -54,6 +60,7 @@ export class SettingUtils {
     async save() {
         let data = this.dump();
         await this.plugin.saveData(this.file, this.dump());
+        console.debug('Save config:', data);
         return data;
     }
 
@@ -66,6 +73,14 @@ export class SettingUtils {
         return this.settings.get(key)?.value;
     }
 
+    set(key: string, value: any) {
+        let item = this.settings.get(key);
+        if (item) {
+            item.value = value;
+            this.updateElementFromValue(key);
+        }
+    }
+
     /**
      * 将设置项目导出为 JSON 对象
      * @returns object
@@ -161,7 +176,7 @@ export class SettingUtils {
         })
     }
 
-    private getElement(key: string) {
+    getElement(key: string) {
         let item = this.settings.get(key);
         let element = this.elements.get(key) as any;
         switch (item.type) {
@@ -185,7 +200,7 @@ export class SettingUtils {
         return element;
     }
 
-    private updateValue(key: string) {
+    private updateValueFromElement(key: string) {
         let item = this.settings.get(key);
         let element = this.elements.get(key) as any;
         // console.debug(element, element?.value);
@@ -208,4 +223,26 @@ export class SettingUtils {
         }
     }
 
+    private updateElementFromValue(key: string) {
+        let item = this.settings.get(key);
+        let element = this.elements.get(key) as any;
+        switch (item.type) {
+            case 'checkbox':
+                element.checked = item.value;
+                break;
+            case 'select':
+                element.value = item.value;
+                break;
+            case 'slider':
+                element.value = item.value;
+                break;
+            case 'textinput':
+                element.value = item.value;
+                break;
+            case 'textarea':
+                element.value = item.value;
+                break;
+        }
+    }
+
 }
\ No newline at end of file

From 3ad078d548a7c5f5d35cd9e0fea243cc8c701bfb Mon Sep 17 00:00:00 2001
From: frostime <frostime@foxmail.com>
Date: Sun, 21 Jan 2024 13:14:34 +0800
Subject: [PATCH 22/82] =?UTF-8?q?=E2=AC=86=EF=B8=8F=20=20ci:=20update=20vi?=
 =?UTF-8?q?te?=
MIME-Version: 1.0
Content-Type: text/plain; charset=UTF-8
Content-Transfer-Encoding: 8bit

---
 package.json | 12 ++++++------
 1 file changed, 6 insertions(+), 6 deletions(-)

diff --git a/package.json b/package.json
index ed76517..e19a567 100644
--- a/package.json
+++ b/package.json
@@ -13,20 +13,20 @@
     "build": "vite build"
   },
   "devDependencies": {
-    "@sveltejs/vite-plugin-svelte": "^2.0.3",
+    "@sveltejs/vite-plugin-svelte": "^2.4.1",
     "@tsconfig/svelte": "^4.0.1",
-    "@types/node": "^20.2.0",
+    "@types/node": "^20.3.0",
     "eslint": "^8.42.0",
     "fast-glob": "^3.2.12",
     "glob": "^7.2.3",
     "minimist": "^1.2.8",
     "rollup-plugin-livereload": "^2.0.5",
-    "sass": "^1.62.1",
+    "sass": "^1.63.3",
     "siyuan": "0.9.2",
-    "svelte": "^3.57.0",
+    "svelte": "^3.59.1",
     "ts-node": "^10.9.1",
-    "typescript": "^5.0.4",
-    "vite": "^4.3.7",
+    "typescript": "^5.1.3",
+    "vite": "^4.3.9",
     "vite-plugin-static-copy": "^0.15.0",
     "vite-plugin-zip-pack": "^1.0.5"
   }

From 65b9505ae4f9a44343c5cc11a5ae84402e899db3 Mon Sep 17 00:00:00 2001
From: frostime <frostime@foxmail.com>
Date: Mon, 22 Jan 2024 00:36:26 +0800
Subject: [PATCH 23/82] :arrow_up: ci: update vite

---
 package.json | 2 +-
 1 file changed, 1 insertion(+), 1 deletion(-)

diff --git a/package.json b/package.json
index e19a567..20aed08 100644
--- a/package.json
+++ b/package.json
@@ -26,7 +26,7 @@
     "svelte": "^3.59.1",
     "ts-node": "^10.9.1",
     "typescript": "^5.1.3",
-    "vite": "^4.3.9",
+    "vite": "^4.5.2",
     "vite-plugin-static-copy": "^0.15.0",
     "vite-plugin-zip-pack": "^1.0.5"
   }

From 881ff608b08b29d383de38519c06a2f231db1778 Mon Sep 17 00:00:00 2001
From: frostime <frostime@foxmail.com>
Date: Tue, 23 Jan 2024 21:59:02 +0800
Subject: [PATCH 24/82] =?UTF-8?q?=F0=9F=9A=9A=20move:=20png=20back=20to=20?=
 =?UTF-8?q?root?=
MIME-Version: 1.0
Content-Type: text/plain; charset=UTF-8
Content-Transfer-Encoding: 8bit

---
 public/icon.png => icon.png       | Bin
 public/preview.png => preview.png | Bin
 2 files changed, 0 insertions(+), 0 deletions(-)
 rename public/icon.png => icon.png (100%)
 rename public/preview.png => preview.png (100%)

diff --git a/public/icon.png b/icon.png
similarity index 100%
rename from public/icon.png
rename to icon.png
diff --git a/public/preview.png b/preview.png
similarity index 100%
rename from public/preview.png
rename to preview.png

From 90b2805d9e505516715fe450226d0cebab2adbdc Mon Sep 17 00:00:00 2001
From: frostime <frostime@foxmail.com>
Date: Tue, 23 Jan 2024 22:03:34 +0800
Subject: [PATCH 25/82] =?UTF-8?q?=F0=9F=90=9B=20fix:=20vite.config.ts?=
MIME-Version: 1.0
Content-Type: text/plain; charset=UTF-8
Content-Transfer-Encoding: 8bit

---
 vite.config.ts | 10 +++++++++-
 1 file changed, 9 insertions(+), 1 deletion(-)

diff --git a/vite.config.ts b/vite.config.ts
index 96dd0d5..4da8c11 100644
--- a/vite.config.ts
+++ b/vite.config.ts
@@ -34,6 +34,14 @@ export default defineConfig({
                 {
                     src: "./plugin.json",
                     dest: "./",
+                },
+                {
+                    src: "./preview.png",
+                    dest: "./",
+                },
+                {
+                    src: "./icon.png",
+                    dest: "./",
                 }
             ],
         }),
@@ -79,7 +87,7 @@ export default defineConfig({
                             name: 'watch-external',
                             async buildStart() {
                                 const files = await fg([
-                                    'public/**',
+                                    'public/i18n/**',
                                     './README*.md',
                                     './plugin.json'
                                 ]);

From c33a63aa8fba10d6b212bc05acbdde5162af6230 Mon Sep 17 00:00:00 2001
From: frostime <frostime@foxmail.com>
Date: Wed, 24 Jan 2024 12:55:11 +0800
Subject: [PATCH 26/82] =?UTF-8?q?=F0=9F=90=9B=20fix:=202.12.4=20block=20ic?=
 =?UTF-8?q?on=20incompatible?=
MIME-Version: 1.0
Content-Type: text/plain; charset=UTF-8
Content-Transfer-Encoding: 8bit

---
 src/index.ts | 4 ++--
 1 file changed, 2 insertions(+), 2 deletions(-)

diff --git a/src/index.ts b/src/index.ts
index eec1136..1ec1fab 100644
--- a/src/index.ts
+++ b/src/index.ts
@@ -145,11 +145,11 @@ export default class PluginSample extends Plugin {
                     dock.element.innerHTML = `<div class="fn__flex-1 fn__flex-column">
                     <div class="block__icons">
                         <div class="block__logo">
-                            <svg><use xlink:href="#iconEmoji"></use></svg>
+                            <svg class="block__logoicon"><use xlink:href="#iconEmoji"></use></svg>
                             Custom Dock
                         </div>
                         <span class="fn__flex-1 fn__space"></span>
-                        <span data-type="min" class="block__icon b3-tooltips b3-tooltips__sw" aria-label="Min ${adaptHotkey("⌘W")}"><svg><use xlink:href="#iconMin"></use></svg></span>
+                        <span data-type="min" class="block__icon b3-tooltips b3-tooltips__sw" aria-label="Min ${adaptHotkey("⌘W")}"><svg class="block__logoicon"><use xlink:href="#iconMin"></use></svg></span>
                     </div>
                     <div class="fn__flex-1 plugin-sample__custom-dock">
                         ${dock.data.text}

From e48a9abbc9999111e0bf3acd072ed26135440ac7 Mon Sep 17 00:00:00 2001
From: frostime <frostime@foxmail.com>
Date: Sun, 3 Mar 2024 17:37:02 +0800
Subject: [PATCH 27/82] =?UTF-8?q?=F0=9F=9A=80=20update?=
MIME-Version: 1.0
Content-Type: text/plain; charset=UTF-8
Content-Transfer-Encoding: 8bit

---
 CHANGELOG.md | 20 +++++++++++++++++++-
 package.json |  2 +-
 plugin.json  |  4 ++--
 src/index.ts | 13 +++++++++++++
 4 files changed, 35 insertions(+), 4 deletions(-)

diff --git a/CHANGELOG.md b/CHANGELOG.md
index c5116c8..ce36ec8 100644
--- a/CHANGELOG.md
+++ b/CHANGELOG.md
@@ -1,6 +1,24 @@
 # Changelog
 
-## 0.3.2 2024-01
+## 0.3.5 2024-03
+
+## 0.3.4 2024-02-20
+
+* [Add plugin event bus `click-flashcard-action`](https://github.com/siyuan-note/siyuan/issues/10318)
+
+## 0.3.3 2024-01-24
+
+* Update dock icon class
+
+## 0.3.2 2024-01-09
+
+* [Add plugin `protyleOptions`](https://github.com/siyuan-note/siyuan/issues/10090)
+* [Add plugin api `uninstall`](https://github.com/siyuan-note/siyuan/issues/10063)
+* [Add plugin method `updateCards`](https://github.com/siyuan-note/siyuan/issues/10065)
+* [Add plugin function `lockScreen`](https://github.com/siyuan-note/siyuan/issues/10063)
+* [Add plugin event bus `lock-screen`](https://github.com/siyuan-note/siyuan/pull/9967)
+* [Add plugin event bus `open-menu-inbox`](https://github.com/siyuan-note/siyuan/pull/9967)
+
 
 ## 0.3.1 2023-12-06
 
diff --git a/package.json b/package.json
index 20aed08..24108a9 100644
--- a/package.json
+++ b/package.json
@@ -22,7 +22,7 @@
     "minimist": "^1.2.8",
     "rollup-plugin-livereload": "^2.0.5",
     "sass": "^1.63.3",
-    "siyuan": "0.9.2",
+    "siyuan": "0.9.4",
     "svelte": "^3.59.1",
     "ts-node": "^10.9.1",
     "typescript": "^5.1.3",
diff --git a/plugin.json b/plugin.json
index c9be70d..b810268 100644
--- a/plugin.json
+++ b/plugin.json
@@ -2,8 +2,8 @@
   "name": "plugin-sample-vite-svelte",
   "author": "frostime",
   "url": "https://github.com/siyuan-note/plugin-sample-vite-svelte",
-  "version": "0.3.2",
-  "minAppVersion": "2.12.1",
+  "version": "0.3.4",
+  "minAppVersion": "3.0.0",
   "backends": [
     "windows",
     "linux",
diff --git a/src/index.ts b/src/index.ts
index 1ec1fab..ce1c09b 100644
--- a/src/index.ts
+++ b/src/index.ts
@@ -120,6 +120,7 @@ export default class PluginSample extends Plugin {
                 size: { width: 200, height: 0 },
                 icon: "iconSaving",
                 title: "Custom Dock",
+                hotkey: "⌥⌘W",
             },
             data: {
                 text: "This is my custom dock"
@@ -592,6 +593,18 @@ export default class PluginSample extends Plugin {
                 click: () => {
                     this.eventBus.off("click-editortitleicon", this.eventBusLog);
                 }
+            }, {
+                icon: "iconSelect",
+                label: "On click-flashcard-action",
+                click: () => {
+                    this.eventBus.on("click-flashcard-action", this.eventBusLog);
+                }
+            }, {
+                icon: "iconClose",
+                label: "Off click-flashcard-action",
+                click: () => {
+                    this.eventBus.off("click-flashcard-action", this.eventBusLog);
+                }
             }, {
                 icon: "iconSelect",
                 label: "On open-noneditableblock",

From 0ff9fde56037722587ae814c0fb53386f924896c Mon Sep 17 00:00:00 2001
From: frostime <frostime@foxmail.com>
Date: Sun, 3 Mar 2024 17:41:28 +0800
Subject: [PATCH 28/82] =?UTF-8?q?=F0=9F=93=9D=20doc:=20update=20readme?=
MIME-Version: 1.0
Content-Type: text/plain; charset=UTF-8
Content-Transfer-Encoding: 8bit

---
 README.md       | 2 +-
 README_zh_CN.md | 2 +-
 2 files changed, 2 insertions(+), 2 deletions(-)

diff --git a/README.md b/README.md
index 1f4ea7a..df5278b 100644
--- a/README.md
+++ b/README.md
@@ -3,7 +3,7 @@
 
 [中文版](./README_zh_CN.md)
 
-> Consistent with [siyuan/plugin-sample](https://github.com/siyuan-note/plugin-sample) [v0.3.2](https://github.com/siyuan-note/plugin-sample/tree/v0.3.2)
+> Consistent with [siyuan/plugin-sample](https://github.com/siyuan-note/plugin-sample) [v0.3.4](https://github.com/siyuan-note/plugin-sample/tree/v0.3.4)
 
 
 
diff --git a/README_zh_CN.md b/README_zh_CN.md
index a0f9538..c24a95e 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.3.2](https://github.com/siyuan-note/plugin-sample/tree/v0.3.2)
+> 本例同 [siyuan/plugin-sample](https://github.com/siyuan-note/plugin-sample) [v0.3.4](https://github.com/siyuan-note/plugin-sample/tree/v0.3.4)
 
 1. 使用 vite 打包
 2. 使用符号链接、而不是把项目放到插件目录下的模式进行开发

From 4a7bd848a960a66bd549012de7a659fa5a30dea5 Mon Sep 17 00:00:00 2001
From: frostime <frostime@foxmail.com>
Date: Sat, 16 Mar 2024 13:43:27 +0800
Subject: [PATCH 29/82] =?UTF-8?q?=F0=9F=93=9D=20doc:=20add=20developer=20g?=
 =?UTF-8?q?uide=20document?=
MIME-Version: 1.0
Content-Type: text/plain; charset=UTF-8
Content-Transfer-Encoding: 8bit

---
 README.md       | 53 ++++++++++++++++++++++++++++++++++++++++++++++++
 README_zh_CN.md | 54 +++++++++++++++++++++++++++++++++++++++++++++++++
 2 files changed, 107 insertions(+)

diff --git a/README.md b/README.md
index df5278b..981ca43 100644
--- a/README.md
+++ b/README.md
@@ -233,3 +233,56 @@ If you insist on removing all svelte dependencies so that they do not pollute yo
     - Line 20: `svelte(),`
 4. delete line 37 of `tsconfig.json` from `"svelte"` 5.
 5. re-run `pnpm i`
+
+## Developer's Guide
+
+Developers of SiYuan need to pay attention to the following specifications.
+
+### 1. File Reading and Writing Specifications
+
+If plugins or external extensions require direct reading or writing of files under the `data` directory, please use the kernel API to achieve this. **Do not call `fs` or other electron or nodejs APIs directly**, as it may result in data loss during synchronization and cause damage to cloud data.
+
+Related APIs can be found at: `/api/file/*` (e.g., `/api/file/getFile`).
+
+### 2. Daily Note Attribute Specifications
+
+When creating a diary in SiYuan, a custom-dailynote-yyyymmdd attribute will be automatically added to the document to distinguish it from regular documents.
+
+> For more details, please refer to [Github Issue #9807](https://github.com/siyuan-note/siyuan/issues/9807).
+
+Developers should pay attention to the following when developing the functionality to manually create Daily Notes:
+
+- If `/api/filetree/createDailyNote` is called to create a diary, the attribute will be automatically added to the document, and developers do not need to handle it separately.
+- If a document is created manually by developer's code (e.g., using the `createDocWithMd` API to create a diary), please manually add this attribute to the document.
+
+Here is a reference code:
+
+```ts
+/*
+ * Copyright (c) 2023 by frostime. All Rights Reserved.
+ * @Author       : frostime
+ * @Url          : https://github.com/frostime/siyuan-dailynote-today/blob/v1.3.0/src/func/dailynote/dn-attr.ts
+ */
+
+export function formatDate(date?: Date, sep=''): string {
+    date = date === undefined ? new Date() : date;
+    let year = date.getFullYear();
+    let month = date.getMonth() + 1;
+    let day = date.getDate();
+    return `${year}${sep}${month < 10 ? '0' + month : month}${sep}${day < 10 ? '0' + day : day}`;
+}
+
+/**
+ * Set custom attribute: `custom-dailynote-yyyyMMdd`: yyyyMMdd
+ * https://github.com/siyuan-note/siyuan/issues/9807
+ * @param doc_id Id of daily note
+ */
+export function setCustomDNAttr(doc_id: string, date?: Date) {
+    let td = formatDate(date);
+    let attr = `custom-dailynote-${td}`;
+    // 构建 attr: td
+    let attrs: { [key: string]: string } = {};
+    attrs[attr] = td;
+    serverApi.setBlockAttrs(doc_id, attrs);
+}
+```
diff --git a/README_zh_CN.md b/README_zh_CN.md
index c24a95e..b2d3ad3 100644
--- a/README_zh_CN.md
+++ b/README_zh_CN.md
@@ -224,3 +224,57 @@ PR 社区集市仓库。
     - 第二十行: `svelte(),`
 4. 删掉 `tsconfig.json` 中 37 行 `"svelte"`
 5. 重新执行 `pnpm i`
+
+
+## 开发者须知
+
+思源开发者需注意以下规范。
+
+### 1. 读写文件规范
+
+插件或者外部扩展如果有直接读取或者写入 data 下文件的需求,请通过调用内核 API 来实现,**不要自行调用 `fs` 或者其他 electron、nodejs API**,否则可能会导致数据同步时分块丢失,造成云端数据损坏。
+
+相关 API 见: `/api/file/*`(例如 `/api/file/getFile` 等)。
+
+### 2. Daily Note 属性规范
+
+思源在创建日记的时候会自动为文档添加 custom-dailynote-yyyymmdd 属性, 以方便将日记文档同普通文档区分。
+
+> 详情请见 [Github Issue #9807](https://github.com/siyuan-note/siyuan/issues/9807)。
+
+开发者在开发手动创建 Daily Note 的功能时请注意:
+
+- 如果调用了 `/api/filetree/createDailyNote` 创建日记,那么文档会自动添加这个属性,无需开发者特别处理。
+- 如果是开发者代码手动创建文档(例如使用 `createDocWithMd` API 创建日记),请手动为文档添加该属性。
+
+参考代码:
+
+```ts
+/*
+ * Copyright (c) 2023 by frostime. All Rights Reserved.
+ * @Author       : frostime
+ * @Url          : https://github.com/frostime/siyuan-dailynote-today/blob/v1.3.0/src/func/dailynote/dn-attr.ts
+ */
+
+export function formatDate(date?: Date, sep=''): string {
+    date = date === undefined ? new Date() : date;
+    let year = date.getFullYear();
+    let month = date.getMonth() + 1;
+    let day = date.getDate();
+    return `${year}${sep}${month < 10 ? '0' + month : month}${sep}${day < 10 ? '0' + day : day}`;
+}
+
+/**
+ * Set custom attribute: `custom-dailynote-yyyyMMdd`: yyyyMMdd
+ * https://github.com/siyuan-note/siyuan/issues/9807
+ * @param doc_id Id of daily note
+ */
+export function setCustomDNAttr(doc_id: string, date?: Date) {
+    let td = formatDate(date);
+    let attr = `custom-dailynote-${td}`;
+    // 构建 attr: td
+    let attrs: { [key: string]: string } = {};
+    attrs[attr] = td;
+    serverApi.setBlockAttrs(doc_id, attrs);
+}
+```

From 9fcb5bb4817dc0c3e11e686f2f1db9981e956e5d Mon Sep 17 00:00:00 2001
From: frostime <frostime@foxmail.com>
Date: Thu, 28 Mar 2024 20:06:59 +0800
Subject: [PATCH 30/82] =?UTF-8?q?=E2=9C=A8=20feat:=20make=20install=20scri?=
 =?UTF-8?q?pt?=
MIME-Version: 1.0
Content-Type: text/plain; charset=UTF-8
Content-Transfer-Encoding: 8bit

---
 package.json            |   3 +-
 scripts/make_install.js | 191 ++++++++++++++++++++++++++++++++++++++++
 2 files changed, 193 insertions(+), 1 deletion(-)
 create mode 100644 scripts/make_install.js

diff --git a/package.json b/package.json
index 24108a9..3b29e49 100644
--- a/package.json
+++ b/package.json
@@ -10,7 +10,8 @@
   "scripts": {
     "make-link": "node  --no-warnings ./scripts/make_dev_link.js",
     "dev": "vite build --watch",
-    "build": "vite build"
+    "build": "vite build",
+    "install": "vite build && node --no-warnings ./scripts/make_install.js"
   },
   "devDependencies": {
     "@sveltejs/vite-plugin-svelte": "^2.4.1",
diff --git a/scripts/make_install.js b/scripts/make_install.js
new file mode 100644
index 0000000..9c2cd9b
--- /dev/null
+++ b/scripts/make_install.js
@@ -0,0 +1,191 @@
+import fs from 'fs';
+import path from 'path';
+import http from 'node:http';
+import readline  from 'node:readline';
+
+
+//************************************ Write you dir here ************************************
+
+let targetDir = ''; // the target directory of the plugin, '*/data/plugin'
+//********************************************************************************************
+
+const log = (info) => console.log(`\x1B[36m%s\x1B[0m`, info);
+const error = (info) => console.log(`\x1B[31m%s\x1B[0m`, info);
+
+let POST_HEADER = {
+    // "Authorization": `Token ${token}`,
+    "Content-Type": "application/json",
+}
+
+async function myfetch(url, options) {
+    //使用 http 模块,从而兼容那些不支持 fetch 的 nodejs 版本
+    return new Promise((resolve, reject) => {
+        let req = http.request(url, options, (res) => {
+            let data = '';
+            res.on('data', (chunk) => {
+                data += chunk;
+            });
+            res.on('end', () => {
+                resolve({
+                    ok: true,
+                    status: res.statusCode,
+                    json: () => JSON.parse(data)
+                });
+            });
+        });
+        req.on('error', (e) => {
+            reject(e);
+        });
+        req.end();
+    });
+}
+
+async function getSiYuanDir() {
+    let url = 'http://127.0.0.1:6806/api/system/getWorkspaces';
+    let conf = {};
+    try {
+        let response = await myfetch(url, {
+            method: 'POST',
+            headers: POST_HEADER
+        });
+        if (response.ok) {
+            conf = await response.json();
+        } else {
+            error(`\tHTTP-Error: ${response.status}`);
+            return null;
+        }
+    } catch (e) {
+        error(`\tError: ${e}`);
+        error("\tPlease make sure SiYuan is running!!!");
+        return null;
+    }
+    return conf.data;
+}
+
+async function chooseTarget(workspaces) {
+    let count = workspaces.length;
+    log(`>>> Got ${count} SiYuan ${count > 1 ? 'workspaces' : 'workspace'}`)
+    for (let i = 0; i < workspaces.length; i++) {
+        log(`\t[${i}] ${workspaces[i].path}`);
+    }
+
+    if (count == 1) {
+        return `${workspaces[0].path}/data/plugins`;
+    } else {
+        const rl = readline.createInterface({
+            input: process.stdin,
+            output: process.stdout
+        });
+        let index = await new Promise((resolve, reject) => {
+            rl.question(`\tPlease select a workspace[0-${count-1}]: `, (answer) => {
+                resolve(answer);
+            });
+        });
+        rl.close();
+        return `${workspaces[index].path}/data/plugins`;
+    }
+}
+
+log('>>> Try to visit constant "targetDir" in make_install.js...')
+
+if (targetDir === '') {
+    log('>>> Constant "targetDir" is empty, try to get SiYuan directory automatically....')
+    let res = await getSiYuanDir();
+    
+    if (res === null || res === undefined || res.length === 0) {
+        error('>>> Can not get SiYuan directory automatically');
+
+        process.exit(1);
+    } else {
+        targetDir = await chooseTarget(res);
+    }
+
+    log(`>>> Successfully got target directory: ${targetDir}`);
+}
+
+//Check
+if (!fs.existsSync(targetDir)) {
+    error(`Failed! plugin directory not exists: "${targetDir}"`);
+    error(`Please set the plugin directory in scripts/make_install.js`);
+    process.exit(1);
+}
+
+
+//check if plugin.json exists
+if (!fs.existsSync('./plugin.json')) {
+    //change dir to parent
+    process.chdir('../');
+    if (!fs.existsSync('./plugin.json')) {
+        error('Failed! plugin.json not found');
+        process.exit(1);
+    }
+}
+
+//load plugin.json
+const plugin = JSON.parse(fs.readFileSync('./plugin.json', 'utf8'));
+const name = plugin?.name;
+if (!name || name === '') {
+    error('Failed! Please set plugin name in plugin.json');
+    process.exit(1);
+}
+
+const distDir = `${process.cwd()}/dist`;
+//mkdir if not exists
+if (!fs.existsSync(distDir)) {
+    fs.mkdirSync(distDir);
+}
+
+function cmpPath(path1, path2) {
+    path1 = path1.replace(/\\/g, '/');
+    path2 = path2.replace(/\\/g, '/');
+    // sepertor at tail
+    if (path1[path1.length - 1] !== '/') {
+        path1 += '/';
+    }
+    if (path2[path2.length - 1] !== '/') {
+        path2 += '/';
+    }
+    return path1 === path2;
+}
+
+const targetPath = `${targetDir}/${name}`;
+
+
+function copyDirectory(srcDir, dstDir) {
+    if (!fs.existsSync(dstDir)) {
+        fs.mkdirSync(dstDir);
+        log(`Created directory ${dstDir}`);
+    }
+    //将 distDir 下的所有文件复制到 targetPath
+    fs.readdir(srcDir, { withFileTypes: true }, (err, files) => {
+        if (err) {
+            error('Error reading source directory:', err);
+            return;
+        }
+
+        // 遍历源目录中的所有文件和子目录
+        files.forEach((file) => {
+            const src = path.join(srcDir, file.name);
+            const dst = path.join(dstDir, file.name);
+
+            // 判断当前项是文件还是目录
+            if (file.isDirectory()) {
+                // 如果是目录,则递归调用复制函数复制子目录
+                copyDirectory(src, dst);
+            } else {
+                // 如果是文件,则复制文件到目标目录
+                fs.copyFile(src, dst, (err) => {
+                if (err) {
+                    error('Error copying file:' + err);
+                } else {
+                    log(`Copied file: ${src} --> ${dst}`);
+                }
+                });
+            }
+        });
+        log(`Copied ${distDir} to ${targetPath}`);
+    });
+}
+copyDirectory(distDir, targetPath);
+
+

From ec00075b4e50ec59040e3f4ea735b655aa96a59d Mon Sep 17 00:00:00 2001
From: frostime <frostime@foxmail.com>
Date: Thu, 4 Apr 2024 17:50:01 +0800
Subject: [PATCH 31/82] =?UTF-8?q?=E2=9C=A8=20remove=20save=20at=20`onunloa?=
 =?UTF-8?q?d`?=
MIME-Version: 1.0
Content-Type: text/plain; charset=UTF-8
Content-Transfer-Encoding: 8bit

---
 src/index.ts | 1 -
 1 file changed, 1 deletion(-)

diff --git a/src/index.ts b/src/index.ts
index ce1c09b..307f7d2 100644
--- a/src/index.ts
+++ b/src/index.ts
@@ -316,7 +316,6 @@ export default class PluginSample extends Plugin {
 
     async onunload() {
         console.log(this.i18n.byePlugin);
-        await this.settingUtils.save();
         showMessage("Goodbye SiYuan Plugin");
         console.log("onunload");
     }

From c58c836b7c6be1295baa3560add0199e33f06e41 Mon Sep 17 00:00:00 2001
From: =?UTF-8?q?=E6=B5=94=E9=98=B3=E9=99=8C=E5=AE=A2?=
 <virtualtengfei@163.com>
Date: Fri, 5 Apr 2024 16:37:25 +0800
Subject: [PATCH 32/82] =?UTF-8?q?=E4=B8=BAcheckout=E6=B7=BB=E5=8A=A0?=
 =?UTF-8?q?=E5=88=87=E6=8D=A2=E7=8A=B6=E6=80=81=E6=97=B6=E6=B7=BB=E5=8A=A0?=
 =?UTF-8?q?=E5=9B=9E=E8=B0=83=E5=87=BD=E6=95=B0?=
MIME-Version: 1.0
Content-Type: text/plain; charset=UTF-8
Content-Transfer-Encoding: 8bit

---
 .gitignore                   | 1 +
 src/index.ts                 | 5 +++++
 src/libs/index.d.ts          | 3 +++
 src/libs/setting-item.svelte | 2 +-
 src/libs/setting-utils.ts    | 1 +
 5 files changed, 11 insertions(+), 1 deletion(-)

diff --git a/.gitignore b/.gitignore
index 764e2d4..c2a2064 100644
--- a/.gitignore
+++ b/.gitignore
@@ -2,6 +2,7 @@
 .vscode
 .DS_Store
 pnpm-lock.yaml
+package-lock.json
 package.zip
 node_modules
 dev
diff --git a/src/index.ts b/src/index.ts
index 307f7d2..0e8893a 100644
--- a/src/index.ts
+++ b/src/index.ts
@@ -191,6 +191,11 @@ export default class PluginSample extends Plugin {
             type: "checkbox",
             title: "Checkbox text",
             description: "Check description",
+            checkbox: {
+                callback: () => {
+                    console.log("Checkbox clicked");
+                }
+            }
         });
         this.settingUtils.addItem({
             key: "Select",
diff --git a/src/libs/index.d.ts b/src/libs/index.d.ts
index 04b53f0..5181c19 100644
--- a/src/libs/index.d.ts
+++ b/src/libs/index.d.ts
@@ -12,6 +12,9 @@ interface ISettingItem {
         step: number;
     };
     options?: { [key: string | number]: string };
+    checkbox?: {
+        callback: () => void;
+    }
     button?: {
         label: string;
         callback: () => void;
diff --git a/src/libs/setting-item.svelte b/src/libs/setting-item.svelte
index 19ad92e..d59912b 100644
--- a/src/libs/setting-item.svelte
+++ b/src/libs/setting-item.svelte
@@ -31,7 +31,7 @@
         {title}
         <div class="b3-label__text">
             {@html description}
-        </div>
+        </div> 
     </div>
     <span class="fn__space" />
     <!-- <slot /> -->
diff --git a/src/libs/setting-utils.ts b/src/libs/setting-utils.ts
index 7112212..323443b 100644
--- a/src/libs/setting-utils.ts
+++ b/src/libs/setting-utils.ts
@@ -104,6 +104,7 @@ export class SettingUtils {
                 element.checked = item.value;
                 element.className = "b3-switch fn__flex-center";
                 itemElement = element;
+                element.onchange = item.checkbox?.callback ?? (() => { });
                 break;
             case 'select':
                 let selectElement: HTMLSelectElement = document.createElement('select');

From f9ea1bec3fa915eb192200fb819c9c9c9fb4731b Mon Sep 17 00:00:00 2001
From: =?UTF-8?q?=E6=B5=94=E9=98=B3=E9=99=8C=E5=AE=A2?=
 <virtualtengfei@163.com>
Date: Fri, 5 Apr 2024 21:16:33 +0800
Subject: [PATCH 33/82] =?UTF-8?q?=E4=B8=BA=E8=A1=A8=E5=8D=95=E6=8F=90?=
 =?UTF-8?q?=E4=BE=9B=E7=A6=81=E7=94=A8=E5=92=8C=E5=90=AF=E7=94=A8=E7=9A=84?=
 =?UTF-8?q?=E6=96=B9=E6=B3=95?=
MIME-Version: 1.0
Content-Type: text/plain; charset=UTF-8
Content-Transfer-Encoding: 8bit

---
 src/libs/setting-utils.ts | 21 +++++++++++++++++++++
 1 file changed, 21 insertions(+)

diff --git a/src/libs/setting-utils.ts b/src/libs/setting-utils.ts
index 323443b..17a850b 100644
--- a/src/libs/setting-utils.ts
+++ b/src/libs/setting-utils.ts
@@ -81,6 +81,27 @@ export class SettingUtils {
         }
     }
 
+    /**
+     * Disable setting item
+     * @param key key name
+     */
+    disable(key: string) {
+        let element = this.elements.get(key) as any;
+        if (element) {
+            element.disabled = true;
+        }
+    }
+    /**
+     * Enable setting item
+     * @param key key name
+     */
+    enable(key: string) {
+        let element = this.elements.get(key) as any;
+        if (element) {
+            element.disabled = false;
+        }
+    }
+
     /**
      * 将设置项目导出为 JSON 对象
      * @returns object

From 91deda2a723086b702d333334a2d6907d85e1053 Mon Sep 17 00:00:00 2001
From: frostime <frostime@foxmail.com>
Date: Fri, 5 Apr 2024 22:41:07 +0800
Subject: [PATCH 34/82] =?UTF-8?q?=F0=9F=94=A7=20build:=20=E4=BD=BF?=
 =?UTF-8?q?=E7=94=A8=20yaml=20i18n?=
MIME-Version: 1.0
Content-Type: text/plain; charset=UTF-8
Content-Transfer-Encoding: 8bit

---
 README.md              |  5 ++-
 README_zh_CN.md        |  5 ++-
 package.json           |  1 +
 public/i18n/en_US.yaml |  0
 public/i18n/zh_CN.json | 21 -------------
 public/i18n/zh_CN.yaml | 21 +++++++++++++
 vite.config.ts         | 11 +++++--
 yaml-plugin.js         | 69 ++++++++++++++++++++++++++++++++++++++++++
 8 files changed, 108 insertions(+), 25 deletions(-)
 create mode 100644 public/i18n/en_US.yaml
 delete mode 100644 public/i18n/zh_CN.json
 create mode 100644 public/i18n/zh_CN.yaml
 create mode 100644 yaml-plugin.js

diff --git a/README.md b/README.md
index 981ca43..3a6e717 100644
--- a/README.md
+++ b/README.md
@@ -65,8 +65,11 @@ complete the following tasks:
 * Meta information about the plugin itself, such as plugin description and readme
     * `description` and `readme` fields in plugin.json, and the corresponding README*.md file
 * Text used in the plugin, such as button text and tooltips
-    * src/i18n/*.json language configuration files
+    * public/i18n/*.json language configuration files
     * Use `this.i18.key` to get the text in the code
+* YAML Support
+  * This template specifically supports I18n based on YAML syntax, see `public/i18n/zh_CN.yaml`
+  * During compilation, the defined YAML files will be automatically translated into JSON files and placed in the dist or dev directory.
 
 It is recommended that the plugin supports at least English and Simplified Chinese, so that more people can use it more
 conveniently.
diff --git a/README_zh_CN.md b/README_zh_CN.md
index b2d3ad3..6b46f16 100644
--- a/README_zh_CN.md
+++ b/README_zh_CN.md
@@ -62,9 +62,12 @@
 * 插件自身的元信息,比如插件描述和自述文件
   * plugin.json 中的 `description` 和 `readme` 字段,以及对应的 README*.md 文件
 * 插件中使用的文本,比如按钮文字和提示信息
-  * src/i18n/*.json 语言配置文件
+  * public/i18n/*.json 语言配置文件
   * 代码中使用 `this.i18.key` 获取文本
 * 最后在 plugin.json 中的 `i18n` 字段中声明该插件支持的语言
+* yaml 支持
+  * 本模板特别支持基于 Yaml 语法的 I18n,见 `public/i18n/zh_CN.yaml`
+  * 编译时,会自动把定义的 yaml 文件翻译成 json 文件放到 dist 或 dev 目录下
 
 建议插件至少支持英文和简体中文,这样可以方便更多人使用。
 
diff --git a/package.json b/package.json
index 3b29e49..fdcce30 100644
--- a/package.json
+++ b/package.json
@@ -20,6 +20,7 @@
     "eslint": "^8.42.0",
     "fast-glob": "^3.2.12",
     "glob": "^7.2.3",
+    "js-yaml": "^4.1.0",
     "minimist": "^1.2.8",
     "rollup-plugin-livereload": "^2.0.5",
     "sass": "^1.63.3",
diff --git a/public/i18n/en_US.yaml b/public/i18n/en_US.yaml
new file mode 100644
index 0000000..e69de29
diff --git a/public/i18n/zh_CN.json b/public/i18n/zh_CN.json
deleted file mode 100644
index 9b6231e..0000000
--- a/public/i18n/zh_CN.json
+++ /dev/null
@@ -1,21 +0,0 @@
-{
-    "addTopBarIcon": "使用插件添加一个顶栏按钮",
-    "cancel": "取消",
-    "save": "保存",
-    "byeMenu": "再见,菜单!",
-    "helloPlugin": "你好,插件!",
-    "byePlugin": "再见,插件!",
-    "showDialog": "弹出一个对话框",
-    "removedData": "数据已删除",
-    "confirmRemove": "确认删除 ${name} 中的数据?",
-    "insertEmoji": "插入表情",
-    "removeSpace": "移除空格",
-    "getTab": "在日志中打印出已打开的所有自定义页签",
-    "name": "思源",
-    "hello": {
-        "makesure": "使用这个模板之前,请阅读<a href=\"https://github.com/siyuan-note/plugin-sample\">官方教程</a>, 确保自己已经理解了插件的基本开发流程。"
-    },
-    "hintTitle":"关于",
-    "hintDesc":"<a href='https://github.com/siyuan-note/plugin-sample-vite-svelte'>🔗 plugin-sample-vite-svelte</a><br>💻 @frostime<br>💻 @88250<br>💻 @zxkmm"
-
-}
\ No newline at end of file
diff --git a/public/i18n/zh_CN.yaml b/public/i18n/zh_CN.yaml
new file mode 100644
index 0000000..16099a3
--- /dev/null
+++ b/public/i18n/zh_CN.yaml
@@ -0,0 +1,21 @@
+---
+addTopBarIcon: 使用插件添加一个顶栏按钮
+cancel: 取消
+save: 保存
+byeMenu: 再见,菜单!
+helloPlugin: 你好,插件!
+byePlugin: 再见,插件!
+showDialog: 弹出一个对话框
+removedData: 数据已删除
+confirmRemove: 确认删除 ${name} 中的数据?
+insertEmoji: 插入表情
+removeSpace: 移除空格
+getTab: 在日志中打印出已打开的所有自定义页签
+name: 思源
+hello:
+  makesure: 使用这个模板之前,请阅读<a href="https://github.com/siyuan-note/plugin-sample">官方教程</a>,
+    确保自己已经理解了插件的基本开发流程。
+hintTitle: 关于
+hintDesc: "<a href='https://github.com/siyuan-note/plugin-sample-vite-svelte'>\U0001F517
+  plugin-sample-vite-svelte</a><br>\U0001F4BB @frostime<br>\U0001F4BB @88250<br>\U0001F4BB
+  @zxkmm"
diff --git a/vite.config.ts b/vite.config.ts
index 4da8c11..31fc585 100644
--- a/vite.config.ts
+++ b/vite.config.ts
@@ -7,10 +7,12 @@ import { svelte } from "@sveltejs/vite-plugin-svelte"
 import zipPack from "vite-plugin-zip-pack";
 import fg from 'fast-glob';
 
+import vitePluginYamlI18n from './yaml-plugin';
+
 const args = minimist(process.argv.slice(2))
 const isWatch = args.watch || args.w || false
-const devDistDir = "./dev"
-const distDir = isWatch ? devDistDir : "./dist"
+const devDistDir = "dev"
+const distDir = isWatch ? devDistDir : "dist"
 
 console.log("isWatch=>", isWatch)
 console.log("distDir=>", distDir)
@@ -25,6 +27,11 @@ export default defineConfig({
     plugins: [
         svelte(),
 
+        vitePluginYamlI18n({
+            inDir: 'public/i18n',
+            outDir: `${distDir}/i18n`
+        }),
+
         viteStaticCopy({
             targets: [
                 {
diff --git a/yaml-plugin.js b/yaml-plugin.js
new file mode 100644
index 0000000..48c88f1
--- /dev/null
+++ b/yaml-plugin.js
@@ -0,0 +1,69 @@
+/*
+ * Copyright (c) 2024 by frostime. All Rights Reserved.
+ * @Author       : frostime
+ * @Date         : 2024-04-05 21:27:55
+ * @FilePath     : /yaml-plugin.js
+ * @LastEditTime : 2024-04-05 22:37:40
+ * @Description  : 去妮玛的 json 格式,我就是要用 yaml 写 i18n
+ */
+// plugins/vite-plugin-parse-yaml.js
+import fs from 'fs';
+import yaml from 'js-yaml';
+import { resolve } from 'path';
+
+export default function vitePluginYamlI18n(options = {}) {
+    // Default options with a fallback
+    const DefaultOptions = {
+        inDir: 'src/i18n',
+        outDir: 'dist/i18n',
+    };
+
+    const finalOptions = { ...DefaultOptions, ...options };
+
+    return {
+        name: 'vite-plugin-yaml-i18n',
+        buildStart() {
+            console.log('🌈 Parse I18n: YAML to JSON..');
+            const inDir = finalOptions.inDir;
+            const outDir = finalOptions.outDir
+
+            if (!fs.existsSync(outDir)) {
+                fs.mkdirSync(outDir, { recursive: true });
+            }
+
+            //Remove yaml under outDir, which is auto-moved by vite
+            const outFiles = fs.readdirSync(outDir);
+            for (const file of outFiles) {
+                if (file.endsWith('.yaml') || file.endsWith('.yml')) {
+                    console.log(`-- Remove yaml file ${file} under dist`);
+                    fs.unlinkSync(resolve(outDir, file));
+                }
+            }
+
+            //Parse yaml file, output to json
+            const files = fs.readdirSync(inDir);
+            for (const file of files) {
+                if (file.endsWith('.yaml') || file.endsWith('.yml')) {
+                    console.log(`-- Parsing ${file}`)
+                    //检查是否有同名的json文件
+                    const jsonFile = file.replace(/\.(yaml|yml)$/, '.json');
+                    if (files.includes(jsonFile)) {
+                        console.log(`---- File ${jsonFile} already exists, skipping...`);
+                        continue;
+                    }
+                    try {
+                        const filePath = resolve(inDir, file);
+                        const fileContents = fs.readFileSync(filePath, 'utf8');
+                        const parsed = yaml.load(fileContents);
+                        const jsonContent = JSON.stringify(parsed, null, 2);
+                        const outputFilePath = resolve(outDir, file.replace(/\.(yaml|yml)$/, '.json'));
+                        console.log(`---- Writing to ${outputFilePath}`);
+                        fs.writeFileSync(outputFilePath, jsonContent);
+                    } catch (error) {
+                        this.error(`---- Error parsing YAML file ${file}: ${error.message}`);
+                    }
+                }
+            }
+        },
+    };
+}

From a50c61efafbdfd35c3719430056ec6d2c5ec1ed2 Mon Sep 17 00:00:00 2001
From: frostime <frostime@foxmail.com>
Date: Fri, 5 Apr 2024 22:42:00 +0800
Subject: [PATCH 35/82] =?UTF-8?q?=F0=9F=94=A5=20rm:=20en=5FUS.yaml?=
MIME-Version: 1.0
Content-Type: text/plain; charset=UTF-8
Content-Transfer-Encoding: 8bit

---
 public/i18n/en_US.yaml | 0
 1 file changed, 0 insertions(+), 0 deletions(-)
 delete mode 100644 public/i18n/en_US.yaml

diff --git a/public/i18n/en_US.yaml b/public/i18n/en_US.yaml
deleted file mode 100644
index e69de29..0000000

From 762ae2197b4d868b751c7f4ba3ed519e48baa888 Mon Sep 17 00:00:00 2001
From: frostime <frostime@foxmail.com>
Date: Fri, 5 Apr 2024 22:54:18 +0800
Subject: [PATCH 36/82] =?UTF-8?q?=F0=9F=94=A7=20build:=20yaml-plugin?=
MIME-Version: 1.0
Content-Type: text/plain; charset=UTF-8
Content-Transfer-Encoding: 8bit

---
 yaml-plugin.js | 11 +----------
 1 file changed, 1 insertion(+), 10 deletions(-)

diff --git a/yaml-plugin.js b/yaml-plugin.js
index 48c88f1..01c85e2 100644
--- a/yaml-plugin.js
+++ b/yaml-plugin.js
@@ -3,7 +3,7 @@
  * @Author       : frostime
  * @Date         : 2024-04-05 21:27:55
  * @FilePath     : /yaml-plugin.js
- * @LastEditTime : 2024-04-05 22:37:40
+ * @LastEditTime : 2024-04-05 22:53:34
  * @Description  : 去妮玛的 json 格式,我就是要用 yaml 写 i18n
  */
 // plugins/vite-plugin-parse-yaml.js
@@ -31,15 +31,6 @@ export default function vitePluginYamlI18n(options = {}) {
                 fs.mkdirSync(outDir, { recursive: true });
             }
 
-            //Remove yaml under outDir, which is auto-moved by vite
-            const outFiles = fs.readdirSync(outDir);
-            for (const file of outFiles) {
-                if (file.endsWith('.yaml') || file.endsWith('.yml')) {
-                    console.log(`-- Remove yaml file ${file} under dist`);
-                    fs.unlinkSync(resolve(outDir, file));
-                }
-            }
-
             //Parse yaml file, output to json
             const files = fs.readdirSync(inDir);
             for (const file of files) {

From 44d61785cf8dc9b8d42af3aa698ce7751b131965 Mon Sep 17 00:00:00 2001
From: =?UTF-8?q?=E6=B5=94=E9=98=B3=E9=99=8C=E5=AE=A2?=
 <virtualtengfei@163.com>
Date: Sat, 6 Apr 2024 01:44:54 +0800
Subject: [PATCH 37/82] =?UTF-8?q?=E6=B7=BB=E5=8A=A0setAndSave=E6=96=B9?=
 =?UTF-8?q?=E6=B3=95=E4=BD=9C=E4=B8=BA=E8=AE=BE=E7=BD=AE=E5=8F=82=E6=95=B0?=
 =?UTF-8?q?=E5=B9=B6=E4=BF=9D=E5=AD=98?=
MIME-Version: 1.0
Content-Type: text/plain; charset=UTF-8
Content-Transfer-Encoding: 8bit

---
 src/libs/setting-utils.ts | 14 ++++++++++++++
 1 file changed, 14 insertions(+)

diff --git a/src/libs/setting-utils.ts b/src/libs/setting-utils.ts
index 17a850b..02053a2 100644
--- a/src/libs/setting-utils.ts
+++ b/src/libs/setting-utils.ts
@@ -80,6 +80,20 @@ export class SettingUtils {
             this.updateElementFromValue(key);
         }
     }
+    /**
+     * Set and save setting item value
+     * If you want to set and save immediately you can use this method
+     * @param key key name
+     * @param value value
+     */
+    async setAndSave(key: string, value: any) {
+        let item = this.settings.get(key);
+        if (item) {
+            item.value = value;
+            this.updateElementFromValue(key);
+            await this.save()
+        }
+    }
 
     /**
      * Disable setting item

From f06c82085ef4f592bfa10af1e5bd2e38740bc375 Mon Sep 17 00:00:00 2001
From: =?UTF-8?q?=E6=B5=94=E9=98=B3=E9=99=8C=E5=AE=A2?=
 <virtualtengfei@163.com>
Date: Sat, 6 Apr 2024 13:24:00 +0800
Subject: [PATCH 38/82] =?UTF-8?q?=E4=B8=BA=E7=BB=8F=E5=85=B8=E8=AE=BE?=
 =?UTF-8?q?=E7=BD=AE=E7=AA=97=E5=8F=A3=E6=B7=BB=E5=8A=A0=E5=AE=9E=E6=97=B6?=
 =?UTF-8?q?=E8=AF=BB=E5=8F=96=E5=92=8C=E5=AE=9E=E6=97=B6=E4=BF=9D=E5=AD=98?=
 =?UTF-8?q?=E7=9A=84=E8=83=BD=E5=8A=9B?=
MIME-Version: 1.0
Content-Type: text/plain; charset=UTF-8
Content-Transfer-Encoding: 8bit

---
 src/index.ts              | 39 ++++++++++++++++++++++++---
 src/libs/index.d.ts       |  2 +-
 src/libs/setting-utils.ts | 55 ++++++++++++++++++++++++++++++++++++---
 3 files changed, 89 insertions(+), 7 deletions(-)

diff --git a/src/index.ts b/src/index.ts
index 0e8893a..a444fb8 100644
--- a/src/index.ts
+++ b/src/index.ts
@@ -177,6 +177,13 @@ export default class PluginSample extends Plugin {
             type: "textinput",
             title: "Readonly text",
             description: "Input description",
+            action: {
+                callback: () => {
+                    // Return data and save it in real time
+                    let value = this.settingUtils.takeAndSave("Input")
+                    console.log(value);
+                }
+            }
         });
         this.settingUtils.addItem({
             key: "InputArea",
@@ -184,6 +191,13 @@ export default class PluginSample extends Plugin {
             type: "textarea",
             title: "Readonly text",
             description: "Input description",
+            action: {
+                callback: () => {
+                    // Read data in real time
+                    let value = this.settingUtils.take("InputArea")
+                    console.log(value);
+                }
+            }
         });
         this.settingUtils.addItem({
             key: "Check",
@@ -191,9 +205,12 @@ export default class PluginSample extends Plugin {
             type: "checkbox",
             title: "Checkbox text",
             description: "Check description",
-            checkbox: {
+            action: {
                 callback: () => {
-                    console.log("Checkbox clicked");
+                    // Return data and save it in real time
+                    let value = !this.settingUtils.get("Check")
+                     this.settingUtils.set("Check", value)
+                    console.log(value);
                 }
             }
         });
@@ -201,11 +218,18 @@ export default class PluginSample extends Plugin {
             key: "Select",
             value: 1,
             type: "select",
-            title: "Readonly text",
+            title: "Select",
             description: "Select description",
             options: {
                 1: "Option 1",
                 2: "Option 2"
+            },
+            action: {
+                callback: () => {
+                    // Read data in real time
+                    let value = this.settingUtils.take("Select")
+                    console.log(value);
+                }
             }
         });
         this.settingUtils.addItem({
@@ -218,6 +242,15 @@ export default class PluginSample extends Plugin {
                 min: 0,
                 max: 100,
                 step: 1,
+            },
+            action:{
+                // The callback is called after the action of Silder changes, 
+                // so it should be the this.settingUtils.get() method.
+                callback: () => {
+                    // Read data in real time
+                    let value = this.settingUtils.get("Slider")
+                    console.log(value);
+                }
             }
         });
         this.settingUtils.addItem({
diff --git a/src/libs/index.d.ts b/src/libs/index.d.ts
index 5181c19..cdb1ea3 100644
--- a/src/libs/index.d.ts
+++ b/src/libs/index.d.ts
@@ -12,7 +12,7 @@ interface ISettingItem {
         step: number;
     };
     options?: { [key: string | number]: string };
-    checkbox?: {
+    action?: {
         callback: () => void;
     }
     button?: {
diff --git a/src/libs/setting-utils.ts b/src/libs/setting-utils.ts
index 02053a2..8d2bc53 100644
--- a/src/libs/setting-utils.ts
+++ b/src/libs/setting-utils.ts
@@ -65,7 +65,7 @@ export class SettingUtils {
     }
 
     /**
-     * Get setting item value
+     * read the data after saving
      * @param key key name
      * @returns setting item value
      */
@@ -73,6 +73,27 @@ export class SettingUtils {
         return this.settings.get(key)?.value;
     }
 
+    /**
+      * Read in real time, 
+      * do not read from the configuration file
+      * @param key key name
+      * @returns value in html 
+      */
+    take(key: string) {
+        let item = this.getElement(key)
+        this.settings.set(key, item)
+        if (item.type === 'button') {
+            return item.value
+        }
+        return item.value
+    }
+
+    /**
+     * Set data to this.settings, 
+     * but do not save it to the configuration file
+     * @param key key name
+     * @param value value
+     */
     set(key: string, value: any) {
         let item = this.settings.get(key);
         if (item) {
@@ -80,6 +101,7 @@ export class SettingUtils {
             this.updateElementFromValue(key);
         }
     }
+
     /**
      * Set and save setting item value
      * If you want to set and save immediately you can use this method
@@ -95,6 +117,18 @@ export class SettingUtils {
         }
     }
 
+    /**
+     * Read data from html and save it
+     * @param key key name
+     * @param value value
+     * @return value in html 
+     */
+    async takeAndSave(key: string) {
+        let value = this.take(key)
+        await this.save()
+        return value
+    }
+
     /**
      * Disable setting item
      * @param key key name
@@ -105,6 +139,7 @@ export class SettingUtils {
             element.disabled = true;
         }
     }
+
     /**
      * Enable setting item
      * @param key key name
@@ -122,6 +157,8 @@ export class SettingUtils {
      */
     dump(): Object {
         let data: any = {};
+
+
         for (let [key, item] of this.settings) {
             if (item.type === 'button') continue;
             data[key] = item.value;
@@ -139,7 +176,7 @@ export class SettingUtils {
                 element.checked = item.value;
                 element.className = "b3-switch fn__flex-center";
                 itemElement = element;
-                element.onchange = item.checkbox?.callback ?? (() => { });
+                element.onchange = item.action?.callback ?? (() => { });
                 break;
             case 'select':
                 let selectElement: HTMLSelectElement = document.createElement('select');
@@ -153,6 +190,7 @@ export class SettingUtils {
                     selectElement.appendChild(optionElement);
                 }
                 selectElement.value = item.value;
+                selectElement.onchange = item.action?.callback ?? (() => { });
                 itemElement = selectElement;
                 break;
             case 'slider':
@@ -166,6 +204,7 @@ export class SettingUtils {
                 sliderElement.value = item.value;
                 sliderElement.onchange = () => {
                     sliderElement.ariaLabel = sliderElement.value;
+                    item.action?.callback();
                 }
                 itemElement = sliderElement;
                 break;
@@ -173,12 +212,15 @@ export class SettingUtils {
                 let textInputElement: HTMLInputElement = document.createElement('input');
                 textInputElement.className = 'b3-text-field fn__flex-center fn__size200';
                 textInputElement.value = item.value;
+                textInputElement.onkeyup = item.action?.callback ?? (() => { });
                 itemElement = textInputElement;
+
                 break;
             case 'textarea':
                 let textareaElement: HTMLTextAreaElement = document.createElement('textarea');
                 textareaElement.className = "b3-text-field fn__block";
                 textareaElement.value = item.value;
+                textareaElement.onkeyup = item.action?.callback ?? (() => { });
                 itemElement = textareaElement;
                 break;
             case 'number':
@@ -192,7 +234,7 @@ export class SettingUtils {
                 let buttonElement: HTMLButtonElement = document.createElement('button');
                 buttonElement.className = "b3-button b3-button--outline fn__flex-center fn__size200";
                 buttonElement.innerText = item.button?.label ?? 'Button';
-                buttonElement.onclick = item.button?.callback ?? (() => {});
+                buttonElement.onclick = item.button?.callback ?? (() => { });
                 itemElement = buttonElement;
                 break;
             case 'hint':
@@ -212,11 +254,18 @@ export class SettingUtils {
         })
     }
 
+    /**
+     * Set the value in the setting to the value of the element 
+     * and return the element information
+     * @param key key name
+     * @returns element
+     */
     getElement(key: string) {
         let item = this.settings.get(key);
         let element = this.elements.get(key) as any;
         switch (item.type) {
             case 'checkbox':
+                element.value = element.checked ? true : false;
                 element.checked = item.value;
                 break;
             case 'select':

From 0e372e90681d0c06226f1c739c8740933926a5a5 Mon Sep 17 00:00:00 2001
From: =?UTF-8?q?=E6=B5=94=E9=98=B3=E9=99=8C=E5=AE=A2?=
 <virtualtengfei@163.com>
Date: Sat, 6 Apr 2024 14:48:25 +0800
Subject: [PATCH 39/82] =?UTF-8?q?=E4=BF=AE=E6=94=B9textinput=E5=92=8Ctexta?=
 =?UTF-8?q?rea=E7=9A=84=E5=9B=9E=E8=B0=83=E5=87=BD=E6=95=B0?=
MIME-Version: 1.0
Content-Type: text/plain; charset=UTF-8
Content-Transfer-Encoding: 8bit

---
 src/index.ts              | 2 ++
 src/libs/setting-utils.ts | 4 ++--
 2 files changed, 4 insertions(+), 2 deletions(-)

diff --git a/src/index.ts b/src/index.ts
index a444fb8..df1131b 100644
--- a/src/index.ts
+++ b/src/index.ts
@@ -178,6 +178,7 @@ export default class PluginSample extends Plugin {
             title: "Readonly text",
             description: "Input description",
             action: {
+                // Called when focus is lost and content changes
                 callback: () => {
                     // Return data and save it in real time
                     let value = this.settingUtils.takeAndSave("Input")
@@ -191,6 +192,7 @@ export default class PluginSample extends Plugin {
             type: "textarea",
             title: "Readonly text",
             description: "Input description",
+            // Called when focus is lost and content changes
             action: {
                 callback: () => {
                     // Read data in real time
diff --git a/src/libs/setting-utils.ts b/src/libs/setting-utils.ts
index 8d2bc53..321b019 100644
--- a/src/libs/setting-utils.ts
+++ b/src/libs/setting-utils.ts
@@ -212,7 +212,7 @@ export class SettingUtils {
                 let textInputElement: HTMLInputElement = document.createElement('input');
                 textInputElement.className = 'b3-text-field fn__flex-center fn__size200';
                 textInputElement.value = item.value;
-                textInputElement.onkeyup = item.action?.callback ?? (() => { });
+                textInputElement.onchange = item.action?.callback ?? (() => { });
                 itemElement = textInputElement;
 
                 break;
@@ -220,7 +220,7 @@ export class SettingUtils {
                 let textareaElement: HTMLTextAreaElement = document.createElement('textarea');
                 textareaElement.className = "b3-text-field fn__block";
                 textareaElement.value = item.value;
-                textareaElement.onkeyup = item.action?.callback ?? (() => { });
+                textareaElement.onchange = item.action?.callback ?? (() => { });
                 itemElement = textareaElement;
                 break;
             case 'number':

From 4286a49a0b852e278506c713dc39ab8daba301af Mon Sep 17 00:00:00 2001
From: frostime <frostime@foxmail.com>
Date: Fri, 19 Apr 2024 18:52:39 +0800
Subject: [PATCH 40/82] =?UTF-8?q?=E2=AC=86=EF=B8=8F=20update=20svelte=20de?=
 =?UTF-8?q?ps?=
MIME-Version: 1.0
Content-Type: text/plain; charset=UTF-8
Content-Transfer-Encoding: 8bit

---
 package.json               | 12 ++++++------
 src/setting-example.svelte |  1 +
 2 files changed, 7 insertions(+), 6 deletions(-)

diff --git a/package.json b/package.json
index fdcce30..df94e90 100644
--- a/package.json
+++ b/package.json
@@ -11,10 +11,10 @@
     "make-link": "node  --no-warnings ./scripts/make_dev_link.js",
     "dev": "vite build --watch",
     "build": "vite build",
-    "install": "vite build && node --no-warnings ./scripts/make_install.js"
+    "make-install": "vite build && node --no-warnings ./scripts/make_install.js"
   },
   "devDependencies": {
-    "@sveltejs/vite-plugin-svelte": "^2.4.1",
+    "@sveltejs/vite-plugin-svelte": "^3.0.0",
     "@tsconfig/svelte": "^4.0.1",
     "@types/node": "^20.3.0",
     "eslint": "^8.42.0",
@@ -24,12 +24,12 @@
     "minimist": "^1.2.8",
     "rollup-plugin-livereload": "^2.0.5",
     "sass": "^1.63.3",
-    "siyuan": "0.9.4",
-    "svelte": "^3.59.1",
+    "siyuan": "0.9.7",
+    "svelte": "^4.2.0",
     "ts-node": "^10.9.1",
     "typescript": "^5.1.3",
-    "vite": "^4.5.2",
-    "vite-plugin-static-copy": "^0.15.0",
+    "vite": "^5.0.0",
+    "vite-plugin-static-copy": "^1.0.2",
     "vite-plugin-zip-pack": "^1.0.5"
   }
 }
diff --git a/src/setting-example.svelte b/src/setting-example.svelte
index 3ae156a..5f8958b 100644
--- a/src/setting-example.svelte
+++ b/src/setting-example.svelte
@@ -63,6 +63,7 @@
 <div class="fn__flex-1 fn__flex config__panel">
     <ul class="b3-tab-bar b3-list b3-list--background">
         {#each groups as group}
+            <!-- svelte-ignore a11y-no-noninteractive-element-interactions -->
             <li
                 data-name="editor"
                 class:b3-list-item--focus={group === focusGroup}

From adf7d86dcd2ee098155449b00e2ea41a8a8b545f Mon Sep 17 00:00:00 2001
From: frostime <frostime@foxmail.com>
Date: Fri, 19 Apr 2024 19:09:15 +0800
Subject: [PATCH 41/82] =?UTF-8?q?=F0=9F=94=A7=20config=20svelte?=
MIME-Version: 1.0
Content-Type: text/plain; charset=UTF-8
Content-Transfer-Encoding: 8bit

---
 svelte.config.js | 19 +++++++++++++++++++
 1 file changed, 19 insertions(+)

diff --git a/svelte.config.js b/svelte.config.js
index 7c8df62..d62a343 100644
--- a/svelte.config.js
+++ b/svelte.config.js
@@ -1,7 +1,26 @@
+/*
+ * Copyright (c) 2024 by frostime. All Rights Reserved.
+ * @Author       : frostime
+ * @Date         : 2023-05-19 19:49:13
+ * @FilePath     : /svelte.config.js
+ * @LastEditTime : 2024-04-19 19:01:55
+ * @Description  : 
+ */
 import { vitePreprocess } from "@sveltejs/vite-plugin-svelte"
 
+const NoWarns = new Set([
+    "a11y-click-events-have-key-events",
+    "a11y-no-static-element-interactions",
+    "a11y-no-noninteractive-element-interactions"
+]);
+
 export default {
     // Consult https://svelte.dev/docs#compile-time-svelte-preprocess
     // for more information about preprocessors
     preprocess: vitePreprocess(),
+    onwarn: (warning, handler) => {
+        // suppress warnings on `vite dev` and `vite build`; but even without this, things still work
+        if (NoWarns.has(warning.code)) return;
+        handler(warning);
+    }
 }

From 3b47436806f5ed0074674d0ab2a8ad5bf8c72910 Mon Sep 17 00:00:00 2001
From: frostime <frostime@foxmail.com>
Date: Fri, 19 Apr 2024 19:17:49 +0800
Subject: [PATCH 42/82] =?UTF-8?q?=F0=9F=93=9D=20readme?=
MIME-Version: 1.0
Content-Type: text/plain; charset=UTF-8
Content-Transfer-Encoding: 8bit

---
 README.md       | 37 +++----------------------------------
 README_zh_CN.md | 40 ++++------------------------------------
 2 files changed, 7 insertions(+), 70 deletions(-)

diff --git a/README.md b/README.md
index 3a6e717..3c233a7 100644
--- a/README.md
+++ b/README.md
@@ -249,43 +249,12 @@ Related APIs can be found at: `/api/file/*` (e.g., `/api/file/getFile`).
 
 ### 2. Daily Note Attribute Specifications
 
-When creating a diary in SiYuan, a custom-dailynote-yyyymmdd attribute will be automatically added to the document to distinguish it from regular documents.
+When creating a daily note in SiYuan, a custom-dailynote-yyyymmdd attribute will be automatically added to the document to distinguish it from regular documents.
 
 > For more details, please refer to [Github Issue #9807](https://github.com/siyuan-note/siyuan/issues/9807).
 
 Developers should pay attention to the following when developing the functionality to manually create Daily Notes:
 
-- If `/api/filetree/createDailyNote` is called to create a diary, the attribute will be automatically added to the document, and developers do not need to handle it separately.
-- If a document is created manually by developer's code (e.g., using the `createDocWithMd` API to create a diary), please manually add this attribute to the document.
+* If `/api/filetree/createDailyNote` is called to create a daily note, the attribute will be automatically added to the document, and developers do not need to handle it separately
+* If a document is created manually by developer's code (e.g., using the `createDocWithMd` API to create a daily note), please manually add this attribute to the document
 
-Here is a reference code:
-
-```ts
-/*
- * Copyright (c) 2023 by frostime. All Rights Reserved.
- * @Author       : frostime
- * @Url          : https://github.com/frostime/siyuan-dailynote-today/blob/v1.3.0/src/func/dailynote/dn-attr.ts
- */
-
-export function formatDate(date?: Date, sep=''): string {
-    date = date === undefined ? new Date() : date;
-    let year = date.getFullYear();
-    let month = date.getMonth() + 1;
-    let day = date.getDate();
-    return `${year}${sep}${month < 10 ? '0' + month : month}${sep}${day < 10 ? '0' + day : day}`;
-}
-
-/**
- * Set custom attribute: `custom-dailynote-yyyyMMdd`: yyyyMMdd
- * https://github.com/siyuan-note/siyuan/issues/9807
- * @param doc_id Id of daily note
- */
-export function setCustomDNAttr(doc_id: string, date?: Date) {
-    let td = formatDate(date);
-    let attr = `custom-dailynote-${td}`;
-    // 构建 attr: td
-    let attrs: { [key: string]: string } = {};
-    attrs[attr] = td;
-    serverApi.setBlockAttrs(doc_id, attrs);
-}
-```
diff --git a/README_zh_CN.md b/README_zh_CN.md
index 6b46f16..1510f9b 100644
--- a/README_zh_CN.md
+++ b/README_zh_CN.md
@@ -237,47 +237,15 @@ PR 社区集市仓库。
 
 插件或者外部扩展如果有直接读取或者写入 data 下文件的需求,请通过调用内核 API 来实现,**不要自行调用 `fs` 或者其他 electron、nodejs API**,否则可能会导致数据同步时分块丢失,造成云端数据损坏。
 
-相关 API 见: `/api/file/*`(例如 `/api/file/getFile` 等)。
+相关 API 见 `/api/file/*`(例如 `/api/file/getFile` 等)。
 
 ### 2. Daily Note 属性规范
 
-思源在创建日记的时候会自动为文档添加 custom-dailynote-yyyymmdd 属性, 以方便将日记文档同普通文档区分。
+思源在创建日记的时候会自动为文档添加 custom-dailynote-yyyymmdd 属性,以方便将日记文档同普通文档区分。
 
 > 详情请见 [Github Issue #9807](https://github.com/siyuan-note/siyuan/issues/9807)。
 
 开发者在开发手动创建 Daily Note 的功能时请注意:
 
-- 如果调用了 `/api/filetree/createDailyNote` 创建日记,那么文档会自动添加这个属性,无需开发者特别处理。
-- 如果是开发者代码手动创建文档(例如使用 `createDocWithMd` API 创建日记),请手动为文档添加该属性。
-
-参考代码:
-
-```ts
-/*
- * Copyright (c) 2023 by frostime. All Rights Reserved.
- * @Author       : frostime
- * @Url          : https://github.com/frostime/siyuan-dailynote-today/blob/v1.3.0/src/func/dailynote/dn-attr.ts
- */
-
-export function formatDate(date?: Date, sep=''): string {
-    date = date === undefined ? new Date() : date;
-    let year = date.getFullYear();
-    let month = date.getMonth() + 1;
-    let day = date.getDate();
-    return `${year}${sep}${month < 10 ? '0' + month : month}${sep}${day < 10 ? '0' + day : day}`;
-}
-
-/**
- * Set custom attribute: `custom-dailynote-yyyyMMdd`: yyyyMMdd
- * https://github.com/siyuan-note/siyuan/issues/9807
- * @param doc_id Id of daily note
- */
-export function setCustomDNAttr(doc_id: string, date?: Date) {
-    let td = formatDate(date);
-    let attr = `custom-dailynote-${td}`;
-    // 构建 attr: td
-    let attrs: { [key: string]: string } = {};
-    attrs[attr] = td;
-    serverApi.setBlockAttrs(doc_id, attrs);
-}
-```
+* 如果调用了 `/api/filetree/createDailyNote` 创建日记,那么文档会自动添加这个属性,无需开发者特别处理
+* 如果是开发者代码手动创建文档(例如使用 `createDocWithMd` API 创建日记),请手动为文档添加该属性

From 786cc5e93311b94440c7277519be090f0369e8ff Mon Sep 17 00:00:00 2001
From: =?UTF-8?q?=E6=B5=94=E9=98=B3=E9=99=8C=E5=AE=A2?=
 <virtualtengfei@163.com>
Date: Wed, 24 Apr 2024 12:17:56 +0800
Subject: [PATCH 43/82] =?UTF-8?q?=E4=BF=AE=E6=94=B9getElement()=E6=96=B9?=
 =?UTF-8?q?=E6=B3=95=E5=92=8C=E6=96=B9=E6=B3=95=E8=AF=B4=E6=98=8E?=
MIME-Version: 1.0
Content-Type: text/plain; charset=UTF-8
Content-Transfer-Encoding: 8bit

---
 src/libs/setting-utils.ts | 15 +++++++--------
 1 file changed, 7 insertions(+), 8 deletions(-)

diff --git a/src/libs/setting-utils.ts b/src/libs/setting-utils.ts
index 321b019..a6fd592 100644
--- a/src/libs/setting-utils.ts
+++ b/src/libs/setting-utils.ts
@@ -255,8 +255,7 @@ export class SettingUtils {
     }
 
     /**
-     * Set the value in the setting to the value of the element 
-     * and return the element information
+     * return the element information
      * @param key key name
      * @returns element
      */
@@ -265,21 +264,21 @@ export class SettingUtils {
         let element = this.elements.get(key) as any;
         switch (item.type) {
             case 'checkbox':
-                element.value = element.checked ? true : false;
-                element.checked = item.value;
+                item.value = element.checked ? true : false;
+                element.checked = element.value;
                 break;
             case 'select':
-                element.value = item.value;
+                item.value = element.value;
                 break;
             case 'slider':
-                element.value = item.value;
+                item.value = element.value;
                 element.ariaLabel = item.value;
                 break;
             case 'textinput':
-                element.value = item.value;
+                item.value = element.value;
                 break;
             case 'textarea':
-                element.value = item.value;
+                item.value = element.value;
                 break;
         }
         return element;

From 8f27804c8397a541acb69c15b909327b1e3b0973 Mon Sep 17 00:00:00 2001
From: =?UTF-8?q?=E6=B5=94=E9=98=B3=E9=99=8C=E5=AE=A2?=
 <virtualtengfei@163.com>
Date: Wed, 24 Apr 2024 14:23:51 +0800
Subject: [PATCH 44/82] =?UTF-8?q?=E5=AE=8C=E5=96=84=E4=BF=AE=E6=94=B9?=
MIME-Version: 1.0
Content-Type: text/plain; charset=UTF-8
Content-Transfer-Encoding: 8bit

---
 src/libs/setting-utils.ts | 2 --
 1 file changed, 2 deletions(-)

diff --git a/src/libs/setting-utils.ts b/src/libs/setting-utils.ts
index a6fd592..8ef8074 100644
--- a/src/libs/setting-utils.ts
+++ b/src/libs/setting-utils.ts
@@ -265,14 +265,12 @@ export class SettingUtils {
         switch (item.type) {
             case 'checkbox':
                 item.value = element.checked ? true : false;
-                element.checked = element.value;
                 break;
             case 'select':
                 item.value = element.value;
                 break;
             case 'slider':
                 item.value = element.value;
-                element.ariaLabel = item.value;
                 break;
             case 'textinput':
                 item.value = element.value;

From 587d5f21125c213d726af5be03d3ded7682002a8 Mon Sep 17 00:00:00 2001
From: =?UTF-8?q?=E6=B5=94=E9=98=B3=E9=99=8C=E5=AE=A2?=
 <virtualtengfei@163.com>
Date: Wed, 24 Apr 2024 14:46:41 +0800
Subject: [PATCH 45/82] =?UTF-8?q?=E4=BF=AE=E5=A4=8D=E5=88=B7=E6=96=B0?=
 =?UTF-8?q?=E9=A1=B5=E9=9D=A2=E5=90=8E,=E6=95=B0=E6=8D=AE=E5=B7=B2?=
 =?UTF-8?q?=E5=8A=A0=E8=BD=BD,=E4=BD=86=E6=B2=A1=E6=9C=89=E6=98=BE?=
 =?UTF-8?q?=E7=A4=BA=E5=88=B0=E8=AE=BE=E7=BD=AE=E9=A1=B5=E9=9D=A2=E7=9A=84?=
 =?UTF-8?q?=E9=97=AE=E9=A2=98?=
MIME-Version: 1.0
Content-Type: text/plain; charset=UTF-8
Content-Transfer-Encoding: 8bit

---
 src/libs/setting-utils.ts | 2 ++
 1 file changed, 2 insertions(+)

diff --git a/src/libs/setting-utils.ts b/src/libs/setting-utils.ts
index 8ef8074..dd16ca5 100644
--- a/src/libs/setting-utils.ts
+++ b/src/libs/setting-utils.ts
@@ -51,6 +51,8 @@ export class SettingUtils {
         if (data) {
             for (let [key, item] of this.settings) {
                 item.value = data?.[key] ?? item.value;
+                this.updateElementFromValue(key);
+
             }
         }
         this.plugin.data[this.name] = this.dump();

From 7a46168ed264057bf1ae9a0fd4fe867a2201e384 Mon Sep 17 00:00:00 2001
From: =?UTF-8?q?=E6=B5=94=E9=98=B3=E9=99=8C=E5=AE=A2?=
 <virtualtengfei@163.com>
Date: Thu, 25 Apr 2024 21:16:28 +0800
Subject: [PATCH 46/82] =?UTF-8?q?=E4=BF=AE=E6=94=B9take()=E7=9A=84?=
 =?UTF-8?q?=E8=8E=B7=E5=8F=96=E5=80=BC=E6=96=B9=E6=B3=95?=
MIME-Version: 1.0
Content-Type: text/plain; charset=UTF-8
Content-Transfer-Encoding: 8bit

---
 src/index.ts              |  4 +---
 src/libs/setting-utils.ts | 29 +++++++++++++++--------------
 2 files changed, 16 insertions(+), 17 deletions(-)

diff --git a/src/index.ts b/src/index.ts
index df1131b..1899daa 100644
--- a/src/index.ts
+++ b/src/index.ts
@@ -246,11 +246,9 @@ export default class PluginSample extends Plugin {
                 step: 1,
             },
             action:{
-                // The callback is called after the action of Silder changes, 
-                // so it should be the this.settingUtils.get() method.
                 callback: () => {
                     // Read data in real time
-                    let value = this.settingUtils.get("Slider")
+                    let value = this.settingUtils.take("Slider")
                     console.log(value);
                 }
             }
diff --git a/src/libs/setting-utils.ts b/src/libs/setting-utils.ts
index dd16ca5..e0ff2bc 100644
--- a/src/libs/setting-utils.ts
+++ b/src/libs/setting-utils.ts
@@ -51,8 +51,6 @@ export class SettingUtils {
         if (data) {
             for (let [key, item] of this.settings) {
                 item.value = data?.[key] ?? item.value;
-                this.updateElementFromValue(key);
-
             }
         }
         this.plugin.data[this.name] = this.dump();
@@ -82,12 +80,12 @@ export class SettingUtils {
       * @returns value in html 
       */
     take(key: string) {
-        let item = this.getElement(key)
-        this.settings.set(key, item)
-        if (item.type === 'button') {
-            return item.value
+        let element = this.elements.get(key) as any;
+        if (!element){
+            return
         }
-        return item.value
+        this.settings.set(key, element.value)
+        return element.value 
     }
 
     /**
@@ -257,7 +255,8 @@ export class SettingUtils {
     }
 
     /**
-     * return the element information
+     * Set the value in the setting to the value of the element 
+     * and return the element information
      * @param key key name
      * @returns element
      */
@@ -266,19 +265,21 @@ export class SettingUtils {
         let element = this.elements.get(key) as any;
         switch (item.type) {
             case 'checkbox':
-                item.value = element.checked ? true : false;
+                element.value = element.checked ? true : false;
+                element.checked = item.value;
                 break;
             case 'select':
-                item.value = element.value;
+                element.value = item.value;
                 break;
             case 'slider':
-                item.value = element.value;
+                element.value = item.value;
+                element.ariaLabel = item.value;
                 break;
             case 'textinput':
-                item.value = element.value;
+                element.value = item.value;
                 break;
             case 'textarea':
-                item.value = element.value;
+                element.value = item.value;
                 break;
         }
         return element;
@@ -328,5 +329,5 @@ export class SettingUtils {
                 break;
         }
     }
-
+ 
 }
\ No newline at end of file

From 8daa01aedb3d672ca38848f2581959e78f5a11bd Mon Sep 17 00:00:00 2001
From: frostime <frostime@foxmail.com>
Date: Sat, 27 Apr 2024 16:55:46 +0800
Subject: [PATCH 47/82] =?UTF-8?q?=F0=9F=90=9B=20fix:=20=E6=A8=A1=E6=9D=BF?=
 =?UTF-8?q?=E6=8F=90=E4=BE=9B=E7=9A=84=20button=20=E7=9A=84=20click=20?=
 =?UTF-8?q?=E4=BA=8B=E4=BB=B6=E4=B8=8D=E7=94=9F=E6=95=88;=20close=20#31?=
MIME-Version: 1.0
Content-Type: text/plain; charset=UTF-8
Content-Transfer-Encoding: 8bit

---
 src/index.ts                  |  2 +-
 src/libs/setting-item.svelte  | 13 +++++++++----
 src/libs/setting-panel.svelte |  3 ++-
 src/libs/setting-utils.ts     |  3 +--
 src/setting-example.svelte    | 17 +++++++++++++++++
 5 files changed, 30 insertions(+), 8 deletions(-)

diff --git a/src/index.ts b/src/index.ts
index 1899daa..9a47d54 100644
--- a/src/index.ts
+++ b/src/index.ts
@@ -382,7 +382,7 @@ export default class PluginSample extends Plugin {
         let dialog = new Dialog({
             title: "SettingPannel",
             content: `<div id="SettingPanel" style="height: 100%;"></div>`,
-            width: "600px",
+            width: "800px",
             destroyCallback: (options) => {
                 console.log("destroyCallback", options);
                 //You'd better destroy the component when the dialog is closed
diff --git a/src/libs/setting-item.svelte b/src/libs/setting-item.svelte
index d59912b..ef1a964 100644
--- a/src/libs/setting-item.svelte
+++ b/src/libs/setting-item.svelte
@@ -14,11 +14,16 @@
         max: number;
         step: number;
     } = { min: 0, max: 100, step: 1 }; // Use it if type is slider
+    export let button: {
+        label: string;
+        callback: () => void;
+    } = { label: settingValue, callback: () => {} }; // Use it if type is button
 
     const dispatch = createEventDispatcher();
 
-    function clicked() {
-        dispatch("clicked");
+    function click() {
+        button?.callback();
+        dispatch("click", { key: settingKey });
     }
 
     function changed() {
@@ -66,9 +71,9 @@
         <button
             class="b3-button b3-button--outline fn__flex-center fn__size200"
             id={settingKey}
-            on:click={clicked}
+            on:click={click}
         >
-            {settingValue}
+            {button.label}
         </button>
     {:else if type === "select"}
         <!-- Dropdown select -->
diff --git a/src/libs/setting-panel.svelte b/src/libs/setting-panel.svelte
index fc5e508..038d890 100644
--- a/src/libs/setting-panel.svelte
+++ b/src/libs/setting-panel.svelte
@@ -3,7 +3,7 @@
  Author       : frostime
  Date         : 2023-07-01 19:23:50
  FilePath     : /src/libs/setting-panel.svelte
- LastEditTime : 2023-11-28 21:45:10
+ LastEditTime : 2024-04-27 16:46:49
  Description  : 
 -->
 <script lang="ts">
@@ -39,6 +39,7 @@
             placeholder={item?.placeholder}
             options={item?.options}
             slider={item?.slider}
+            button={item?.button}
             on:click={onClick}
             on:changed={onChanged}
         />
diff --git a/src/libs/setting-utils.ts b/src/libs/setting-utils.ts
index e0ff2bc..ddb576c 100644
--- a/src/libs/setting-utils.ts
+++ b/src/libs/setting-utils.ts
@@ -3,7 +3,7 @@
  * @Author       : frostime
  * @Date         : 2023-12-17 18:28:19
  * @FilePath     : /src/libs/setting-utils.ts
- * @LastEditTime : 2024-01-15 20:45:16
+ * @LastEditTime : 2024-04-27 16:38:09
  * @Description  : 
  */
 
@@ -329,5 +329,4 @@ export class SettingUtils {
                 break;
         }
     }
- 
 }
\ No newline at end of file
diff --git a/src/setting-example.svelte b/src/setting-example.svelte
index 5f8958b..4bafee5 100644
--- a/src/setting-example.svelte
+++ b/src/setting-example.svelte
@@ -1,4 +1,5 @@
 <script lang="ts">
+    import { showMessage } from "siyuan";
     import SettingPanel from "./libs/setting-panel.svelte";
 
     let groups: string[] = ["🌈 Default"];
@@ -43,6 +44,19 @@
                 max: 100,
                 step: 1
             }
+        },
+        {
+            type: 'button',
+            title: 'button',
+            description: 'This is a button',
+            key: 'e',
+            value: 'Click Button',
+            button: {
+                label: 'Click Me',
+                callback: () => {
+                    showMessage('Hello, world!');
+                }
+            }
         }
     ];
 
@@ -56,6 +70,8 @@
     const onChanged = ({ detail }: CustomEvent<ChangeEvent>) => {
         if (detail.group === groups[0]) {
             // setting.set(detail.key, detail.value);
+            //Please add your code here
+            //Udpate the plugins setting data, don't forget to call plugin.save() for data persistence
         }
     };
 </script>
@@ -83,6 +99,7 @@
             settingItems={SettingItems}
             display={focusGroup === groups[0]}
             on:changed={onChanged}
+            on:click={({ detail }) => { console.debug("Click:", detail.key); }}
         >
             <div class="fn__flex b3-label">
                 💡 This is our default settings.

From 56be185458ea2c96ca276305e3c6b687c4d4f1e6 Mon Sep 17 00:00:00 2001
From: frostime <frostime@foxmail.com>
Date: Sun, 28 Apr 2024 17:48:49 +0800
Subject: [PATCH 48/82] =?UTF-8?q?=E2=9C=A8=20misc(setting-utils)?=
MIME-Version: 1.0
Content-Type: text/plain; charset=UTF-8
Content-Transfer-Encoding: 8bit

---
 src/index.ts              |  14 ++---
 src/libs/setting-utils.ts | 114 ++++++++++++++++++++++----------------
 2 files changed, 74 insertions(+), 54 deletions(-)

diff --git a/src/index.ts b/src/index.ts
index 9a47d54..49cc9c4 100644
--- a/src/index.ts
+++ b/src/index.ts
@@ -164,13 +164,6 @@ export default class PluginSample extends Plugin {
         });
 
         this.settingUtils = new SettingUtils(this, STORAGE_NAME);
-
-        try {
-            this.settingUtils.load();
-        } catch (error) {
-            console.error("Error loading settings storage, probably empty config json:", error);
-        }
-
         this.settingUtils.addItem({
             key: "Input",
             value: "",
@@ -274,6 +267,13 @@ export default class PluginSample extends Plugin {
             description: this.i18n.hintDesc,
         });
 
+        try {
+            this.settingUtils.load();
+        } catch (error) {
+            console.error("Error loading settings storage, probably empty config json:", error);
+        }
+
+
         this.protyleSlash = [{
             filter: ["insert emoji 😊", "插入表情 😊", "crbqwx"],
             html: `<div class="b3-list-item__first"><span class="b3-list-item__text">${this.i18n.insertEmoji}</span><span class="b3-list-item__meta">😊</span></div>`,
diff --git a/src/libs/setting-utils.ts b/src/libs/setting-utils.ts
index ddb576c..c5bff05 100644
--- a/src/libs/setting-utils.ts
+++ b/src/libs/setting-utils.ts
@@ -3,12 +3,44 @@
  * @Author       : frostime
  * @Date         : 2023-12-17 18:28:19
  * @FilePath     : /src/libs/setting-utils.ts
- * @LastEditTime : 2024-04-27 16:38:09
+ * @LastEditTime : 2024-04-28 17:43:43
  * @Description  : 
  */
 
 import { Plugin, Setting } from 'siyuan';
 
+const valueOf = (ele: HTMLElement) => {
+    if (ele instanceof HTMLInputElement) {
+        if (ele.type === 'checkbox') {
+            return ele.checked;
+        } else {
+            return ele.value;
+        }
+    } else if (ele instanceof HTMLSelectElement) {
+        return ele.value;
+    } else if (ele instanceof HTMLTextAreaElement) {
+        return ele.value;
+    } else {
+        return ele.textContent;
+    }
+}
+
+const setValue = (ele: HTMLElement, value: any) => {
+    if (ele instanceof HTMLInputElement) {
+        if (ele.type === 'checkbox') {
+            ele.checked = value;
+        } else {
+            ele.value = value;
+        }
+    } else if (ele instanceof HTMLSelectElement) {
+        ele.value = value;
+    } else if (ele instanceof HTMLTextAreaElement) {
+        ele.value = value;
+    } else {
+        ele.textContent = value;
+    }
+}
+
 export class SettingUtils {
     plugin: Plugin;
     name: string;
@@ -31,10 +63,9 @@ export class SettingUtils {
                 let data = this.dump();
                 if (callback !== undefined) {
                     callback(data);
-                } else {
-                    this.plugin.data[this.name] = data;
-                    this.save();
                 }
+                this.plugin.data[this.name] = data;
+                this.save();
             },
             destroyCallback: () => {
                 //从值恢复元素
@@ -73,21 +104,6 @@ export class SettingUtils {
         return this.settings.get(key)?.value;
     }
 
-    /**
-      * Read in real time, 
-      * do not read from the configuration file
-      * @param key key name
-      * @returns value in html 
-      */
-    take(key: string) {
-        let element = this.elements.get(key) as any;
-        if (!element){
-            return
-        }
-        this.settings.set(key, element.value)
-        return element.value 
-    }
-
     /**
      * Set data to this.settings, 
      * but do not save it to the configuration file
@@ -113,20 +129,38 @@ export class SettingUtils {
         if (item) {
             item.value = value;
             this.updateElementFromValue(key);
-            await this.save()
+            await this.save();
         }
     }
 
+    /**
+      * Read in the value of element instead of setting obj in real time
+      * @param key key name
+      * @param apply whether to apply the value to the setting object
+      *        if true, the value will be applied to the setting object
+      * @returns value in html
+      */
+    take(key: string, apply: boolean = false) {
+        let element = this.elements.get(key) as any;
+        if (!element){
+            return
+        }
+        if (apply) {
+            this.updateValueFromElement(key);
+        }
+        return valueOf(element)
+    }
+
     /**
      * Read data from html and save it
      * @param key key name
      * @param value value
-     * @return value in html 
+     * @return value in html
      */
     async takeAndSave(key: string) {
-        let value = this.take(key)
-        await this.save()
-        return value
+        let value = this.take(key, true);
+        await this.save();
+        return value;
     }
 
     /**
@@ -248,6 +282,7 @@ export class SettingUtils {
             title: item.title,
             description: item?.description,
             createActionElement: () => {
+                this.updateElementFromValue(item.key);
                 let element = this.getElement(item.key);
                 return element;
             }
@@ -255,40 +290,19 @@ export class SettingUtils {
     }
 
     /**
-     * Set the value in the setting to the value of the element 
-     * and return the element information
+     * return the setting element
      * @param key key name
      * @returns element
      */
     getElement(key: string) {
-        let item = this.settings.get(key);
+        // let item = this.settings.get(key);
         let element = this.elements.get(key) as any;
-        switch (item.type) {
-            case 'checkbox':
-                element.value = element.checked ? true : false;
-                element.checked = item.value;
-                break;
-            case 'select':
-                element.value = item.value;
-                break;
-            case 'slider':
-                element.value = item.value;
-                element.ariaLabel = item.value;
-                break;
-            case 'textinput':
-                element.value = item.value;
-                break;
-            case 'textarea':
-                element.value = item.value;
-                break;
-        }
         return element;
     }
 
     private updateValueFromElement(key: string) {
         let item = this.settings.get(key);
         let element = this.elements.get(key) as any;
-        // console.debug(element, element?.value);
         switch (item.type) {
             case 'checkbox':
                 item.value = element.checked;
@@ -305,6 +319,9 @@ export class SettingUtils {
             case 'textarea':
                 item.value = element.value;
                 break;
+            case 'number':
+                item.value = parseInt(element.value);
+                break;
         }
     }
 
@@ -327,6 +344,9 @@ export class SettingUtils {
             case 'textarea':
                 element.value = item.value;
                 break;
+            case 'number':
+                element.value = item.value;
+                break;
         }
     }
 }
\ No newline at end of file

From 57cc62f7b516a9331ad1cfaeb7f5fce3f163ead5 Mon Sep 17 00:00:00 2001
From: frostime <frostime@foxmail.com>
Date: Sun, 28 Apr 2024 17:50:52 +0800
Subject: [PATCH 49/82] =?UTF-8?q?=F0=9F=8E=A8=20refactor:=20setting-utils?=
 =?UTF-8?q?=20args?=
MIME-Version: 1.0
Content-Type: text/plain; charset=UTF-8
Content-Transfer-Encoding: 8bit

---
 src/index.ts              |  4 +++-
 src/libs/setting-utils.ts | 22 ++++++++++++++--------
 2 files changed, 17 insertions(+), 9 deletions(-)

diff --git a/src/index.ts b/src/index.ts
index 49cc9c4..704ad00 100644
--- a/src/index.ts
+++ b/src/index.ts
@@ -163,7 +163,9 @@ export default class PluginSample extends Plugin {
             }
         });
 
-        this.settingUtils = new SettingUtils(this, STORAGE_NAME);
+        this.settingUtils = new SettingUtils({
+            plugin: this, name: STORAGE_NAME
+        });
         this.settingUtils.addItem({
             key: "Input",
             value: "",
diff --git a/src/libs/setting-utils.ts b/src/libs/setting-utils.ts
index c5bff05..c839d1a 100644
--- a/src/libs/setting-utils.ts
+++ b/src/libs/setting-utils.ts
@@ -3,7 +3,7 @@
  * @Author       : frostime
  * @Date         : 2023-12-17 18:28:19
  * @FilePath     : /src/libs/setting-utils.ts
- * @LastEditTime : 2024-04-28 17:43:43
+ * @LastEditTime : 2024-04-28 17:49:31
  * @Description  : 
  */
 
@@ -49,20 +49,26 @@ export class SettingUtils {
     settings: Map<string, ISettingItem> = new Map();
     elements: Map<string, HTMLElement> = new Map();
 
-    constructor(plugin: Plugin, name?: string, callback?: (data: any) => void, width?: string, height?: string) {
-        this.name = name ?? 'settings';
-        this.plugin = plugin;
+    constructor(args: {
+        plugin: Plugin,
+        name?: string,
+        callback?: (data: any) => void,
+        width?: string,
+        height?: string
+    }) {
+        this.name = args.name ?? 'settings';
+        this.plugin = args.plugin;
         this.file = this.name.endsWith('.json') ? this.name : `${this.name}.json`;
         this.plugin.setting = new Setting({
-            width: width,
-            height: height,
+            width: args.width,
+            height: args.height,
             confirmCallback: () => {
                 for (let key of this.settings.keys()) {
                     this.updateValueFromElement(key);
                 }
                 let data = this.dump();
-                if (callback !== undefined) {
-                    callback(data);
+                if (args.callback !== undefined) {
+                    args.callback(data);
                 }
                 this.plugin.data[this.name] = data;
                 this.save();

From 0df9ec29ea61f34655028b1f6ddaa331e1f8c3d0 Mon Sep 17 00:00:00 2001
From: frostime <frostime@foxmail.com>
Date: Mon, 29 Apr 2024 17:43:02 +0800
Subject: [PATCH 50/82] =?UTF-8?q?=E2=9C=A8=20feat(setting-utils):=20?=
 =?UTF-8?q?=E5=A2=9E=E5=8A=A0=E8=87=AA=E5=AE=9A=E4=B9=89=E5=85=83=E7=B4=A0?=
 =?UTF-8?q?=E5=8A=9F=E8=83=BD?=
MIME-Version: 1.0
Content-Type: text/plain; charset=UTF-8
Content-Transfer-Encoding: 8bit

---
 src/libs/index.d.ts       |   1 +
 src/libs/setting-utils.ts | 148 ++++++++++++++++++++++----------------
 2 files changed, 89 insertions(+), 60 deletions(-)

diff --git a/src/libs/index.d.ts b/src/libs/index.d.ts
index cdb1ea3..9e0b889 100644
--- a/src/libs/index.d.ts
+++ b/src/libs/index.d.ts
@@ -19,4 +19,5 @@ interface ISettingItem {
         label: string;
         callback: () => void;
     }
+    createElement?: (currentVal: any) => HTMLElement;
 }
diff --git a/src/libs/setting-utils.ts b/src/libs/setting-utils.ts
index c839d1a..f43ab36 100644
--- a/src/libs/setting-utils.ts
+++ b/src/libs/setting-utils.ts
@@ -3,35 +3,45 @@
  * @Author       : frostime
  * @Date         : 2023-12-17 18:28:19
  * @FilePath     : /src/libs/setting-utils.ts
- * @LastEditTime : 2024-04-28 17:49:31
+ * @LastEditTime : 2024-04-29 17:17:19
  * @Description  : 
  */
 
 import { Plugin, Setting } from 'siyuan';
 
-const valueOf = (ele: HTMLElement) => {
+const valueOf = (ele: HTMLElement, parseNumber: Function=parseInt) => {
+    let val: any = null;
     if (ele instanceof HTMLInputElement) {
         if (ele.type === 'checkbox') {
-            return ele.checked;
+            val = ele.checked;
         } else {
-            return ele.value;
+            if (ele.type === 'number') {
+                val = parseNumber(ele.value);
+            } else {
+                val = ele.value;
+            }
         }
     } else if (ele instanceof HTMLSelectElement) {
-        return ele.value;
+        val = ele.value;
     } else if (ele instanceof HTMLTextAreaElement) {
-        return ele.value;
+        val = ele.value;
     } else {
-        return ele.textContent;
+        val = ele?.textContent;
     }
+    return val;
 }
 
 const setValue = (ele: HTMLElement, value: any) => {
+    if (ele === null || ele === undefined) return;
     if (ele instanceof HTMLInputElement) {
         if (ele.type === 'checkbox') {
             ele.checked = value;
         } else {
             ele.value = value;
         }
+        if (ele.type === 'range') {
+            ele.ariaLabel = value;
+        }
     } else if (ele instanceof HTMLSelectElement) {
         ele.value = value;
     } else if (ele instanceof HTMLTextAreaElement) {
@@ -74,7 +84,7 @@ export class SettingUtils {
                 this.save();
             },
             destroyCallback: () => {
-                //从值恢复元素
+                //Restore the original value
                 for (let key of this.settings.keys()) {
                     this.updateElementFromValue(key);
                 }
@@ -197,8 +207,6 @@ export class SettingUtils {
      */
     dump(): Object {
         let data: any = {};
-
-
         for (let [key, item] of this.settings) {
             if (item.type === 'button') continue;
             data[key] = item.value;
@@ -208,6 +216,33 @@ export class SettingUtils {
 
     addItem(item: ISettingItem) {
         this.settings.set(item.key, item);
+        if (item.createElement === undefined) {
+            let itemElement = this.createDefaultElement(item);
+            this.elements.set(item.key, itemElement);
+            this.plugin.setting.addItem({
+                title: item.title,
+                description: item?.description,
+                createActionElement: () => {
+                    this.updateElementFromValue(item.key);
+                    let element = this.getElement(item.key);
+                    return element;
+                }
+            });
+        } else {
+            this.plugin.setting.addItem({
+                title: item.title,
+                description: item?.description,
+                createActionElement: () => {
+                    let val = this.get(item.key);
+                    let element = item.createElement(val);
+                    this.elements.set(item.key, element);
+                    return element;
+                }
+            });
+        }
+    }
+
+    createDefaultElement(item: ISettingItem) {
         let itemElement: HTMLElement;
         switch (item.type) {
             case 'checkbox':
@@ -283,16 +318,7 @@ export class SettingUtils {
                 itemElement = hintElement;
                 break;
         }
-        this.elements.set(item.key, itemElement);
-        this.plugin.setting.addItem({
-            title: item.title,
-            description: item?.description,
-            createActionElement: () => {
-                this.updateElementFromValue(item.key);
-                let element = this.getElement(item.key);
-                return element;
-            }
-        })
+        return itemElement;
     }
 
     /**
@@ -309,50 +335,52 @@ export class SettingUtils {
     private updateValueFromElement(key: string) {
         let item = this.settings.get(key);
         let element = this.elements.get(key) as any;
-        switch (item.type) {
-            case 'checkbox':
-                item.value = element.checked;
-                break;
-            case 'select':
-                item.value = element.value;
-                break;
-            case 'slider':
-                item.value = element.value;
-                break;
-            case 'textinput':
-                item.value = element.value;
-                break;
-            case 'textarea':
-                item.value = element.value;
-                break;
-            case 'number':
-                item.value = parseInt(element.value);
-                break;
-        }
+        item.value = valueOf(element);
+        // switch (item.type) {
+        //     case 'checkbox':
+        //         item.value = element.checked;
+        //         break;
+        //     case 'select':
+        //         item.value = element.value;
+        //         break;
+        //     case 'slider':
+        //         item.value = element.value;
+        //         break;
+        //     case 'textinput':
+        //         item.value = element.value;
+        //         break;
+        //     case 'textarea':
+        //         item.value = element.value;
+        //         break;
+        //     case 'number':
+        //         item.value = parseInt(element.value);
+        //         break;
+        // }
     }
 
     private updateElementFromValue(key: string) {
         let item = this.settings.get(key);
         let element = this.elements.get(key) as any;
-        switch (item.type) {
-            case 'checkbox':
-                element.checked = item.value;
-                break;
-            case 'select':
-                element.value = item.value;
-                break;
-            case 'slider':
-                element.value = item.value;
-                break;
-            case 'textinput':
-                element.value = item.value;
-                break;
-            case 'textarea':
-                element.value = item.value;
-                break;
-            case 'number':
-                element.value = item.value;
-                break;
-        }
+        setValue(element, item.value);
+        // switch (item.type) {
+        //     case 'checkbox':
+        //         element.checked = item.value;
+        //         break;
+        //     case 'select':
+        //         element.value = item.value;
+        //         break;
+        //     case 'slider':
+        //         element.value = item.value;
+        //         break;
+        //     case 'textinput':
+        //         element.value = item.value;
+        //         break;
+        //     case 'textarea':
+        //         element.value = item.value;
+        //         break;
+        //     case 'number':
+        //         element.value = item.value;
+        //         break;
+        // }
     }
 }
\ No newline at end of file

From 4a9d0f39531157c69af1c212c2ab8e251bb08d3a Mon Sep 17 00:00:00 2001
From: frostime <frostime@foxmail.com>
Date: Tue, 30 Apr 2024 15:57:43 +0800
Subject: [PATCH 51/82] =?UTF-8?q?=F0=9F=94=A5=20remove=20eslint?=
MIME-Version: 1.0
Content-Type: text/plain; charset=UTF-8
Content-Transfer-Encoding: 8bit

---
 .eslintignore |  2 --
 .eslintrc.cjs | 38 --------------------------------------
 package.json  |  1 -
 3 files changed, 41 deletions(-)
 delete mode 100644 .eslintignore
 delete mode 100644 .eslintrc.cjs

diff --git a/.eslintignore b/.eslintignore
deleted file mode 100644
index f06235c..0000000
--- a/.eslintignore
+++ /dev/null
@@ -1,2 +0,0 @@
-node_modules
-dist
diff --git a/.eslintrc.cjs b/.eslintrc.cjs
deleted file mode 100644
index 0781605..0000000
--- a/.eslintrc.cjs
+++ /dev/null
@@ -1,38 +0,0 @@
-module.exports = {
-  extends: [
-    "eslint:recommended",
-    "plugin:@typescript-eslint/recommended",
-    "plugin:svelte/recommended",
-    "turbo",
-    "prettier",
-  ],
-
-  parser: "@typescript-eslint/parser",
-
-  overrides: [
-    {
-      files: ["*.svelte"],
-      parser: "svelte-eslint-parser",
-      // Parse the script in `.svelte` as TypeScript by adding the following configuration.
-      parserOptions: {
-        parser: "@typescript-eslint/parser",
-      },
-    },
-  ],
-
-  plugins: ["@typescript-eslint", "prettier"],
-
-  rules: {
-    // Note: you must disable the base rule as it can report incorrect errors
-    semi: "off",
-    quotes: "off",
-    "no-undef": "off",
-    "@typescript-eslint/no-var-requires": "off",
-    "@typescript-eslint/no-this-alias": "off",
-    "@typescript-eslint/no-non-null-assertion": "off",
-    "@typescript-eslint/no-unused-vars": "off",
-    "@typescript-eslint/no-explicit-any": "off",
-    "turbo/no-undeclared-env-vars": "off",
-    "prettier/prettier": "error",
-  },
-}
diff --git a/package.json b/package.json
index df94e90..4bccd57 100644
--- a/package.json
+++ b/package.json
@@ -17,7 +17,6 @@
     "@sveltejs/vite-plugin-svelte": "^3.0.0",
     "@tsconfig/svelte": "^4.0.1",
     "@types/node": "^20.3.0",
-    "eslint": "^8.42.0",
     "fast-glob": "^3.2.12",
     "glob": "^7.2.3",
     "js-yaml": "^4.1.0",

From 7159755c7074be1c7054141cc74bb50b0ece02f6 Mon Sep 17 00:00:00 2001
From: frostime <frostime@foxmail.com>
Date: Tue, 30 Apr 2024 16:00:03 +0800
Subject: [PATCH 52/82] =?UTF-8?q?=F0=9F=94=A8=20=E8=B0=83=E6=95=B4=20setti?=
 =?UTF-8?q?ng=20=E7=9A=84=20interface=20=E5=AE=9A=E4=B9=89?=
MIME-Version: 1.0
Content-Type: text/plain; charset=UTF-8
Content-Transfer-Encoding: 8bit

---
 src/libs/index.d.ts       | 32 +++++++++++++++++++++++++-------
 src/libs/setting-utils.ts |  8 ++++----
 2 files changed, 29 insertions(+), 11 deletions(-)

diff --git a/src/libs/index.d.ts b/src/libs/index.d.ts
index 9e0b889..7ca733f 100644
--- a/src/libs/index.d.ts
+++ b/src/libs/index.d.ts
@@ -1,10 +1,17 @@
+/*
+ * Copyright (c) 2024 by frostime. All Rights Reserved.
+ * @Author       : frostime
+ * @Date         : 2024-04-19 18:30:12
+ * @FilePath     : /src/libs/index.d.ts
+ * @LastEditTime : 2024-04-30 15:53:15
+ * @Description  : 
+ */
 type TSettingItemType = "checkbox" | "select" | "textinput" | "textarea" | "number" | "slider" | "button" | "hint";
-interface ISettingItem {
+
+interface ISettingItemCore {
+    type: TSettingItemType;
     key: string;
     value: any;
-    type: TSettingItemType;
-    title: string;
-    description?: string;
     placeholder?: string;
     slider?: {
         min: number;
@@ -12,12 +19,23 @@ interface ISettingItem {
         step: number;
     };
     options?: { [key: string | number]: string };
-    action?: {
-        callback: () => void;
-    }
     button?: {
         label: string;
         callback: () => void;
     }
+}
+
+interface ISettingItem extends ISettingItemCore {
+    title: string;
+    description: string;
+}
+
+
+//Interface for setting-utils
+interface ISettingUtilsItem extends ISettingItem {
+    direction?: "row" | "column";
+    action?: {
+        callback: () => void;
+    }
     createElement?: (currentVal: any) => HTMLElement;
 }
diff --git a/src/libs/setting-utils.ts b/src/libs/setting-utils.ts
index f43ab36..2a3926b 100644
--- a/src/libs/setting-utils.ts
+++ b/src/libs/setting-utils.ts
@@ -3,7 +3,7 @@
  * @Author       : frostime
  * @Date         : 2023-12-17 18:28:19
  * @FilePath     : /src/libs/setting-utils.ts
- * @LastEditTime : 2024-04-29 17:17:19
+ * @LastEditTime : 2024-04-30 15:50:07
  * @Description  : 
  */
 
@@ -56,7 +56,7 @@ export class SettingUtils {
     name: string;
     file: string;
 
-    settings: Map<string, ISettingItem> = new Map();
+    settings: Map<string, ISettingItemCore> = new Map();
     elements: Map<string, HTMLElement> = new Map();
 
     constructor(args: {
@@ -214,7 +214,7 @@ export class SettingUtils {
         return data;
     }
 
-    addItem(item: ISettingItem) {
+    addItem(item: ISettingUtilsItem) {
         this.settings.set(item.key, item);
         if (item.createElement === undefined) {
             let itemElement = this.createDefaultElement(item);
@@ -242,7 +242,7 @@ export class SettingUtils {
         }
     }
 
-    createDefaultElement(item: ISettingItem) {
+    createDefaultElement(item: ISettingUtilsItem) {
         let itemElement: HTMLElement;
         switch (item.type) {
             case 'checkbox':

From 59daefa941e3fb2b2c590a58a8872c9ea8cfb519 Mon Sep 17 00:00:00 2001
From: frostime <frostime@foxmail.com>
Date: Tue, 30 Apr 2024 16:15:58 +0800
Subject: [PATCH 53/82] =?UTF-8?q?=E2=AC=86=EF=B8=8F=20update=20siyuan:=20a?=
 =?UTF-8?q?dd=20`direction`=20parameter?=
MIME-Version: 1.0
Content-Type: text/plain; charset=UTF-8
Content-Transfer-Encoding: 8bit

---
 README.md                 | 2 +-
 README_zh_CN.md           | 2 +-
 package.json              | 6 +++---
 plugin.json               | 4 ++--
 src/index.ts              | 1 +
 src/libs/setting-utils.ts | 6 +++++-
 6 files changed, 13 insertions(+), 8 deletions(-)

diff --git a/README.md b/README.md
index 3c233a7..09c8cdd 100644
--- a/README.md
+++ b/README.md
@@ -3,7 +3,7 @@
 
 [中文版](./README_zh_CN.md)
 
-> Consistent with [siyuan/plugin-sample](https://github.com/siyuan-note/plugin-sample) [v0.3.4](https://github.com/siyuan-note/plugin-sample/tree/v0.3.4)
+> Consistent with [siyuan/plugin-sample](https://github.com/siyuan-note/plugin-sample) [v0.3.5](https://github.com/siyuan-note/plugin-sample/tree/v0.3.5)
 
 
 
diff --git a/README_zh_CN.md b/README_zh_CN.md
index 1510f9b..ca35f7c 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.3.4](https://github.com/siyuan-note/plugin-sample/tree/v0.3.4)
+> 本例同 [siyuan/plugin-sample](https://github.com/siyuan-note/plugin-sample) [v0.3.5](https://github.com/siyuan-note/plugin-sample/tree/v0.3.5)
 
 1. 使用 vite 打包
 2. 使用符号链接、而不是把项目放到插件目录下的模式进行开发
diff --git a/package.json b/package.json
index 4bccd57..44cdd78 100644
--- a/package.json
+++ b/package.json
@@ -1,11 +1,11 @@
 {
   "name": "plugin-sample-vite-svelte",
-  "version": "0.3.2",
+  "version": "0.3.5",
   "type": "module",
   "description": "This is a sample plugin based on vite and svelte for Siyuan (https://b3log.org/siyuan)",
   "repository": "",
   "homepage": "",
-  "author": "",
+  "author": "frostime",
   "license": "MIT",
   "scripts": {
     "make-link": "node  --no-warnings ./scripts/make_dev_link.js",
@@ -23,7 +23,7 @@
     "minimist": "^1.2.8",
     "rollup-plugin-livereload": "^2.0.5",
     "sass": "^1.63.3",
-    "siyuan": "0.9.7",
+    "siyuan": "0.9.8",
     "svelte": "^4.2.0",
     "ts-node": "^10.9.1",
     "typescript": "^5.1.3",
diff --git a/plugin.json b/plugin.json
index b810268..071b2a3 100644
--- a/plugin.json
+++ b/plugin.json
@@ -2,8 +2,8 @@
   "name": "plugin-sample-vite-svelte",
   "author": "frostime",
   "url": "https://github.com/siyuan-note/plugin-sample-vite-svelte",
-  "version": "0.3.4",
-  "minAppVersion": "3.0.0",
+  "version": "0.3.5",
+  "minAppVersion": "3.0.12",
   "backends": [
     "windows",
     "linux",
diff --git a/src/index.ts b/src/index.ts
index 704ad00..4af2041 100644
--- a/src/index.ts
+++ b/src/index.ts
@@ -235,6 +235,7 @@ export default class PluginSample extends Plugin {
             type: "slider",
             title: "Slider text",
             description: "Slider description",
+            direction: "column",
             slider: {
                 min: 0,
                 max: 100,
diff --git a/src/libs/setting-utils.ts b/src/libs/setting-utils.ts
index 2a3926b..e611ce8 100644
--- a/src/libs/setting-utils.ts
+++ b/src/libs/setting-utils.ts
@@ -3,7 +3,7 @@
  * @Author       : frostime
  * @Date         : 2023-12-17 18:28:19
  * @FilePath     : /src/libs/setting-utils.ts
- * @LastEditTime : 2024-04-30 15:50:07
+ * @LastEditTime : 2024-04-30 16:09:54
  * @Description  : 
  */
 
@@ -222,6 +222,7 @@ export class SettingUtils {
             this.plugin.setting.addItem({
                 title: item.title,
                 description: item?.description,
+                direction: item?.direction,
                 createActionElement: () => {
                     this.updateElementFromValue(item.key);
                     let element = this.getElement(item.key);
@@ -232,6 +233,7 @@ export class SettingUtils {
             this.plugin.setting.addItem({
                 title: item.title,
                 description: item?.description,
+                direction: item?.direction,
                 createActionElement: () => {
                     let val = this.get(item.key);
                     let element = item.createElement(val);
@@ -334,6 +336,7 @@ export class SettingUtils {
 
     private updateValueFromElement(key: string) {
         let item = this.settings.get(key);
+        if (item.type === 'button') return;
         let element = this.elements.get(key) as any;
         item.value = valueOf(element);
         // switch (item.type) {
@@ -360,6 +363,7 @@ export class SettingUtils {
 
     private updateElementFromValue(key: string) {
         let item = this.settings.get(key);
+        if (item.type === 'button') return;
         let element = this.elements.get(key) as any;
         setValue(element, item.value);
         // switch (item.type) {

From 29c494a3f4aa977341f103f0b794b692f0b73f5e Mon Sep 17 00:00:00 2001
From: frostime <frostime@foxmail.com>
Date: Tue, 30 Apr 2024 16:32:26 +0800
Subject: [PATCH 54/82] =?UTF-8?q?=E2=9C=A8=20feat(setting-utils):=20getter?=
 =?UTF-8?q?=20and=20setting?=
MIME-Version: 1.0
Content-Type: text/plain; charset=UTF-8
Content-Transfer-Encoding: 8bit

---
 src/index.ts              |  12 +--
 src/libs/index.d.ts       |   4 +-
 src/libs/setting-utils.ts | 153 ++++++++++++++++++--------------------
 3 files changed, 81 insertions(+), 88 deletions(-)

diff --git a/src/index.ts b/src/index.ts
index 4af2041..6a59d4c 100644
--- a/src/index.ts
+++ b/src/index.ts
@@ -176,7 +176,7 @@ export default class PluginSample extends Plugin {
                 // Called when focus is lost and content changes
                 callback: () => {
                     // Return data and save it in real time
-                    let value = this.settingUtils.takeAndSave("Input")
+                    let value = this.settingUtils.takeAndSave("Input");
                     console.log(value);
                 }
             }
@@ -191,7 +191,7 @@ export default class PluginSample extends Plugin {
             action: {
                 callback: () => {
                     // Read data in real time
-                    let value = this.settingUtils.take("InputArea")
+                    let value = this.settingUtils.take("InputArea");
                     console.log(value);
                 }
             }
@@ -205,8 +205,8 @@ export default class PluginSample extends Plugin {
             action: {
                 callback: () => {
                     // Return data and save it in real time
-                    let value = !this.settingUtils.get("Check")
-                     this.settingUtils.set("Check", value)
+                    let value = !this.settingUtils.get("Check");
+                    this.settingUtils.set("Check", value);
                     console.log(value);
                 }
             }
@@ -224,7 +224,7 @@ export default class PluginSample extends Plugin {
             action: {
                 callback: () => {
                     // Read data in real time
-                    let value = this.settingUtils.take("Select")
+                    let value = this.settingUtils.take("Select");
                     console.log(value);
                 }
             }
@@ -244,7 +244,7 @@ export default class PluginSample extends Plugin {
             action:{
                 callback: () => {
                     // Read data in real time
-                    let value = this.settingUtils.take("Slider")
+                    let value = this.settingUtils.take("Slider");
                     console.log(value);
                 }
             }
diff --git a/src/libs/index.d.ts b/src/libs/index.d.ts
index 7ca733f..539076a 100644
--- a/src/libs/index.d.ts
+++ b/src/libs/index.d.ts
@@ -3,7 +3,7 @@
  * @Author       : frostime
  * @Date         : 2024-04-19 18:30:12
  * @FilePath     : /src/libs/index.d.ts
- * @LastEditTime : 2024-04-30 15:53:15
+ * @LastEditTime : 2024-04-30 16:26:23
  * @Description  : 
  */
 type TSettingItemType = "checkbox" | "select" | "textinput" | "textarea" | "number" | "slider" | "button" | "hint";
@@ -38,4 +38,6 @@ interface ISettingUtilsItem extends ISettingItem {
         callback: () => void;
     }
     createElement?: (currentVal: any) => HTMLElement;
+    getEleVal?: (ele: HTMLElement) => any;
+    setEleVal?: (ele: HTMLElement, val: any) => void;
 }
diff --git a/src/libs/setting-utils.ts b/src/libs/setting-utils.ts
index e611ce8..19952c4 100644
--- a/src/libs/setting-utils.ts
+++ b/src/libs/setting-utils.ts
@@ -3,60 +3,83 @@
  * @Author       : frostime
  * @Date         : 2023-12-17 18:28:19
  * @FilePath     : /src/libs/setting-utils.ts
- * @LastEditTime : 2024-04-30 16:09:54
+ * @LastEditTime : 2024-04-30 16:28:00
  * @Description  : 
  */
 
 import { Plugin, Setting } from 'siyuan';
 
-const valueOf = (ele: HTMLElement, parseNumber: Function=parseInt) => {
-    let val: any = null;
-    if (ele instanceof HTMLInputElement) {
-        if (ele.type === 'checkbox') {
-            val = ele.checked;
-        } else {
-            if (ele.type === 'number') {
-                val = parseNumber(ele.value);
-            } else {
-                val = ele.value;
+
+/**
+ * The default function to get the value of the element
+ * @param type 
+ * @returns 
+ */
+const createDefaultGetter = (type: TSettingItemType) => {
+    let getter: (ele: HTMLElement) => any;
+    switch (type) {
+        case 'checkbox':
+            getter = (ele: HTMLInputElement) => {
+                return ele.checked;
             }
-        }
-    } else if (ele instanceof HTMLSelectElement) {
-        val = ele.value;
-    } else if (ele instanceof HTMLTextAreaElement) {
-        val = ele.value;
-    } else {
-        val = ele?.textContent;
+            break;
+        case 'select':
+        case 'slider':
+        case 'textinput':
+        case 'textarea':
+            getter = (ele: HTMLInputElement | HTMLTextAreaElement | HTMLSelectElement) => {
+                return ele.value;
+            }
+            break;
+        case 'number':
+            getter = (ele: HTMLInputElement) => {
+                return parseInt(ele.value);
+            }
+            break;
+        default:
+            getter = (ele: HTMLElement) => {
+                return ele?.textContent;
+            }
+            break;
     }
-    return val;
+    return getter;
 }
 
-const setValue = (ele: HTMLElement, value: any) => {
-    if (ele === null || ele === undefined) return;
-    if (ele instanceof HTMLInputElement) {
-        if (ele.type === 'checkbox') {
-            ele.checked = value;
-        } else {
-            ele.value = value;
-        }
-        if (ele.type === 'range') {
-            ele.ariaLabel = value;
-        }
-    } else if (ele instanceof HTMLSelectElement) {
-        ele.value = value;
-    } else if (ele instanceof HTMLTextAreaElement) {
-        ele.value = value;
-    } else {
-        ele.textContent = value;
+
+/**
+ * The default function to set the value of the element
+ * @param type 
+ * @returns 
+ */
+const createDefaultSetter = (type: TSettingItemType) => {
+    let setter: (ele: HTMLElement, value: any) => void;
+    switch (type) {
+        case 'checkbox':
+            setter = (ele: HTMLInputElement, value: any) => {
+                ele.checked = value;
+            }
+            break;
+        case 'select':
+        case 'slider':
+        case 'textinput':
+        case 'textarea':
+        case 'number':
+            setter = (ele: HTMLInputElement | HTMLTextAreaElement | HTMLSelectElement, value: any) => {
+                ele.value = value;
+            }
+            break;
     }
+    return setter;
+
 }
 
+
 export class SettingUtils {
     plugin: Plugin;
     name: string;
     file: string;
 
-    settings: Map<string, ISettingItemCore> = new Map();
+    settings: Map<string, ISettingUtilsItem> = new Map();
     elements: Map<string, HTMLElement> = new Map();
 
     constructor(args: {
@@ -157,14 +180,15 @@ export class SettingUtils {
       * @returns value in html
       */
     take(key: string, apply: boolean = false) {
+        let item = this.settings.get(key);
         let element = this.elements.get(key) as any;
-        if (!element){
+        if (!element) {
             return
         }
         if (apply) {
             this.updateValueFromElement(key);
         }
-        return valueOf(element)
+        return item.getEleVal(element);
     }
 
     /**
@@ -216,6 +240,13 @@ export class SettingUtils {
 
     addItem(item: ISettingUtilsItem) {
         this.settings.set(item.key, item);
+        if (item.getEleVal === undefined) {
+            item.getEleVal = createDefaultGetter(item.type);
+        }
+        if (item.setEleVal === undefined) {
+            item.setEleVal = createDefaultSetter(item.type);
+        }
+
         if (item.createElement === undefined) {
             let itemElement = this.createDefaultElement(item);
             this.elements.set(item.key, itemElement);
@@ -338,53 +369,13 @@ export class SettingUtils {
         let item = this.settings.get(key);
         if (item.type === 'button') return;
         let element = this.elements.get(key) as any;
-        item.value = valueOf(element);
-        // switch (item.type) {
-        //     case 'checkbox':
-        //         item.value = element.checked;
-        //         break;
-        //     case 'select':
-        //         item.value = element.value;
-        //         break;
-        //     case 'slider':
-        //         item.value = element.value;
-        //         break;
-        //     case 'textinput':
-        //         item.value = element.value;
-        //         break;
-        //     case 'textarea':
-        //         item.value = element.value;
-        //         break;
-        //     case 'number':
-        //         item.value = parseInt(element.value);
-        //         break;
-        // }
+        item.value = item.getEleVal(element);
     }
 
     private updateElementFromValue(key: string) {
         let item = this.settings.get(key);
         if (item.type === 'button') return;
         let element = this.elements.get(key) as any;
-        setValue(element, item.value);
-        // switch (item.type) {
-        //     case 'checkbox':
-        //         element.checked = item.value;
-        //         break;
-        //     case 'select':
-        //         element.value = item.value;
-        //         break;
-        //     case 'slider':
-        //         element.value = item.value;
-        //         break;
-        //     case 'textinput':
-        //         element.value = item.value;
-        //         break;
-        //     case 'textarea':
-        //         element.value = item.value;
-        //         break;
-        //     case 'number':
-        //         element.value = item.value;
-        //         break;
-        // }
+        item.setEleVal(element, item.value);
     }
 }
\ No newline at end of file

From 3d565424aec01410a512c066f1d0ce3d94944439 Mon Sep 17 00:00:00 2001
From: frostime <frostime@foxmail.com>
Date: Tue, 30 Apr 2024 16:48:10 +0800
Subject: [PATCH 55/82] =?UTF-8?q?=F0=9F=8E=A8=20feat:=20Add=20"custom=20se?=
 =?UTF-8?q?tting-items"=20example?=
MIME-Version: 1.0
Content-Type: text/plain; charset=UTF-8
Content-Transfer-Encoding: 8bit

---
 src/index.ts              | 26 ++++++++++++++++++++++++--
 src/libs/index.d.ts       |  4 ++--
 src/libs/setting-utils.ts | 24 ++++++++++++++++--------
 3 files changed, 42 insertions(+), 12 deletions(-)

diff --git a/src/index.ts b/src/index.ts
index 6a59d4c..62a09a7 100644
--- a/src/index.ts
+++ b/src/index.ts
@@ -31,7 +31,7 @@ const DOCK_TYPE = "dock_tab";
 
 export default class PluginSample extends Plugin {
 
-    private customTab: () => IModel;
+    customTab: () => IModel;
     private isMobile: boolean;
     private blockIconEventBindThis = this.blockIconEvent.bind(this);
     private settingUtils: SettingUtils;
@@ -262,6 +262,28 @@ export default class PluginSample extends Plugin {
                 }
             }
         });
+        this.settingUtils.addItem({
+            key: "Custom Element",
+            value: "",
+            type: "custom",
+            direction: "row",
+            title: "Custom Element",
+            description: "Custom Element description",
+            //Any custom element must offer the following methods
+            createElement: (currentVal: any) => {
+                let div = document.createElement('div');
+                div.style.border = "1px solid var(--b3-theme-primary)";
+                div.contentEditable = "true";
+                div.textContent = currentVal;
+                return div;
+            },
+            getEleVal: (ele: HTMLElement) => {
+                return ele.textContent;
+            },
+            setEleVal: (ele: HTMLElement, val: any) => {
+                ele.textContent = val;
+            }
+        });
         this.settingUtils.addItem({
             key: "Hint",
             value: "",
@@ -437,7 +459,7 @@ export default class PluginSample extends Plugin {
             title: `SiYuan ${Constants.SIYUAN_VERSION}`,
             content: `<div id="helloPanel" class="b3-dialog__content"></div>`,
             width: this.isMobile ? "92vw" : "720px",
-            destroyCallback(options) {
+            destroyCallback() {
                 // hello.$destroy();
             },
         });
diff --git a/src/libs/index.d.ts b/src/libs/index.d.ts
index 539076a..ab2f6c3 100644
--- a/src/libs/index.d.ts
+++ b/src/libs/index.d.ts
@@ -3,10 +3,10 @@
  * @Author       : frostime
  * @Date         : 2024-04-19 18:30:12
  * @FilePath     : /src/libs/index.d.ts
- * @LastEditTime : 2024-04-30 16:26:23
+ * @LastEditTime : 2024-04-30 16:39:54
  * @Description  : 
  */
-type TSettingItemType = "checkbox" | "select" | "textinput" | "textarea" | "number" | "slider" | "button" | "hint";
+type TSettingItemType = "checkbox" | "select" | "textinput" | "textarea" | "number" | "slider" | "button" | "hint" | "custom";
 
 interface ISettingItemCore {
     type: TSettingItemType;
diff --git a/src/libs/setting-utils.ts b/src/libs/setting-utils.ts
index 19952c4..948dc2f 100644
--- a/src/libs/setting-utils.ts
+++ b/src/libs/setting-utils.ts
@@ -3,7 +3,7 @@
  * @Author       : frostime
  * @Date         : 2023-12-17 18:28:19
  * @FilePath     : /src/libs/setting-utils.ts
- * @LastEditTime : 2024-04-30 16:28:00
+ * @LastEditTime : 2024-04-30 16:42:23
  * @Description  : 
  */
 
@@ -21,7 +21,7 @@ const createDefaultGetter = (type: TSettingItemType) => {
         case 'checkbox':
             getter = (ele: HTMLInputElement) => {
                 return ele.checked;
-            }
+            };
             break;
         case 'select':
         case 'slider':
@@ -29,7 +29,7 @@ const createDefaultGetter = (type: TSettingItemType) => {
         case 'textarea':
             getter = (ele: HTMLInputElement | HTMLTextAreaElement | HTMLSelectElement) => {
                 return ele.value;
-            }
+            };
             break;
         case 'number':
             getter = (ele: HTMLInputElement) => {
@@ -37,9 +37,7 @@ const createDefaultGetter = (type: TSettingItemType) => {
             }
             break;
         default:
-            getter = (ele: HTMLElement) => {
-                return ele?.textContent;
-            }
+            getter = () => null;
             break;
     }
     return getter;
@@ -57,7 +55,7 @@ const createDefaultSetter = (type: TSettingItemType) => {
         case 'checkbox':
             setter = (ele: HTMLInputElement, value: any) => {
                 ele.checked = value;
-            }
+            };
             break;
         case 'select':
         case 'slider':
@@ -66,7 +64,10 @@ const createDefaultSetter = (type: TSettingItemType) => {
         case 'number':
             setter = (ele: HTMLInputElement | HTMLTextAreaElement | HTMLSelectElement, value: any) => {
                 ele.value = value;
-            }
+            };
+            break;
+        default:
+            setter = () => {};
             break;
     }
     return setter;
@@ -240,6 +241,13 @@ export class SettingUtils {
 
     addItem(item: ISettingUtilsItem) {
         this.settings.set(item.key, item);
+        const IsCustom = item.type === 'custom';
+        let error = IsCustom && (item.createElement === undefined || item.getEleVal === undefined || item.setEleVal === undefined);
+        if (error) {
+            console.error('The custom setting item must have createElement, getEleVal and setEleVal methods');
+            return;
+        }
+
         if (item.getEleVal === undefined) {
             item.getEleVal = createDefaultGetter(item.type);
         }

From 9f54e7046cf6070ac2ae5547240ea8d45e0425ae Mon Sep 17 00:00:00 2001
From: frostime <frostime@foxmail.com>
Date: Tue, 30 Apr 2024 22:15:50 +0800
Subject: [PATCH 56/82] =?UTF-8?q?=E2=9C=A8=20misc?=
MIME-Version: 1.0
Content-Type: text/plain; charset=UTF-8
Content-Transfer-Encoding: 8bit

---
 src/libs/setting-utils.ts | 8 ++++----
 1 file changed, 4 insertions(+), 4 deletions(-)

diff --git a/src/libs/setting-utils.ts b/src/libs/setting-utils.ts
index 948dc2f..33c5cac 100644
--- a/src/libs/setting-utils.ts
+++ b/src/libs/setting-utils.ts
@@ -3,7 +3,7 @@
  * @Author       : frostime
  * @Date         : 2023-12-17 18:28:19
  * @FilePath     : /src/libs/setting-utils.ts
- * @LastEditTime : 2024-04-30 16:42:23
+ * @LastEditTime : 2024-04-30 22:15:25
  * @Description  : 
  */
 
@@ -105,7 +105,7 @@ export class SettingUtils {
                     args.callback(data);
                 }
                 this.plugin.data[this.name] = data;
-                this.save();
+                this.save(data);
             },
             destroyCallback: () => {
                 //Restore the original value
@@ -128,8 +128,8 @@ export class SettingUtils {
         return data;
     }
 
-    async save() {
-        let data = this.dump();
+    async save(data?: any) {
+        data = data ?? this.dump();
         await this.plugin.saveData(this.file, this.dump());
         console.debug('Save config:', data);
         return data;

From 03ab34f5523274b3c416801359d8df32f8617141 Mon Sep 17 00:00:00 2001
From: frostime <frostime@foxmail.com>
Date: Wed, 1 May 2024 17:45:05 +0800
Subject: [PATCH 57/82] =?UTF-8?q?=E2=9A=A1=20=E7=A6=81=E7=94=A8=E6=80=9D?=
 =?UTF-8?q?=E6=BA=90=E5=86=85=E9=83=A8=E7=9A=84=20enter=20confirm=20?=
 =?UTF-8?q?=E7=89=B9=E6=80=A7?=
MIME-Version: 1.0
Content-Type: text/plain; charset=UTF-8
Content-Transfer-Encoding: 8bit

---
 src/libs/setting-utils.ts | 12 ++++++++++--
 1 file changed, 10 insertions(+), 2 deletions(-)

diff --git a/src/libs/setting-utils.ts b/src/libs/setting-utils.ts
index 33c5cac..ae316e2 100644
--- a/src/libs/setting-utils.ts
+++ b/src/libs/setting-utils.ts
@@ -3,7 +3,7 @@
  * @Author       : frostime
  * @Date         : 2023-12-17 18:28:19
  * @FilePath     : /src/libs/setting-utils.ts
- * @LastEditTime : 2024-04-30 22:15:25
+ * @LastEditTime : 2024-05-01 17:44:16
  * @Description  : 
  */
 
@@ -285,6 +285,13 @@ export class SettingUtils {
 
     createDefaultElement(item: ISettingUtilsItem) {
         let itemElement: HTMLElement;
+        //阻止思源内置的回车键确认
+        const preventEnterConfirm = (e) => {
+            if (e.key === 'Enter') {
+                e.preventDefault();
+                e.stopImmediatePropagation();
+            }
+        }
         switch (item.type) {
             case 'checkbox':
                 let element: HTMLInputElement = document.createElement('input');
@@ -330,7 +337,7 @@ export class SettingUtils {
                 textInputElement.value = item.value;
                 textInputElement.onchange = item.action?.callback ?? (() => { });
                 itemElement = textInputElement;
-
+                textInputElement.addEventListener('keydown', preventEnterConfirm);
                 break;
             case 'textarea':
                 let textareaElement: HTMLTextAreaElement = document.createElement('textarea');
@@ -345,6 +352,7 @@ export class SettingUtils {
                 numberElement.className = 'b3-text-field fn__flex-center fn__size200';
                 numberElement.value = item.value;
                 itemElement = numberElement;
+                numberElement.addEventListener('keydown', preventEnterConfirm);
                 break;
             case 'button':
                 let buttonElement: HTMLButtonElement = document.createElement('button');

From 67d6024a04dce60765347c35c6d2422c33971321 Mon Sep 17 00:00:00 2001
From: frostime <frostime@foxmail.com>
Date: Sat, 1 Jun 2024 16:29:07 +0800
Subject: [PATCH 58/82] =?UTF-8?q?=E2=9C=A8=20feat:=20add=20kits=20about=20?=
 =?UTF-8?q?dialogs?=
MIME-Version: 1.0
Content-Type: text/plain; charset=UTF-8
Content-Transfer-Encoding: 8bit

---
 src/libs/dialog.ts | 121 +++++++++++++++++++++++++++++++++++++++++++++
 1 file changed, 121 insertions(+)
 create mode 100644 src/libs/dialog.ts

diff --git a/src/libs/dialog.ts b/src/libs/dialog.ts
new file mode 100644
index 0000000..ea2b187
--- /dev/null
+++ b/src/libs/dialog.ts
@@ -0,0 +1,121 @@
+/*
+ * Copyright (c) 2024 by frostime. All Rights Reserved.
+ * @Author       : frostime
+ * @Date         : 2024-03-23 21:37:33
+ * @FilePath     : /src/libs/dialog.ts
+ * @LastEditTime : 2024-06-01 16:28:30
+ * @Description  : Kits about dialogs
+ */
+import { Dialog } from "siyuan";
+
+export const inputDialog = (args: {
+    title: string, placeholder?: string, defaultText?: string,
+    confirm?: (text: string) => void, cancel?: () => void,
+    width?: string, height?: string
+}) => {
+    const dialog = new Dialog({
+        title: args.title,
+        content: `<div class="b3-dialog__content">
+    <div class="ft__breakword"><textarea class="b3-text-field fn__block" style="height: 100%;" placeholder=${args?.placeholder ?? ''}>${args?.defaultText ?? ''}</textarea></div>
+</div>
+<div class="b3-dialog__action">
+    <button class="b3-button b3-button--cancel">${window.siyuan.languages.cancel}</button><div class="fn__space"></div>
+    <button class="b3-button b3-button--text" id="confirmDialogConfirmBtn">${window.siyuan.languages.confirm}</button>
+</div>`,
+        width: args.width ?? "520px",
+        height: args.height
+    });
+    const target: HTMLTextAreaElement = dialog.element.querySelector(".b3-dialog__content>div.ft__breakword>textarea");
+    const btnsElement = dialog.element.querySelectorAll(".b3-button");
+    btnsElement[0].addEventListener("click", () => {
+        if (args?.cancel) {
+            args.cancel();
+        }
+        dialog.destroy();
+    });
+    btnsElement[1].addEventListener("click", () => {
+        if (args?.confirm) {
+            args.confirm(target.value);
+        }
+        dialog.destroy();
+    });
+};
+
+export const inputDialogSync = async (args: {
+    title: string, placeholder?: string, defaultText?: string,
+    width?: string, height?: string
+}) => {
+    return new Promise<string>((resolve) => {
+        let newargs = {
+            ...args, confirm: (text) => {
+                resolve(text);
+            }, cancel: () => {
+                resolve(null);
+            }
+        };
+        inputDialog(newargs);
+    });
+}
+
+
+interface IConfirmDialogArgs {
+    title: string;
+    content: string | HTMLElement;
+    confirm?: (ele?: HTMLElement) => void;
+    cancel?: (ele?: HTMLElement) => void;
+    width?: string;
+    height?: string;
+}
+
+export const confirmDialog = (args: IConfirmDialogArgs) => {
+    const { title, content, confirm, cancel, width, height } = args;
+
+    const dialog = new Dialog({
+        title,
+        content: `<div class="b3-dialog__content">
+    <div class="ft__breakword">
+    </div>
+</div>
+<div class="b3-dialog__action">
+    <button class="b3-button b3-button--cancel">${window.siyuan.languages.cancel}</button><div class="fn__space"></div>
+    <button class="b3-button b3-button--text" id="confirmDialogConfirmBtn">${window.siyuan.languages.confirm}</button>
+</div>`,
+        width: width,
+        height: height
+    });
+
+    const target: HTMLElement = dialog.element.querySelector(".b3-dialog__content>div.ft__breakword");
+    if (typeof content === "string") {
+        target.innerHTML = content;
+    } else {
+        target.appendChild(content);
+    }
+
+    const btnsElement = dialog.element.querySelectorAll(".b3-button");
+    btnsElement[0].addEventListener("click", () => {
+        if (cancel) {
+            cancel(target);
+        }
+        dialog.destroy();
+    });
+    btnsElement[1].addEventListener("click", () => {
+        if (confirm) {
+            confirm(target);
+        }
+        dialog.destroy();
+    });
+};
+
+
+export const confirmDialogSync = async (args: IConfirmDialogArgs) => {
+    return new Promise<HTMLElement>((resolve) => {
+        let newargs = {
+            ...args, confirm: (ele: HTMLElement) => {
+                resolve(ele);
+            }, cancel: (ele: HTMLElement) => {
+                resolve(ele);
+            }
+        };
+        confirmDialog(newargs);
+    });
+};

From 81d5bee160e9b8b505a09b523c27fea2f89d18a3 Mon Sep 17 00:00:00 2001
From: frostime <frostime@foxmail.com>
Date: Sat, 1 Jun 2024 16:35:02 +0800
Subject: [PATCH 59/82] =?UTF-8?q?=F0=9F=93=9Ddocs?=
MIME-Version: 1.0
Content-Type: text/plain; charset=UTF-8
Content-Transfer-Encoding: 8bit

---
 CHANGELOG.md    | 5 ++++-
 README.md       | 2 +-
 README_zh_CN.md | 2 +-
 package.json    | 2 +-
 4 files changed, 7 insertions(+), 4 deletions(-)

diff --git a/CHANGELOG.md b/CHANGELOG.md
index ce36ec8..af04807 100644
--- a/CHANGELOG.md
+++ b/CHANGELOG.md
@@ -1,6 +1,9 @@
 # Changelog
 
-## 0.3.5 2024-03
+## v0.3.5 2024-04-30
+
+* [Add `direction` to plugin method `Setting.addItem`](https://github.com/siyuan-note/siyuan/issues/11183)
+
 
 ## 0.3.4 2024-02-20
 
diff --git a/README.md b/README.md
index 09c8cdd..5f3c813 100644
--- a/README.md
+++ b/README.md
@@ -11,7 +11,7 @@
 2. Use symbolic linking instead of putting the project into the plugins directory program development
 3. Built-in support for the svelte framework
 
-     > If don't want svelte, turn to this template: [frostime/plugin-sample-vite](https://github.com/frostime/plugin-sample-vite)
+     > **If don't want svelte, turn to this template**: [frostime/plugin-sample-vite](https://github.com/frostime/plugin-sample-vite)
 
 4. Provides a github action template to automatically generate package.zip and upload to new release
 
diff --git a/README_zh_CN.md b/README_zh_CN.md
index ca35f7c..9f447fa 100644
--- a/README_zh_CN.md
+++ b/README_zh_CN.md
@@ -10,7 +10,7 @@
 2. 使用符号链接、而不是把项目放到插件目录下的模式进行开发
 3. 内置对 svelte 框架的支持
   
-   > 如果不想要 svelte,请移步 [frostime/plugin-sample-vite](https://github.com/frostime/plugin-sample-vite)
+   > **如果不想要 svelte,请移步这个模板:** [frostime/plugin-sample-vite](https://github.com/frostime/plugin-sample-vite)
 
 4. 提供一个github action 模板,能自动生成package.zip并上传到新版本中
 
diff --git a/package.json b/package.json
index 44cdd78..fd49b9c 100644
--- a/package.json
+++ b/package.json
@@ -23,7 +23,7 @@
     "minimist": "^1.2.8",
     "rollup-plugin-livereload": "^2.0.5",
     "sass": "^1.63.3",
-    "siyuan": "0.9.8",
+    "siyuan": "0.9.9",
     "svelte": "^4.2.0",
     "ts-node": "^10.9.1",
     "typescript": "^5.1.3",

From 9c7572b820189d61320b84f25132a1d44782f71b Mon Sep 17 00:00:00 2001
From: frostime <frostime@foxmail.com>
Date: Tue, 4 Jun 2024 15:04:07 +0800
Subject: [PATCH 60/82] =?UTF-8?q?=F0=9F=90=9B=20getFile=20API?=
MIME-Version: 1.0
Content-Type: text/plain; charset=UTF-8
Content-Transfer-Encoding: 8bit

---
 src/api.ts | 13 ++++++-------
 1 file changed, 6 insertions(+), 7 deletions(-)

diff --git a/src/api.ts b/src/api.ts
index c227518..0a421ce 100644
--- a/src/api.ts
+++ b/src/api.ts
@@ -6,7 +6,7 @@
  * API 文档见 [API_zh_CN.md](https://github.com/siyuan-note/siyuan/blob/master/API_zh_CN.md)
  */
 
-import { fetchSyncPost, IWebSocketData } from "siyuan";
+import { fetchPost, fetchSyncPost, IWebSocketData } from "siyuan";
 
 
 export async function request(url: string, data: any) {
@@ -328,12 +328,11 @@ export async function getFile(path: string): Promise<any> {
         path: path
     }
     let url = '/api/file/getFile';
-    try {
-        let file = await fetchSyncPost(url, data);
-        return file;
-    } catch (error_msg) {
-        return null;
-    }
+    return new Promise((resolve, _) => {
+        fetchPost(url, data, (content: any) => {
+            resolve(content)
+        });
+    });
 }
 
 export async function putFile(path: string, isDir: boolean, file: any) {

From 49eb06330da596e276afd399f993a340957f691f Mon Sep 17 00:00:00 2001
From: Frostime <frostime@foxmail.com>
Date: Thu, 6 Jun 2024 22:31:20 +0800
Subject: [PATCH 61/82] =?UTF-8?q?=F0=9F=94=A8=20Remove=20the=20default=20f?=
 =?UTF-8?q?und=20in=20plugin.json?=
MIME-Version: 1.0
Content-Type: text/plain; charset=UTF-8
Content-Transfer-Encoding: 8bit

---
 plugin.json | 2 +-
 1 file changed, 1 insertion(+), 1 deletion(-)

diff --git a/plugin.json b/plugin.json
index 071b2a3..865a240 100644
--- a/plugin.json
+++ b/plugin.json
@@ -32,7 +32,7 @@
   },
   "funding": {
     "custom": [
-      "https://afdian.net/a/frostime"
+      ""
     ]
   },
   "keywords": [

From 6d75a46a56c0aad62a74b0ee7943fc4e1601fc56 Mon Sep 17 00:00:00 2001
From: frostime <frostime@foxmail.com>
Date: Sat, 8 Jun 2024 18:30:53 +0800
Subject: [PATCH 62/82] =?UTF-8?q?=F0=9F=94=A8=20refactor:=20=E9=87=8D?=
 =?UTF-8?q?=E6=9E=84=E5=9F=BA=E4=BA=8E=20svelte=20=E7=9A=84=E8=AE=BE?=
 =?UTF-8?q?=E7=BD=AE=E9=9D=A2=E6=9D=BF=E5=8A=9F=E8=83=BD?=
MIME-Version: 1.0
Content-Type: text/plain; charset=UTF-8
Content-Transfer-Encoding: 8bit

---
 src/libs/index.d.ts           |   2 +-
 src/libs/item-input.svelte    | 108 ++++++++++++++++++++++++++++++++++
 src/libs/item-wrap.svelte     |  51 ++++++++++++++++
 src/libs/setting-item.svelte  | 105 ---------------------------------
 src/libs/setting-panel.svelte |  31 ++++++----
 src/setting-example.svelte    |   9 +++
 6 files changed, 187 insertions(+), 119 deletions(-)
 create mode 100644 src/libs/item-input.svelte
 create mode 100644 src/libs/item-wrap.svelte
 delete mode 100644 src/libs/setting-item.svelte

diff --git a/src/libs/index.d.ts b/src/libs/index.d.ts
index ab2f6c3..27a27ed 100644
--- a/src/libs/index.d.ts
+++ b/src/libs/index.d.ts
@@ -28,12 +28,12 @@ interface ISettingItemCore {
 interface ISettingItem extends ISettingItemCore {
     title: string;
     description: string;
+    direction?: "row" | "column";
 }
 
 
 //Interface for setting-utils
 interface ISettingUtilsItem extends ISettingItem {
-    direction?: "row" | "column";
     action?: {
         callback: () => void;
     }
diff --git a/src/libs/item-input.svelte b/src/libs/item-input.svelte
new file mode 100644
index 0000000..3a42b45
--- /dev/null
+++ b/src/libs/item-input.svelte
@@ -0,0 +1,108 @@
+<!--
+ Copyright (c) 2024 by frostime. All Rights Reserved.
+ Author       : frostime
+ Date         : 2024-06-07 18:49:52
+ FilePath     : /src/libs/components/input-item.svelte
+ LastEditTime : 2024-06-07 20:07:58
+ Description  : 
+-->
+<script lang="ts">
+    import { createEventDispatcher } from "svelte";
+    export let type: string; // Setting Type
+    export let key: string;
+    export let value: any;
+
+    //Optional
+    export let placeholder: string = "";
+    export let options: { [key: string | number]: string } = {};
+    export let slider: {
+        min: number;
+        max: number;
+        step: number;
+    } = { min: 0, max: 100, step: 1 };
+    export let button: {
+        label: string;
+        callback?: () => void;
+    } = { label: value, callback: () => {} };
+
+    const dispatch = createEventDispatcher();
+
+    function click() {
+        button?.callback();
+        dispatch("click", { key: key });
+    }
+
+    function changed() {
+        dispatch("changed", { key: key, value: value });
+    }
+</script>
+
+{#if type === "checkbox"}
+    <!-- Checkbox -->
+    <input
+        class="b3-switch fn__flex-center"
+        id={key}
+        type="checkbox"
+        bind:checked={value}
+        on:change={changed}
+    />
+{:else if type === "textinput"}
+    <!-- Text Input -->
+    <input
+        class="b3-text-field fn__flex-center fn__size200"
+        id={key}
+        {placeholder}
+        bind:value={value}
+        on:change={changed}
+    />
+{:else if type === "textarea"}
+    <textarea
+        class="b3-text-field fn__block"
+        style="resize: vertical; height: 10em; white-space: nowrap;"
+        bind:value={value}
+        on:change={changed}
+    />
+{:else if type === "number"}
+    <input
+        class="b3-text-field fn__flex-center fn__size200"
+        id={key}
+        type="number"
+        bind:value={value}
+        on:change={changed}
+    />
+{:else if type === "button"}
+    <!-- Button Input -->
+    <button
+        class="b3-button b3-button--outline fn__flex-center fn__size200"
+        id={key}
+        on:click={click}
+    >
+        {button.label}
+    </button>
+{:else if type === "select"}
+    <!-- Dropdown select -->
+    <select
+        class="b3-select fn__flex-center fn__size200"
+        id="iconPosition"
+        bind:value={value}
+        on:change={changed}
+    >
+        {#each Object.entries(options) as [value, text]}
+            <option {value}>{text}</option>
+        {/each}
+    </select>
+{:else if type == "slider"}
+    <!-- Slider -->
+    <div class="b3-tooltips b3-tooltips__n" aria-label={value}>
+        <input
+            class="b3-slider fn__size200"
+            id="fontSize"
+            min={slider.min}
+            max={slider.max}
+            step={slider.step}
+            type="range"
+            bind:value={value}
+            on:change={changed}
+        />
+    </div>
+{/if}
diff --git a/src/libs/item-wrap.svelte b/src/libs/item-wrap.svelte
new file mode 100644
index 0000000..0d839d5
--- /dev/null
+++ b/src/libs/item-wrap.svelte
@@ -0,0 +1,51 @@
+<!--
+ Copyright (c) 2024 by frostime. All Rights Reserved.
+ Author       : frostime
+ Date         : 2024-06-01 20:03:50
+ FilePath     : /src/libs/setting-item-wrap.svelte
+ LastEditTime : 2024-06-07 19:14:28
+ Description  : The setting item container
+-->
+<script lang="ts">
+    export let title: string; // Displayint Setting Title
+    export let description: string; // Displaying Setting Text
+    export let direction: 'row' | 'column' = 'column';
+</script>
+
+{#if direction === "row"}
+    <div class="item-wrap b3-label" data-key="CustomCSS">
+        <div class="fn__block">
+            <span class="title">{title}</span>
+            <div class="b3-label__text">{@html description}</div>
+            <div class="fn__hr"></div>
+            <slot />
+        </div>
+    </div>
+{:else}
+    <div class="item-wrap fn__flex b3-label config__item">
+        <div class="fn__flex-1">
+            <span class="title">{title}</span>
+            <div class="b3-label__text">
+                {@html description}
+            </div>
+        </div>
+        <span class="fn__space" />
+        <slot />
+    </div>
+{/if}
+
+<style>
+    span.title {
+        font-weight: bold;
+        color: var(--b3-theme-primary)
+    }
+    .item-wrap.b3-label {
+        box-shadow: none !important;
+        padding-bottom: 16px;
+        margin-bottom: 16px;
+    }
+
+    .item-wrap.b3-label:not(:last-child) {
+        border-bottom: 1px solid var(--b3-border-color);
+    }
+</style>
diff --git a/src/libs/setting-item.svelte b/src/libs/setting-item.svelte
deleted file mode 100644
index ef1a964..0000000
--- a/src/libs/setting-item.svelte
+++ /dev/null
@@ -1,105 +0,0 @@
-<script lang="ts">
-    import { createEventDispatcher } from "svelte";
-    export let type: string; // Setting Type
-    export let title: string; // Displayint Setting Title
-    export let description: string; // Displaying Setting Text
-    export let settingKey: string;
-    export let settingValue: any;
-
-    //Optional
-    export let placeholder: string = ""; // Use it if type is input
-    export let options: { [key: string | number]: string } = {}; // Use it if type is select
-    export let slider: {
-        min: number;
-        max: number;
-        step: number;
-    } = { min: 0, max: 100, step: 1 }; // Use it if type is slider
-    export let button: {
-        label: string;
-        callback: () => void;
-    } = { label: settingValue, callback: () => {} }; // Use it if type is button
-
-    const dispatch = createEventDispatcher();
-
-    function click() {
-        button?.callback();
-        dispatch("click", { key: settingKey });
-    }
-
-    function changed() {
-        dispatch("changed", { key: settingKey, value: settingValue });
-    }
-</script>
-
-<label class="fn__flex b3-label">
-    <div class="fn__flex-1">
-        {title}
-        <div class="b3-label__text">
-            {@html description}
-        </div> 
-    </div>
-    <span class="fn__space" />
-    <!-- <slot /> -->
-    {#if type === "checkbox"}
-        <!-- Checkbox -->
-        <input
-            class="b3-switch fn__flex-center"
-            id={settingKey}
-            type="checkbox"
-            bind:checked={settingValue}
-            on:change={changed}
-        />
-    {:else if type === "textinput"}
-        <!-- Text Input -->
-        <input
-            class="b3-text-field fn__flex-center fn__size200"
-            id={settingKey}
-            {placeholder}
-            bind:value={settingValue}
-            on:change={changed}
-        />
-    {:else if type === "number"}
-        <input
-            class="b3-text-field fn__flex-center fn__size200"
-            id={settingKey}
-            type="number"
-            bind:value={settingValue}
-            on:change={changed}
-        />
-    {:else if type === "button"}
-        <!-- Button Input -->
-        <button
-            class="b3-button b3-button--outline fn__flex-center fn__size200"
-            id={settingKey}
-            on:click={click}
-        >
-            {button.label}
-        </button>
-    {:else if type === "select"}
-        <!-- Dropdown select -->
-        <select
-            class="b3-select fn__flex-center fn__size200"
-            id="iconPosition"
-            bind:value={settingValue}
-            on:change={changed}
-        >
-            {#each Object.entries(options) as [value, text]}
-                <option {value}>{text}</option>
-            {/each}
-        </select>
-    {:else if type == "slider"}
-        <!-- Slider -->
-        <div class="b3-tooltips b3-tooltips__n" aria-label={settingValue}>
-            <input
-                class="b3-slider fn__size200"
-                id="fontSize"
-                min={slider.min}
-                max={slider.max}
-                step={slider.step}
-                type="range"
-                bind:value={settingValue}
-                on:change={changed}
-            />
-        </div>
-    {/if}
-</label>
diff --git a/src/libs/setting-panel.svelte b/src/libs/setting-panel.svelte
index 038d890..6a304a0 100644
--- a/src/libs/setting-panel.svelte
+++ b/src/libs/setting-panel.svelte
@@ -3,12 +3,13 @@
  Author       : frostime
  Date         : 2023-07-01 19:23:50
  FilePath     : /src/libs/setting-panel.svelte
- LastEditTime : 2024-04-27 16:46:49
+ LastEditTime : 2024-06-08 18:25:34
  Description  : 
 -->
 <script lang="ts">
     import { createEventDispatcher } from "svelte";
-    import SettingItem from "./setting-item.svelte";
+    import ItemWrap from "./item-wrap.svelte";
+    import InputItem from "./item-input.svelte";
 
     export let group: string;
     export let settingItems: ISettingItem[];
@@ -30,18 +31,22 @@
 <div class="config__tab-container {fn__none}" data-name={group}>
     <slot />
     {#each settingItems as item (item.key)}
-        <SettingItem
-            type={item.type}
+        <ItemWrap
             title={item.title}
             description={item.description}
-            settingKey={item.key}
-            settingValue={item.value}
-            placeholder={item?.placeholder}
-            options={item?.options}
-            slider={item?.slider}
-            button={item?.button}
-            on:click={onClick}
-            on:changed={onChanged}
-        />
+            direction={item?.direction}
+        > 
+            <InputItem
+                type={item.type}
+                key={item.key}
+                bind:value={item.value}
+                placeholder={item?.placeholder}
+                options={item?.options}
+                slider={item?.slider}
+                button={item?.button}
+                on:click={onClick}
+                on:changed={onChanged}
+            />
+        </ItemWrap>
     {/each}
 </div>
\ No newline at end of file
diff --git a/src/setting-example.svelte b/src/setting-example.svelte
index 4bafee5..245ed03 100644
--- a/src/setting-example.svelte
+++ b/src/setting-example.svelte
@@ -21,6 +21,15 @@
             value: 'This is a text',
             placeholder: 'placeholder'
         },
+        {
+            type: 'textarea',
+            title: 'textarea',
+            description: 'This is a textarea',
+            key: 'b2',
+            value: 'This is a textarea',
+            placeholder: 'placeholder',
+            direction: 'row'
+        },
         {
             type: 'select',
             title: 'select',

From ba57ba3f2381372f73d127587de16e3db17703d3 Mon Sep 17 00:00:00 2001
From: frostime <frostime@foxmail.com>
Date: Sat, 8 Jun 2024 20:51:13 +0800
Subject: [PATCH 63/82] =?UTF-8?q?=E2=9C=A8=20feat:=20add=20const.ts?=
MIME-Version: 1.0
Content-Type: text/plain; charset=UTF-8
Content-Transfer-Encoding: 8bit

---
 src/libs/const.ts    | 99 ++++++++++++++++++++++++++++++++++++++++++++
 src/types/index.d.ts | 36 +++++++++++++---
 2 files changed, 129 insertions(+), 6 deletions(-)
 create mode 100644 src/libs/const.ts

diff --git a/src/libs/const.ts b/src/libs/const.ts
new file mode 100644
index 0000000..2dd3e06
--- /dev/null
+++ b/src/libs/const.ts
@@ -0,0 +1,99 @@
+/*
+ * Copyright (c) 2024 by frostime. All Rights Reserved.
+ * @Author       : frostime
+ * @Date         : 2024-06-08 20:36:30
+ * @FilePath     : /src/libs/const.ts
+ * @LastEditTime : 2024-06-08 20:48:06
+ * @Description  : 
+ */
+
+
+export const BlockType2NodeType: {[key in BlockType]: string} = {
+    d: 'NodeDocument',
+    p: 'NodeParagraph',
+    query_embed: 'NodeBlockQueryEmbed',
+    l: 'NodeList',
+    i: 'NodeListItem',
+    h: 'NodeHeading',
+    iframe: 'NodeIFrame',
+    tb: 'NodeThematicBreak',
+    b: 'NodeBlockquote',
+    s: 'NodeSuperBlock',
+    c: 'NodeCodeBlock',
+    widget: 'NodeWidget',
+    t: 'NodeTable',
+    html: 'NodeHTMLBlock',
+    m: 'NodeMathBlock',
+    av: 'NodeAttributeView',
+    audio: 'NodeAudio'
+}
+
+
+export const NodeIcons = {
+    NodeAttributeView: {
+        icon: "iconDatabase"
+    },
+    NodeAudio: {
+        icon: "iconRecord"
+    },
+    NodeBlockQueryEmbed: {
+        icon: "iconSQL"
+    },
+    NodeBlockquote: {
+        icon: "iconQuote"
+    },
+    NodeCodeBlock: {
+        icon: "iconCode"
+    },
+    NodeDocument: {
+        icon: "iconFile"
+    },
+    NodeHTMLBlock: {
+        icon: "iconHTML5"
+    },
+    NodeHeading: {
+        icon: "iconHeadings",
+        subtypes: {
+            h1: { icon: "iconH1" },
+            h2: { icon: "iconH2" },
+            h3: { icon: "iconH3" },
+            h4: { icon: "iconH4" },
+            h5: { icon: "iconH5" },
+            h6: { icon: "iconH6" }
+        }
+    },
+    NodeIFrame: {
+        icon: "iconLanguage"
+    },
+    NodeList: {
+        subtypes: {
+            o: { icon: "iconOrderedList" },
+            t: { icon: "iconCheck" },
+            u: { icon: "iconList" }
+        }
+    },
+    NodeListItem: {
+        icon: "iconListItem"
+    },
+    NodeMathBlock: {
+        icon: "iconMath"
+    },
+    NodeParagraph: {
+        icon: "iconParagraph"
+    },
+    NodeSuperBlock: {
+        icon: "iconSuper"
+    },
+    NodeTable: {
+        icon: "iconTable"
+    },
+    NodeThematicBreak: {
+        icon: "iconLine"
+    },
+    NodeVideo: {
+        icon: "iconVideo"
+    },
+    NodeWidget: {
+        icon: "iconBoth"
+    }
+};
diff --git a/src/types/index.d.ts b/src/types/index.d.ts
index 1891c1a..f224b3e 100644
--- a/src/types/index.d.ts
+++ b/src/types/index.d.ts
@@ -1,10 +1,13 @@
-/**
- * Copyright (c) 2023 frostime. All rights reserved.
+/*
+ * Copyright (c) 2024 by frostime. All Rights Reserved.
+ * @Author       : frostime
+ * @Date         : 2023-08-15 10:28:10
+ * @FilePath     : /src/types/index.d.ts
+ * @LastEditTime : 2024-06-08 20:50:53
+ * @Description  : Frequently used data structures in SiYuan
  */
 
-/**
- * Frequently used data structures in SiYuan
- */
+
 type DocumentId = string;
 type BlockId = string;
 type NotebookId = string;
@@ -28,7 +31,25 @@ type NotebookConf = {
     dailyNoteTemplatePath: string;
 }
 
-type BlockType = "d" | "s" | "h" | "t" | "i" | "p" | "f" | "audio" | "video" | "other";
+type BlockType = 
+    | 'd'
+    | 'p'
+    | 'query_embed'
+    | 'l'
+    | 'i'
+    | 'h'
+    | 'iframe'
+    | 'tb'
+    | 'b'
+    | 's'
+    | 'c'
+    | 'widget'
+    | 't'
+    | 'html'
+    | 'm'
+    | 'av'
+    | 'audio';
+
 
 type BlockSubType = "d1" | "d2" | "s1" | "s2" | "s3" | "t1" | "t2" | "h1" | "h2" | "h3" | "h4" | "h5" | "h6" | "table" | "task" | "toggle" | "latex" | "quote" | "html" | "code" | "footnote" | "cite" | "collection" | "bookmark" | "attachment" | "comment" | "mindmap" | "spreadsheet" | "calendar" | "image" | "audio" | "video" | "other";
 
@@ -70,6 +91,7 @@ type doOperation = {
 
 interface Window {
     siyuan: {
+        config: any;
         notebooks: any;
         menus: any;
         dialogs: any;
@@ -78,5 +100,7 @@ interface Window {
         user: any;
         ws: any;
         languages: any;
+        emojis: any;
     };
+    Lute: any;
 }

From 5ff929e45654eef6ad97159b437eaa4508843357 Mon Sep 17 00:00:00 2001
From: frostime <frostime@foxmail.com>
Date: Fri, 14 Jun 2024 22:35:19 +0800
Subject: [PATCH 64/82] =?UTF-8?q?=F0=9F=93=9D=20doc:=20another=20template?=
MIME-Version: 1.0
Content-Type: text/plain; charset=UTF-8
Content-Transfer-Encoding: 8bit

---
 README.md       | 1 +
 README_zh_CN.md | 1 +
 2 files changed, 2 insertions(+)

diff --git a/README.md b/README.md
index 5f3c813..d441d42 100644
--- a/README.md
+++ b/README.md
@@ -12,6 +12,7 @@
 3. Built-in support for the svelte framework
 
      > **If don't want svelte, turn to this template**: [frostime/plugin-sample-vite](https://github.com/frostime/plugin-sample-vite)
+     > **We also provide with a vite+solidjs template**: [frostime/plugin-sample-vite-solidjs](https://github.com/frostime/plugin-sample-vite-solidjs)
 
 4. Provides a github action template to automatically generate package.zip and upload to new release
 
diff --git a/README_zh_CN.md b/README_zh_CN.md
index 9f447fa..ac92be7 100644
--- a/README_zh_CN.md
+++ b/README_zh_CN.md
@@ -11,6 +11,7 @@
 3. 内置对 svelte 框架的支持
   
    > **如果不想要 svelte,请移步这个模板:** [frostime/plugin-sample-vite](https://github.com/frostime/plugin-sample-vite)
+   > > **这里还提供了一个 vite+solidjs 的模板**: [frostime/plugin-sample-vite-solidjs](https://github.com/frostime/plugin-sample-vite-solidjs)
 
 4. 提供一个github action 模板,能自动生成package.zip并上传到新版本中
 

From 0a8ab5a2278b6cccdde1c66d0afceda1f75f4ce0 Mon Sep 17 00:00:00 2001
From: frostime <frostime@foxmail.com>
Date: Fri, 14 Jun 2024 22:36:29 +0800
Subject: [PATCH 65/82] =?UTF-8?q?=F0=9F=93=9D=20docs?=
MIME-Version: 1.0
Content-Type: text/plain; charset=UTF-8
Content-Transfer-Encoding: 8bit

---
 README.md       | 1 +
 README_zh_CN.md | 3 ++-
 2 files changed, 3 insertions(+), 1 deletion(-)

diff --git a/README.md b/README.md
index d441d42..a675463 100644
--- a/README.md
+++ b/README.md
@@ -12,6 +12,7 @@
 3. Built-in support for the svelte framework
 
      > **If don't want svelte, turn to this template**: [frostime/plugin-sample-vite](https://github.com/frostime/plugin-sample-vite)
+     >
      > **We also provide with a vite+solidjs template**: [frostime/plugin-sample-vite-solidjs](https://github.com/frostime/plugin-sample-vite-solidjs)
 
 4. Provides a github action template to automatically generate package.zip and upload to new release
diff --git a/README_zh_CN.md b/README_zh_CN.md
index ac92be7..d0f153c 100644
--- a/README_zh_CN.md
+++ b/README_zh_CN.md
@@ -11,7 +11,8 @@
 3. 内置对 svelte 框架的支持
   
    > **如果不想要 svelte,请移步这个模板:** [frostime/plugin-sample-vite](https://github.com/frostime/plugin-sample-vite)
-   > > **这里还提供了一个 vite+solidjs 的模板**: [frostime/plugin-sample-vite-solidjs](https://github.com/frostime/plugin-sample-vite-solidjs)
+   >
+   > **这里还提供了一个 vite+solidjs 的模板**: [frostime/plugin-sample-vite-solidjs](https://github.com/frostime/plugin-sample-vite-solidjs)
 
 4. 提供一个github action 模板,能自动生成package.zip并上传到新版本中
 

From 9e2a64b471172758d4508d63c29adc1851321fa3 Mon Sep 17 00:00:00 2001
From: frostime <frostime@foxmail.com>
Date: Sun, 30 Jun 2024 12:45:18 +0800
Subject: [PATCH 66/82] =?UTF-8?q?=F0=9F=94=A7=20ci(vite):=20=E7=94=9F?=
 =?UTF-8?q?=E6=88=90=20inline=20source=20map=EF=BC=8C=E6=96=B9=E4=BE=BF?=
 =?UTF-8?q?=E8=B0=83=E8=AF=95?=
MIME-Version: 1.0
Content-Type: text/plain; charset=UTF-8
Content-Transfer-Encoding: 8bit

---
 vite.config.ts | 2 +-
 1 file changed, 1 insertion(+), 1 deletion(-)

diff --git a/vite.config.ts b/vite.config.ts
index 31fc585..b7d3dae 100644
--- a/vite.config.ts
+++ b/vite.config.ts
@@ -69,7 +69,7 @@ export default defineConfig({
         emptyOutDir: false,
 
         // 构建后是否生成 source map 文件
-        sourcemap: false,
+        sourcemap: 'inline',
 
         // 设置为 false 可以禁用最小化混淆
         // 或是用来指定是应用哪种混淆器

From 504de7d821ffc0267dec4c4fc3286237f2aed90f Mon Sep 17 00:00:00 2001
From: frostime <frostime@foxmail.com>
Date: Mon, 1 Jul 2024 13:44:54 +0800
Subject: [PATCH 67/82] =?UTF-8?q?=F0=9F=90=9B=20fix:=20build=20=E6=98=AF?=
 =?UTF-8?q?=E4=B8=8D=E7=94=9F=E6=88=90=20sourcemap?=
MIME-Version: 1.0
Content-Type: text/plain; charset=UTF-8
Content-Transfer-Encoding: 8bit

---
 vite.config.ts | 2 +-
 1 file changed, 1 insertion(+), 1 deletion(-)

diff --git a/vite.config.ts b/vite.config.ts
index b7d3dae..809da57 100644
--- a/vite.config.ts
+++ b/vite.config.ts
@@ -69,7 +69,7 @@ export default defineConfig({
         emptyOutDir: false,
 
         // 构建后是否生成 source map 文件
-        sourcemap: 'inline',
+        sourcemap: isWatch ? 'inline' : false,
 
         // 设置为 false 可以禁用最小化混淆
         // 或是用来指定是应用哪种混淆器

From 059744903d224ff73e840931952728e5c35413f4 Mon Sep 17 00:00:00 2001
From: frostime <frostime@foxmail.com>
Date: Thu, 4 Jul 2024 21:27:46 +0800
Subject: [PATCH 68/82] =?UTF-8?q?=F0=9F=94=A7=20ci:=20update=20pnpm=20acti?=
 =?UTF-8?q?on?=
MIME-Version: 1.0
Content-Type: text/plain; charset=UTF-8
Content-Transfer-Encoding: 8bit

ref to: https://github.com/pnpm/action-setup/issues/136
---
 .github/workflows/release.yml | 4 ++--
 1 file changed, 2 insertions(+), 2 deletions(-)

diff --git a/.github/workflows/release.yml b/.github/workflows/release.yml
index 7bd6c4e..36af5e0 100644
--- a/.github/workflows/release.yml
+++ b/.github/workflows/release.yml
@@ -17,12 +17,12 @@ jobs:
       - name: Install Node.js
         uses: actions/setup-node@v3
         with:
-          node-version: 18
+          node-version: 20
           registry-url: "https://registry.npmjs.org"
 
         # Install pnpm
       - name: Install pnpm
-        uses: pnpm/action-setup@v2
+        uses: pnpm/action-setup@v4
         id: pnpm-install
         with:
           version: 8

From 46e48452c0eef96416aefccb5ad879e5744dfd13 Mon Sep 17 00:00:00 2001
From: Frostime <frostime@foxmail.com>
Date: Thu, 11 Jul 2024 00:41:46 +0800
Subject: [PATCH 69/82] Update plugin.json

---
 plugin.json | 1 +
 1 file changed, 1 insertion(+)

diff --git a/plugin.json b/plugin.json
index 865a240..1af2cfe 100644
--- a/plugin.json
+++ b/plugin.json
@@ -8,6 +8,7 @@
     "windows",
     "linux",
     "darwin",
+    "docker",
     "ios",
     "android"
   ],

From 6361001b8a8871f4771af7688977dc0973a4e7ff Mon Sep 17 00:00:00 2001
From: frostime <frostime@foxmail.com>
Date: Fri, 19 Jul 2024 15:17:49 +0800
Subject: [PATCH 70/82] =?UTF-8?q?=F0=9F=9A=9A=20mv=EF=BC=9A=E5=B0=86libs?=
 =?UTF-8?q?=E7=9B=AE=E5=BD=95=E4=B8=8B=E7=9A=84=E7=BB=84=E4=BB=B6=E7=A7=BB?=
 =?UTF-8?q?=E8=87=B3components=E5=AD=90=E7=9B=AE=E5=BD=95?=
MIME-Version: 1.0
Content-Type: text/plain; charset=UTF-8
Content-Transfer-Encoding: 8bit

为了更好地组织项目结构,将libs目录下的组件文件移动到新的libs/components子目录中。
同时,更新了setting-example.svelte组件中SettingPanel的导入路径,以反映新的文件位置。
---
 src/libs/{ => components}/b3-typography.svelte | 0
 src/libs/{ => components}/item-input.svelte    | 0
 src/libs/{ => components}/item-wrap.svelte     | 0
 src/libs/{ => components}/setting-panel.svelte | 0
 src/setting-example.svelte                     | 2 +-
 5 files changed, 1 insertion(+), 1 deletion(-)
 rename src/libs/{ => components}/b3-typography.svelte (100%)
 rename src/libs/{ => components}/item-input.svelte (100%)
 rename src/libs/{ => components}/item-wrap.svelte (100%)
 rename src/libs/{ => components}/setting-panel.svelte (100%)

diff --git a/src/libs/b3-typography.svelte b/src/libs/components/b3-typography.svelte
similarity index 100%
rename from src/libs/b3-typography.svelte
rename to src/libs/components/b3-typography.svelte
diff --git a/src/libs/item-input.svelte b/src/libs/components/item-input.svelte
similarity index 100%
rename from src/libs/item-input.svelte
rename to src/libs/components/item-input.svelte
diff --git a/src/libs/item-wrap.svelte b/src/libs/components/item-wrap.svelte
similarity index 100%
rename from src/libs/item-wrap.svelte
rename to src/libs/components/item-wrap.svelte
diff --git a/src/libs/setting-panel.svelte b/src/libs/components/setting-panel.svelte
similarity index 100%
rename from src/libs/setting-panel.svelte
rename to src/libs/components/setting-panel.svelte
diff --git a/src/setting-example.svelte b/src/setting-example.svelte
index 245ed03..818dc5b 100644
--- a/src/setting-example.svelte
+++ b/src/setting-example.svelte
@@ -1,6 +1,6 @@
 <script lang="ts">
     import { showMessage } from "siyuan";
-    import SettingPanel from "./libs/setting-panel.svelte";
+    import SettingPanel from "./libs/components/setting-panel.svelte";
 
     let groups: string[] = ["🌈 Default"];
     let focusGroup = groups[0];

From b739c5ad54d288b4b088681cc5ef96f4fad2c19c Mon Sep 17 00:00:00 2001
From: frostime <frostime@foxmail.com>
Date: Fri, 19 Jul 2024 15:40:50 +0800
Subject: [PATCH 71/82] =?UTF-8?q?=E2=9A=A1=EF=B8=8F=20perf:=20=E4=BC=98?=
 =?UTF-8?q?=E5=8C=96=E5=90=84=E4=B8=AA=E7=BB=84=E4=BB=B6?=
MIME-Version: 1.0
Content-Type: text/plain; charset=UTF-8
Content-Transfer-Encoding: 8bit

---
 src/index.ts                          | 33 +++++++++++++++-------
 src/libs/components/item-input.svelte | 40 +++++++++++++++++----------
 src/libs/components/item-wrap.svelte  |  8 ++++--
 src/libs/dialog.ts                    | 36 +++++++++++++++++++++++-
 4 files changed, 88 insertions(+), 29 deletions(-)

diff --git a/src/index.ts b/src/index.ts
index 62a09a7..5b50c49 100644
--- a/src/index.ts
+++ b/src/index.ts
@@ -24,6 +24,7 @@ import HelloExample from "@/hello.svelte";
 import SettingExample from "@/setting-example.svelte";
 
 import { SettingUtils } from "./libs/setting-utils";
+import { svelteDialog } from "./libs/dialog";
 
 const STORAGE_NAME = "menu-config";
 const TAB_TYPE = "custom_tab";
@@ -455,18 +456,30 @@ export default class PluginSample extends Plugin {
     }
 
     private showDialog() {
-        let dialog = new Dialog({
+        // let dialog = new Dialog({
+        //     title: `SiYuan ${Constants.SIYUAN_VERSION}`,
+        //     content: `<div id="helloPanel" class="b3-dialog__content"></div>`,
+        //     width: this.isMobile ? "92vw" : "720px",
+        //     destroyCallback() {
+        //         // hello.$destroy();
+        //     },
+        // });
+        // new HelloExample({
+        //     target: dialog.element.querySelector("#helloPanel"),
+        //     props: {
+        //         app: this.app,
+        //     }
+        // });
+        svelteDialog({
             title: `SiYuan ${Constants.SIYUAN_VERSION}`,
-            content: `<div id="helloPanel" class="b3-dialog__content"></div>`,
             width: this.isMobile ? "92vw" : "720px",
-            destroyCallback() {
-                // hello.$destroy();
-            },
-        });
-        new HelloExample({
-            target: dialog.element.querySelector("#helloPanel"),
-            props: {
-                app: this.app,
+            constructor: (container: HTMLElement) => {
+                return new HelloExample({
+                    target: container,
+                    props: {
+                        app: this.app,
+                    }
+                });
             }
         });
     }
diff --git a/src/libs/components/item-input.svelte b/src/libs/components/item-input.svelte
index 3a42b45..cbf5a7e 100644
--- a/src/libs/components/item-input.svelte
+++ b/src/libs/components/item-input.svelte
@@ -1,18 +1,10 @@
-<!--
- Copyright (c) 2024 by frostime. All Rights Reserved.
- Author       : frostime
- Date         : 2024-06-07 18:49:52
- FilePath     : /src/libs/components/input-item.svelte
- LastEditTime : 2024-06-07 20:07:58
- Description  : 
--->
 <script lang="ts">
     import { createEventDispatcher } from "svelte";
     export let type: string; // Setting Type
     export let key: string;
     export let value: any;
 
-    //Optional
+    // Optional parameters
     export let placeholder: string = "";
     export let options: { [key: string | number]: string } = {};
     export let slider: {
@@ -24,6 +16,8 @@
         label: string;
         callback?: () => void;
     } = { label: value, callback: () => {} };
+    export let fnSize: boolean = true; // If the form input is used within setting panel context, it is usually given a fixed width by a class named "fn__size200".
+    export let style: string = ""; // Custom style
 
     const dispatch = createEventDispatcher();
 
@@ -45,47 +39,61 @@
         type="checkbox"
         bind:checked={value}
         on:change={changed}
+        style={style}
     />
 {:else if type === "textinput"}
     <!-- Text Input -->
     <input
-        class="b3-text-field fn__flex-center fn__size200"
+        class:b3-text-field={true}
+        class:fn__flex-center={true}
+        class:fn__size200={fnSize}
         id={key}
         {placeholder}
         bind:value={value}
         on:change={changed}
+        style={style}
     />
 {:else if type === "textarea"}
     <textarea
         class="b3-text-field fn__block"
-        style="resize: vertical; height: 10em; white-space: nowrap;"
+        style={`resize: vertical; height: 10em; white-space: nowrap; ${style}`}
         bind:value={value}
         on:change={changed}
     />
 {:else if type === "number"}
     <input
-        class="b3-text-field fn__flex-center fn__size200"
+        class:b3-text-field={true}
+        class:fn__flex-center={true}
+        class:fn__size200={fnSize}
         id={key}
         type="number"
         bind:value={value}
         on:change={changed}
+        style={style}
     />
 {:else if type === "button"}
     <!-- Button Input -->
     <button
-        class="b3-button b3-button--outline fn__flex-center fn__size200"
+        class:b3-button={true}
+        class:b3-button--outline={true}
+        class:fn__flex-center={true}
+        class:fn__size200={fnSize}
         id={key}
         on:click={click}
+        style={style}
     >
         {button.label}
     </button>
 {:else if type === "select"}
     <!-- Dropdown select -->
     <select
-        class="b3-select fn__flex-center fn__size200"
+        class:b3-select={true}
+        class:fn__flex-center={true}
+        class:fn__size200={fnSize}
         id="iconPosition"
         bind:value={value}
         on:change={changed}
+        style={style}
     >
         {#each Object.entries(options) as [value, text]}
             <option {value}>{text}</option>
@@ -95,7 +103,8 @@
     <!-- Slider -->
     <div class="b3-tooltips b3-tooltips__n" aria-label={value}>
         <input
-            class="b3-slider fn__size200"
+            class:b3-slider={true}
+            class:fn__size200={fnSize}
             id="fontSize"
             min={slider.min}
             max={slider.max}
@@ -103,6 +112,7 @@
             type="range"
             bind:value={value}
             on:change={changed}
+            style={style}
         />
     </div>
 {/if}
diff --git a/src/libs/components/item-wrap.svelte b/src/libs/components/item-wrap.svelte
index 0d839d5..4d8092e 100644
--- a/src/libs/components/item-wrap.svelte
+++ b/src/libs/components/item-wrap.svelte
@@ -2,8 +2,8 @@
  Copyright (c) 2024 by frostime. All Rights Reserved.
  Author       : frostime
  Date         : 2024-06-01 20:03:50
- FilePath     : /src/libs/setting-item-wrap.svelte
- LastEditTime : 2024-06-07 19:14:28
+ FilePath     : /src/libs/components/item-wrap.svelte
+ LastEditTime : 2024-07-19 15:28:57
  Description  : The setting item container
 -->
 <script lang="ts">
@@ -18,7 +18,9 @@
             <span class="title">{title}</span>
             <div class="b3-label__text">{@html description}</div>
             <div class="fn__hr"></div>
-            <slot />
+            <div style="display: flex; flex-direction: column; gap: 5px; position: relative;">
+                <slot />
+            </div>
         </div>
     </div>
 {:else}
diff --git a/src/libs/dialog.ts b/src/libs/dialog.ts
index ea2b187..7e86f21 100644
--- a/src/libs/dialog.ts
+++ b/src/libs/dialog.ts
@@ -3,10 +3,11 @@
  * @Author       : frostime
  * @Date         : 2024-03-23 21:37:33
  * @FilePath     : /src/libs/dialog.ts
- * @LastEditTime : 2024-06-01 16:28:30
+ * @LastEditTime : 2024-07-19 15:34:39
  * @Description  : Kits about dialogs
  */
 import { Dialog } from "siyuan";
+import { type SvelteComponent } from "svelte";
 
 export const inputDialog = (args: {
     title: string, placeholder?: string, defaultText?: string,
@@ -119,3 +120,36 @@ export const confirmDialogSync = async (args: IConfirmDialogArgs) => {
         confirmDialog(newargs);
     });
 };
+
+
+export const simpleDialog = (args: {
+    title: string, ele: HTMLElement | DocumentFragment,
+    width?: string, height?: string,
+    callback?: () => void;
+}) => {
+    const dialog = new Dialog({
+        title: args.title,
+        content: `<div class="dialog-content" style="display: flex; height: 100%;"/>`,
+        width: args.width,
+        height: args.height,
+        destroyCallback: args.callback
+    });
+    dialog.element.querySelector(".dialog-content").appendChild(args.ele);
+    return dialog;
+}
+
+
+export const svelteDialog = (args: {
+    title: string, constructor: (container: HTMLElement) => SvelteComponent,
+    width?: string, height?: string,
+    callback?: () => void;
+}) => {
+    let container = document.createElement('div')
+    container.style.display = 'contents';
+    let component = args.constructor(container);
+    simpleDialog({...args, ele: container, callback: () => {
+        component.$destroy();
+        if (args.callback) args.callback();;
+    }});
+    return component;
+}

From 4c004d73b53e8c50bbc91e31004dad218c7e8238 Mon Sep 17 00:00:00 2001
From: frostime <frostime@foxmail.com>
Date: Fri, 19 Jul 2024 15:51:59 +0800
Subject: [PATCH 72/82] =?UTF-8?q?=F0=9F=8E=A8=20misc?=
MIME-Version: 1.0
Content-Type: text/plain; charset=UTF-8
Content-Transfer-Encoding: 8bit

---
 src/setting-example.svelte | 43 ++++++++++++++++++++++++--------------
 1 file changed, 27 insertions(+), 16 deletions(-)

diff --git a/src/setting-example.svelte b/src/setting-example.svelte
index 818dc5b..2a2c809 100644
--- a/src/setting-example.svelte
+++ b/src/setting-example.svelte
@@ -2,10 +2,10 @@
     import { showMessage } from "siyuan";
     import SettingPanel from "./libs/components/setting-panel.svelte";
 
-    let groups: string[] = ["🌈 Default"];
+    let groups: string[] = ["🌈 Group 1", "✨ Group 2"];
     let focusGroup = groups[0];
 
-    const SettingItems: ISettingItem[] = [
+    const group1Items: ISettingItem[] = [
         {
             type: 'checkbox',
             title: 'checkbox',
@@ -41,19 +41,10 @@
                 y: 'y',
                 z: 'z'
             }
-        },
-        {
-            type: 'slider',
-            title: 'slider',
-            description: 'slider',
-            key: 'd',
-            value: 50,
-            slider: {
-                min: 0,
-                max: 100,
-                step: 1
-            }
-        },
+        }
+    ];
+
+    const group2Items: ISettingItem[] = [
         {
             type: 'button',
             title: 'button',
@@ -66,6 +57,18 @@
                     showMessage('Hello, world!');
                 }
             }
+        },
+        {
+            type: 'slider',
+            title: 'slider',
+            description: 'slider',
+            key: 'd',
+            value: 50,
+            slider: {
+                min: 0,
+                max: 100,
+                step: 1
+            }
         }
     ];
 
@@ -105,7 +108,7 @@
     <div class="config__tab-wrap">
         <SettingPanel
             group={groups[0]}
-            settingItems={SettingItems}
+            settingItems={group1Items}
             display={focusGroup === groups[0]}
             on:changed={onChanged}
             on:click={({ detail }) => { console.debug("Click:", detail.key); }}
@@ -114,6 +117,14 @@
                 💡 This is our default settings.
             </div>
         </SettingPanel>
+        <SettingPanel
+            group={groups[1]}
+            settingItems={group2Items}
+            display={focusGroup === groups[1]}
+            on:changed={onChanged}
+            on:click={({ detail }) => { console.debug("Click:", detail.key); }}
+        >
+        </SettingPanel>
     </div>
 </div>
 

From 1ac9cf8ff550a95cc14bb20c4c4c91fdc77c54c4 Mon Sep 17 00:00:00 2001
From: frostime <frostime@foxmail.com>
Date: Thu, 1 Aug 2024 13:34:25 +0800
Subject: [PATCH 73/82] =?UTF-8?q?=E2=9C=A8=20feat(api):=20getFileBlob?=
MIME-Version: 1.0
Content-Type: text/plain; charset=UTF-8
Content-Transfer-Encoding: 8bit

---
 src/api.ts | 22 ++++++++++++++++++++++
 1 file changed, 22 insertions(+)

diff --git a/src/api.ts b/src/api.ts
index 0a421ce..0595371 100644
--- a/src/api.ts
+++ b/src/api.ts
@@ -335,6 +335,28 @@ export async function getFile(path: string): Promise<any> {
     });
 }
 
+
+/**
+ * fetchPost will secretly convert data into json, this func merely return Blob
+ * @param endpoint 
+ * @returns 
+ */
+export const getFileBlob = async (path: string): Promise<Blob | null> => {
+    const endpoint = '/api/file/getFile'
+    let response = await fetch(endpoint, {
+        method: 'POST',
+        body: JSON.stringify({
+            path: path
+        })
+    });
+    if (!response.ok) {
+        return null;
+    }
+    let data = await response.blob();
+    return data;
+}
+
+
 export async function putFile(path: string, isDir: boolean, file: any) {
     let form = new FormData();
     form.append('path', path);

From da4c4ced8f4b2951e4a81f5560349f2623319447 Mon Sep 17 00:00:00 2001
From: frostime <frostime@foxmail.com>
Date: Fri, 9 Aug 2024 21:49:29 +0800
Subject: [PATCH 74/82] =?UTF-8?q?=E2=9C=A8=20feat:=20=E6=9B=B4=E6=96=B0=20?=
 =?UTF-8?q?Form=20=E7=BB=84=E4=BB=B6?=
MIME-Version: 1.0
Content-Type: text/plain; charset=UTF-8
Content-Transfer-Encoding: 8bit

---
 .../{item-input.svelte => Form/form-input.svelte}   |  0
 .../{item-wrap.svelte => Form/form-wrap.svelte}     |  0
 src/libs/components/Form/index.ts                   |  6 ++++++
 src/libs/components/setting-panel.svelte            | 13 ++++++-------
 4 files changed, 12 insertions(+), 7 deletions(-)
 rename src/libs/components/{item-input.svelte => Form/form-input.svelte} (100%)
 rename src/libs/components/{item-wrap.svelte => Form/form-wrap.svelte} (100%)
 create mode 100644 src/libs/components/Form/index.ts

diff --git a/src/libs/components/item-input.svelte b/src/libs/components/Form/form-input.svelte
similarity index 100%
rename from src/libs/components/item-input.svelte
rename to src/libs/components/Form/form-input.svelte
diff --git a/src/libs/components/item-wrap.svelte b/src/libs/components/Form/form-wrap.svelte
similarity index 100%
rename from src/libs/components/item-wrap.svelte
rename to src/libs/components/Form/form-wrap.svelte
diff --git a/src/libs/components/Form/index.ts b/src/libs/components/Form/index.ts
new file mode 100644
index 0000000..a5c81c0
--- /dev/null
+++ b/src/libs/components/Form/index.ts
@@ -0,0 +1,6 @@
+import FormInput from './form-input.svelte';
+import FormWrap from './form-wrap.svelte';
+
+const Form = { Wrap: FormWrap, Input: FormInput };
+export default Form;
+export { FormInput, FormWrap };
diff --git a/src/libs/components/setting-panel.svelte b/src/libs/components/setting-panel.svelte
index 6a304a0..783e2b6 100644
--- a/src/libs/components/setting-panel.svelte
+++ b/src/libs/components/setting-panel.svelte
@@ -2,14 +2,13 @@
  Copyright (c) 2023 by frostime All Rights Reserved.
  Author       : frostime
  Date         : 2023-07-01 19:23:50
- FilePath     : /src/libs/setting-panel.svelte
- LastEditTime : 2024-06-08 18:25:34
+ FilePath     : /src/libs/components/setting-panel.svelte
+ LastEditTime : 2024-08-09 21:41:07
  Description  : 
 -->
 <script lang="ts">
     import { createEventDispatcher } from "svelte";
-    import ItemWrap from "./item-wrap.svelte";
-    import InputItem from "./item-input.svelte";
+    import Form from './Form';
 
     export let group: string;
     export let settingItems: ISettingItem[];
@@ -31,12 +30,12 @@
 <div class="config__tab-container {fn__none}" data-name={group}>
     <slot />
     {#each settingItems as item (item.key)}
-        <ItemWrap
+        <Form.Wrap
             title={item.title}
             description={item.description}
             direction={item?.direction}
         > 
-            <InputItem
+            <Form.Input
                 type={item.type}
                 key={item.key}
                 bind:value={item.value}
@@ -47,6 +46,6 @@
                 on:click={onClick}
                 on:changed={onChanged}
             />
-        </ItemWrap>
+        </Form.Wrap>
     {/each}
 </div>
\ No newline at end of file

From e345e18613bee6e697e14dac57c691502a026be2 Mon Sep 17 00:00:00 2001
From: frostime <frostime@foxmail.com>
Date: Fri, 6 Sep 2024 19:50:50 +0800
Subject: [PATCH 75/82] =?UTF-8?q?=F0=9F=94=A8=20refactor:=20=E5=88=9B?=
 =?UTF-8?q?=E5=BB=BA=20dir=20=E7=AC=A6=E5=8F=B7=E9=93=BE=E6=8E=A5=EF=BC=9B?=
 =?UTF-8?q?=E5=B9=B6=E7=BC=96=E5=86=99=20ps=20=E8=84=9A=E6=9C=AC=E7=94=A8?=
 =?UTF-8?q?=E4=BA=8E=E8=8E=B7=E5=8F=96=20windows=20=E7=AE=A1=E7=90=86?=
 =?UTF-8?q?=E5=91=98=E6=9D=83=E9=99=90?=
MIME-Version: 1.0
Content-Type: text/plain; charset=UTF-8
Content-Transfer-Encoding: 8bit

https://github.com/siyuan-note/siyuan/issues/12399
---
 README.md                |  50 +++++-----
 README_zh_CN.md          |  39 +++++---
 package.json             |   3 +-
 scripts/elevate.ps1      |  24 +++++
 scripts/make_dev_link.js | 188 +++++++------------------------------
 scripts/make_install.js  | 196 +++++++--------------------------------
 scripts/utils.js         | 182 ++++++++++++++++++++++++++++++++++++
 7 files changed, 327 insertions(+), 355 deletions(-)
 create mode 100644 scripts/elevate.ps1
 create mode 100644 scripts/utils.js

diff --git a/README.md b/README.md
index a675463..f0b2a1c 100644
--- a/README.md
+++ b/README.md
@@ -20,15 +20,21 @@
 
 ## Get started
 
-1. Make a copy of this repo as a template with the `Use this template` button, please note that the repo name must be the same as the plugin name, the default branch must be `main`
+1. Use the <kbd>Use this template</kbd> button to make a copy of this repo as a template. Note that the repository name should match the plugin name, and the default branch must be `main`.
+2. Clone your repository to the local development folder.
+    * Note: Unlike `plugin-sample`, this example does not recommend directly downloading the code to `{workspace}/data/plugins/`.
+3. Install [NodeJS](https://nodejs.org/en/download) and [pnpm](https://pnpm.io/installation), then run `pnpm i` in the development folder to install the required dependencies.
+4. Run the `pnpm run make-link` command to create a symbolic link (Windows developers, please refer to the "make-link on Windows" section below).
+5. Execute `pnpm run dev` for real-time compilation.
+6. Open the marketplace in SiYuan and enable the plugin in the download tab.
 
-2. Clone your repo to a local development folder at any place
-    - Notice: we **don't recommand** you to place the folder under your `{workspace}/data/plugins/` folder.
+### Setting the Target Directory for the make-link Command
 
-3. Install NodeJS and pnpm, then run pnpm i in the command line under your repo folder
-4. **Auto create development symbolic links**
-    - Make sure that SiYuan is running
-    - Run `pnpm run make-link`, the script will detect all the siyuan workspace, please select the targe workspace and the script will automatically create the symbolic link under the `{workspace}/data/plugins/` folder
+The `make-link` command creates a symbolic link that binds your `dev` directory to the SiYuan plugin directory. You can configure the target SiYuan workspace and create the symbolic link in three ways:
+
+1. **Select Workspace**
+    - Open SiYuan, ensure the SiYuan kernel is running.
+    - Run `pnpm run make-link`, the script will automatically detect all SiYuan workspaces, please manually enter the number to select the workspace.
         ```bash
         >>> pnpm run make-link
         > plugin-sample-vite-svelte@0.0.3 make-link H:\SrcCode\开源项目\plugin-sample-vite-svelte
@@ -42,22 +48,24 @@
         Got target directory: H:\Media\SiYuan/data/plugins
         Done! Created symlink H:\Media\SiYuan/data/plugins/plugin-sample-vite-svelte
         ```
-4. **Manually create development symbolic links**
-    - Open `./scripts/make_dev_link.js` file, set `targetDir` to your SiYuan plugin directory `<siyuan workspace>/data/plugins`
-    - Run `pnpm run make-link`, succeed if following message is shown:
-      ```bash
-      >>> pnpm run make-link
-      > plugin-sample-vite-svelte@0.0.1 make-link H:\SrcCode\plugin-sample-vite-svelte
-      > node ./scripts/make_dev_link.js
+2. **Manually Configure Target Directory**
+    - Open the `./scripts/make_dev_link.js` file, change `targetDir` to the SiYuan plugin directory `<siyuan workspace>/data/plugins`.
+    - Run the `pnpm run make-link` command. If you see a message similar to the one below, it indicates successful creation:
 
-      Done! Created symlink H:/SiYuanDevSpace/data/plugins/plugin-sample-vite-svelte
-      ```
-5. **Create development symbolic links by using environment variable**
-    - You can set environment variable `SIYUAN_PLUGIN_DIR` as `/data/plugins`
-6. Execute pnpm run dev for real-time compilation
-7. Open SiYuan marketplace and enable plugin in downloaded tab
+3. **Set Environment Variable to Create Symbolic Link**
+    - Set the system environment variable `SIYUAN_PLUGIN_DIR` to the path `workspace/data/plugins`.
 
->  Notice: as the `make-link` script rely on the `fetch` function, please **ensure that at least version v18 of nodejs is installed** if you want to use make-link script.
+### make-link on Windows
+
+Due to SiYuan upgrading to Go 1.23, the old version of junction links cannot be recognized normally on Windows, so it has been changed to create `dir` symbolic links.
+
+> https://github.com/siyuan-note/siyuan/issues/12399
+
+However, creating directory symbolic links on Windows using NodeJs may require administrator privileges. You have the following options:
+
+1. Run `pnpm run make-link` in a command line with administrator privileges.
+2. Configure Windows settings, enable developer mode in [System Settings - Update & Security - Developer Mode] then run `pnpm run make-link`.
+3. Run `pnpm run make-link-win`, this command will use a PowerShell script to request administrator privileges, requiring the system to enable PowerShell script execution permissions.
 
 ## I18n
 
diff --git a/README_zh_CN.md b/README_zh_CN.md
index d0f153c..e60f454 100644
--- a/README_zh_CN.md
+++ b/README_zh_CN.md
@@ -18,11 +18,19 @@
 
 ## 开始
 
-1. 通过 <kbd>Use this template</kbd> 按钮将该库文件复制到你自己的库中,请注意库名必须和插件名称一致,默认分支必须为 `main`
+1. 通过 <kbd>Use this template</kbd> 按钮将该库文件复制到你自己的库中,请注意库名和插件名称一致,默认分支必须为 `main`
 2. 将你的库克隆到本地开发文件夹中
     * 注意: 同 `plugin-sample` 不同, 本样例并不推荐直接把代码下载到 `{workspace}/data/plugins/`
 3. 安装 [NodeJS](https://nodejs.org/en/download) 和 [pnpm](https://pnpm.io/installation),然后在开发文件夹下执行 `pnpm i` 安装所需要的依赖
-3. **自动创建符号链接**
+4. 运行 `pnpm run make-link` 命令创建符号链接 (Windows 下的开发者请参阅下方「Windows 下的 make-link」小节)
+5. 执行 `pnpm run dev` 进行实时编译
+6.  在思源中打开集市并在下载选项卡中启用插件
+
+### 设置 make-link 命令的目标目录
+
+make-link 命令会创建符号链接将你的 `dev` 目录绑定到思源的插件目录下。你可以有三种方式来配置目标的思源工作空间并创建符号链接:
+
+1. **选择工作空间**
     - 打开思源笔记, 确保思源内核正在运行
     - 运行 `pnpm run make-link`, 脚本会自动检测所有思源的工作空间, 请在命令行中手动输入序号以选择工作空间
         ```bash
@@ -38,23 +46,26 @@
         Got target directory: H:\Media\SiYuan/data/plugins
         Done! Created symlink H:\Media\SiYuan/data/plugins/plugin-sample-vite-svelte
         ```
-4. **手动创建符号链接**
+2. **手动配置目标目录**
     - 打开 `./scripts/make_dev_link.js` 文件,更改 `targetDir` 为思源的插件目录 `<siyuan workspace>/data/plugins`
     - 运行 `pnpm run make-link` 命令, 如果看到类似以下的消息,说明创建成功:
-      ```bash
-      ❯❯❯ pnpm run make-link
-      > plugin-sample-vite-svelte@0.0.1 make-link H:\SrcCode\plugin-sample-vite-svelte
-      > node ./scripts/make_dev_link.js
 
-      Done! Created symlink H:/SiYuanDevSpace/data/plugins/plugin-sample-vite-svelte
-      ```
-5. **设置环境变量创建符号链接**
-    - 你也可以设置系统的环境变量 `SIYUAN_PLUGIN_DIR` 为 `/data/plugins` 的路径
-6. 执行 `pnpm run dev` 进行实时编译
-7. 在思源中打开集市并在下载选项卡中启用插件
+3. **设置环境变量创建符号链接**
+    - 设置系统的环境变量 `SIYUAN_PLUGIN_DIR` 为 `工作空间/data/plugins` 的路径
 
 
-> 注意由于使用的 make-link 脚本依赖于 `fetch`,所以如果想要使用 make-link **请保证至少安装 v18 版本的 nodejs**
+### Windows 下的 make-link
+
+由于思源升级了 Go 1.23,旧版创建的 junction link 在 windows 下无法被正常识别,故而改为创建 `dir` 符号链接。
+
+> https://github.com/siyuan-note/siyuan/issues/12399
+
+
+不过 Windows 下使用 NodeJs 创建目录符号链接可能需要管理员权限,你可以有如下几种选择:
+
+1. 在具有管理员权限的命令行中运行 `pnpm run make-link`
+2. 配置 Windows 设置,在 [系统设置-更新与安全-开发者模式] 中启用开发者模式,然后再运行 `pnpm run make-link`
+3. 运行 `pnpm run make-link-win`,该命令会使用一个 powershell 脚本来寻求管理员权限,需要在系统中开启 PowerShell 脚本执行权限
 
 
 ## 国际化
diff --git a/package.json b/package.json
index fd49b9c..971fd74 100644
--- a/package.json
+++ b/package.json
@@ -8,7 +8,8 @@
   "author": "frostime",
   "license": "MIT",
   "scripts": {
-    "make-link": "node  --no-warnings ./scripts/make_dev_link.js",
+    "make-link": "node --no-warnings ./scripts/make_dev_link.js",
+    "make-link-win": "powershell.exe -NoProfile -ExecutionPolicy Bypass -File ./scripts/elevate.ps1 -scriptPath ./scripts/make_dev_link.js",
     "dev": "vite build --watch",
     "build": "vite build",
     "make-install": "vite build && node --no-warnings ./scripts/make_install.js"
diff --git a/scripts/elevate.ps1 b/scripts/elevate.ps1
new file mode 100644
index 0000000..151b8ba
--- /dev/null
+++ b/scripts/elevate.ps1
@@ -0,0 +1,24 @@
+# Copyright (c) 2024 by frostime. All Rights Reserved.
+# @Author       : frostime
+# @Date         : 2024-09-06 19:15:53
+# @FilePath     : /scripts/elevate.ps1
+# @LastEditTime : 2024-09-06 19:39:13
+# @Description  : Force to elevate the script to admin privilege.
+
+param (
+    [string]$scriptPath
+)
+
+$scriptDir = Split-Path -Parent $MyInvocation.MyCommand.Definition
+$projectDir = Split-Path -Parent $scriptDir
+
+if (-NOT ([Security.Principal.WindowsPrincipal] [Security.Principal.WindowsIdentity]::GetCurrent()).IsInRole([Security.Principal.WindowsBuiltInRole] "Administrator")) {
+    $args = "-NoProfile -ExecutionPolicy Bypass -File `"" + $MyInvocation.MyCommand.Path + "`" -scriptPath `"" + $scriptPath + "`""
+    Start-Process powershell.exe -Verb RunAs -ArgumentList $args -WorkingDirectory $projectDir
+    exit
+}
+
+Set-Location -Path $projectDir
+& node $scriptPath
+
+pause
diff --git a/scripts/make_dev_link.js b/scripts/make_dev_link.js
index 845d298..2be3d2b 100644
--- a/scripts/make_dev_link.js
+++ b/scripts/make_dev_link.js
@@ -1,106 +1,29 @@
+/*
+ * Copyright (c) 2024 by frostime. All Rights Reserved.
+ * @Author       : frostime
+ * @Date         : 2023-07-15 15:31:31
+ * @FilePath     : /scripts/make_dev_link.js
+ * @LastEditTime : 2024-09-06 18:13:53
+ * @Description  : 
+ */
+// make_dev_link.js
 import fs from 'fs';
-import http from 'node:http';
-import readline  from 'node:readline';
+import { log, error, getSiYuanDir, chooseTarget, getThisPluginName, makeSymbolicLink } from './utils.js';
 
-
-//************************************ Write you dir here ************************************
-
-//Please write the "workspace/data/plugins" directory here
-//请在这里填写你的 "workspace/data/plugins" 目录
 let targetDir = '';
-//Like this
-// let targetDir = `H:\\SiYuanDevSpace\\data\\plugins`;
-//********************************************************************************************
-
-const log = (info) => console.log(`\x1B[36m%s\x1B[0m`, info);
-const error = (info) => console.log(`\x1B[31m%s\x1B[0m`, info);
-
-let POST_HEADER = {
-    // "Authorization": `Token ${token}`,
-    "Content-Type": "application/json",
-}
-
-async function myfetch(url, options) {
-    //使用 http 模块,从而兼容那些不支持 fetch 的 nodejs 版本
-    return new Promise((resolve, reject) => {
-        let req = http.request(url, options, (res) => {
-            let data = '';
-            res.on('data', (chunk) => {
-                data += chunk;
-            });
-            res.on('end', () => {
-                resolve({
-                    ok: true,
-                    status: res.statusCode,
-                    json: () => JSON.parse(data)
-                });
-            });
-        });
-        req.on('error', (e) => {
-            reject(e);
-        });
-        req.end();
-    });
-}
-
-async function getSiYuanDir() {
-    let url = 'http://127.0.0.1:6806/api/system/getWorkspaces';
-    let conf = {};
-    try {
-        let response = await myfetch(url, {
-            method: 'POST',
-            headers: POST_HEADER
-        });
-        if (response.ok) {
-            conf = await response.json();
-        } else {
-            error(`\tHTTP-Error: ${response.status}`);
-            return null;
-        }
-    } catch (e) {
-        error(`\tError: ${e}`);
-        error("\tPlease make sure SiYuan is running!!!");
-        return null;
-    }
-    return conf.data;
-}
-
-async function chooseTarget(workspaces) {
-    let count = workspaces.length;
-    log(`>>> Got ${count} SiYuan ${count > 1 ? 'workspaces' : 'workspace'}`)
-    for (let i = 0; i < workspaces.length; i++) {
-        log(`\t[${i}] ${workspaces[i].path}`);
-    }
-
-    if (count == 1) {
-        return `${workspaces[0].path}/data/plugins`;
-    } else {
-        const rl = readline.createInterface({
-            input: process.stdin,
-            output: process.stdout
-        });
-        let index = await new Promise((resolve, reject) => {
-            rl.question(`\tPlease select a workspace[0-${count-1}]: `, (answer) => {
-                resolve(answer);
-            });
-        });
-        rl.close();
-        return `${workspaces[index].path}/data/plugins`;
-    }
-}
-
-log('>>> Try to visit constant "targetDir" in make_dev_link.js...')
 
+/**
+ * 1. Get the parent directory to install the plugin
+ */
+log('>>> Try to visit constant "targetDir" in make_dev_link.js...');
 if (targetDir === '') {
-    log('>>> Constant "targetDir" is empty, try to get SiYuan directory automatically....')
+    log('>>> Constant "targetDir" is empty, try to get SiYuan directory automatically....');
     let res = await getSiYuanDir();
-    
-    if (res === null || res === undefined || res.length === 0) {
-        log('>>> Can not get SiYuan directory automatically, try to visit environment variable "SIYUAN_PLUGIN_DIR"....');
 
-        // console.log(process.env)
+    if (!res || res.length === 0) {
+        log('>>> Can not get SiYuan directory automatically, try to visit environment variable "SIYUAN_PLUGIN_DIR"....');
         let env = process.env?.SIYUAN_PLUGIN_DIR;
-        if (env !== undefined && env !== null && env !== '') {
+        if (env) {
             targetDir = env;
             log(`\tGot target directory from environment variable "SIYUAN_PLUGIN_DIR": ${targetDir}`);
         } else {
@@ -111,76 +34,33 @@ if (targetDir === '') {
         targetDir = await chooseTarget(res);
     }
 
-
     log(`>>> Successfully got target directory: ${targetDir}`);
 }
-
-//Check
 if (!fs.existsSync(targetDir)) {
-    error(`Failed! plugin directory not exists: "${targetDir}"`);
-    error(`Please set the plugin directory in scripts/make_dev_link.js`);
+    error(`Failed! Plugin directory not exists: "${targetDir}"`);
+    error('Please set the plugin directory in scripts/make_dev_link.js');
     process.exit(1);
 }
 
-
-//check if plugin.json exists
-if (!fs.existsSync('./plugin.json')) {
-    //change dir to parent
-    process.chdir('../');
-    if (!fs.existsSync('./plugin.json')) {
-        error('Failed! plugin.json not found');
-        process.exit(1);
-    }
-}
-
-//load plugin.json
-const plugin = JSON.parse(fs.readFileSync('./plugin.json', 'utf8'));
-const name = plugin?.name;
-if (!name || name === '') {
-    error('Failed! Please set plugin name in plugin.json');
-    process.exit(1);
-}
-
-//dev directory
+/**
+ * 2. The dev directory, which contains the compiled plugin code
+ */
 const devDir = `${process.cwd()}/dev`;
-//mkdir if not exists
 if (!fs.existsSync(devDir)) {
     fs.mkdirSync(devDir);
 }
 
-function cmpPath(path1, path2) {
-    path1 = path1.replace(/\\/g, '/');
-    path2 = path2.replace(/\\/g, '/');
-    // sepertor at tail
-    if (path1[path1.length - 1] !== '/') {
-        path1 += '/';
-    }
-    if (path2[path2.length - 1] !== '/') {
-        path2 += '/';
-    }
-    return path1 === path2;
-}
 
+/**
+ * 3. The target directory to make symbolic link to dev directory
+ */
+const name = getThisPluginName();
+if (name === null) {
+    process.exit(1);
+}
 const targetPath = `${targetDir}/${name}`;
-//如果已经存在,就退出
-if (fs.existsSync(targetPath)) {
-    let isSymbol = fs.lstatSync(targetPath).isSymbolicLink();
-
-    if (isSymbol) {
-        let srcPath = fs.readlinkSync(targetPath);
-        
-        if (cmpPath(srcPath, devDir)) {
-            log(`Good! ${targetPath} is already linked to ${devDir}`);
-        } else {
-            error(`Error! Already exists symbolic link ${targetPath}\nBut it links to ${srcPath}`);
-        }
-    } else {
-        error(`Failed! ${targetPath} already exists and is not a symbolic link`);
-    }
-
-} else {
-    //创建软链接
-    fs.symlinkSync(devDir, targetPath, 'junction');
-    log(`Done! Created symlink ${targetPath}`);
-}
 
+/**
+ * 4. Make symbolic link
+ */
+makeSymbolicLink(devDir, targetPath);
diff --git a/scripts/make_install.js b/scripts/make_install.js
index 9c2cd9b..cb2a4ac 100644
--- a/scripts/make_install.js
+++ b/scripts/make_install.js
@@ -1,191 +1,57 @@
+/*
+ * Copyright (c) 2024 by frostime. All Rights Reserved.
+ * @Author       : frostime
+ * @Date         : 2024-03-28 20:03:59
+ * @FilePath     : /scripts/make_install.js
+ * @LastEditTime : 2024-09-06 18:08:19
+ * @Description  : 
+ */
+// make_install.js
 import fs from 'fs';
-import path from 'path';
-import http from 'node:http';
-import readline  from 'node:readline';
+import { log, error, getSiYuanDir, chooseTarget, copyDirectory, getThisPluginName } from './utils.js';
 
+let targetDir = '';
 
-//************************************ Write you dir here ************************************
-
-let targetDir = ''; // the target directory of the plugin, '*/data/plugin'
-//********************************************************************************************
-
-const log = (info) => console.log(`\x1B[36m%s\x1B[0m`, info);
-const error = (info) => console.log(`\x1B[31m%s\x1B[0m`, info);
-
-let POST_HEADER = {
-    // "Authorization": `Token ${token}`,
-    "Content-Type": "application/json",
-}
-
-async function myfetch(url, options) {
-    //使用 http 模块,从而兼容那些不支持 fetch 的 nodejs 版本
-    return new Promise((resolve, reject) => {
-        let req = http.request(url, options, (res) => {
-            let data = '';
-            res.on('data', (chunk) => {
-                data += chunk;
-            });
-            res.on('end', () => {
-                resolve({
-                    ok: true,
-                    status: res.statusCode,
-                    json: () => JSON.parse(data)
-                });
-            });
-        });
-        req.on('error', (e) => {
-            reject(e);
-        });
-        req.end();
-    });
-}
-
-async function getSiYuanDir() {
-    let url = 'http://127.0.0.1:6806/api/system/getWorkspaces';
-    let conf = {};
-    try {
-        let response = await myfetch(url, {
-            method: 'POST',
-            headers: POST_HEADER
-        });
-        if (response.ok) {
-            conf = await response.json();
-        } else {
-            error(`\tHTTP-Error: ${response.status}`);
-            return null;
-        }
-    } catch (e) {
-        error(`\tError: ${e}`);
-        error("\tPlease make sure SiYuan is running!!!");
-        return null;
-    }
-    return conf.data;
-}
-
-async function chooseTarget(workspaces) {
-    let count = workspaces.length;
-    log(`>>> Got ${count} SiYuan ${count > 1 ? 'workspaces' : 'workspace'}`)
-    for (let i = 0; i < workspaces.length; i++) {
-        log(`\t[${i}] ${workspaces[i].path}`);
-    }
-
-    if (count == 1) {
-        return `${workspaces[0].path}/data/plugins`;
-    } else {
-        const rl = readline.createInterface({
-            input: process.stdin,
-            output: process.stdout
-        });
-        let index = await new Promise((resolve, reject) => {
-            rl.question(`\tPlease select a workspace[0-${count-1}]: `, (answer) => {
-                resolve(answer);
-            });
-        });
-        rl.close();
-        return `${workspaces[index].path}/data/plugins`;
-    }
-}
-
-log('>>> Try to visit constant "targetDir" in make_install.js...')
-
+/**
+ * 1. Get the parent directory to install the plugin
+ */
+log('>>> Try to visit constant "targetDir" in make_install.js...');
 if (targetDir === '') {
-    log('>>> Constant "targetDir" is empty, try to get SiYuan directory automatically....')
+    log('>>> Constant "targetDir" is empty, try to get SiYuan directory automatically....');
     let res = await getSiYuanDir();
-    
+
     if (res === null || res === undefined || res.length === 0) {
         error('>>> Can not get SiYuan directory automatically');
-
         process.exit(1);
     } else {
         targetDir = await chooseTarget(res);
     }
-
     log(`>>> Successfully got target directory: ${targetDir}`);
 }
-
-//Check
 if (!fs.existsSync(targetDir)) {
-    error(`Failed! plugin directory not exists: "${targetDir}"`);
-    error(`Please set the plugin directory in scripts/make_install.js`);
-    process.exit(1);
-}
-
-
-//check if plugin.json exists
-if (!fs.existsSync('./plugin.json')) {
-    //change dir to parent
-    process.chdir('../');
-    if (!fs.existsSync('./plugin.json')) {
-        error('Failed! plugin.json not found');
-        process.exit(1);
-    }
-}
-
-//load plugin.json
-const plugin = JSON.parse(fs.readFileSync('./plugin.json', 'utf8'));
-const name = plugin?.name;
-if (!name || name === '') {
-    error('Failed! Please set plugin name in plugin.json');
+    error(`Failed! Plugin directory not exists: "${targetDir}"`);
+    error('Please set the plugin directory in scripts/make_install.js');
     process.exit(1);
 }
 
+/**
+ * 2. The dist directory, which contains the compiled plugin code
+ */
 const distDir = `${process.cwd()}/dist`;
-//mkdir if not exists
 if (!fs.existsSync(distDir)) {
     fs.mkdirSync(distDir);
 }
 
-function cmpPath(path1, path2) {
-    path1 = path1.replace(/\\/g, '/');
-    path2 = path2.replace(/\\/g, '/');
-    // sepertor at tail
-    if (path1[path1.length - 1] !== '/') {
-        path1 += '/';
-    }
-    if (path2[path2.length - 1] !== '/') {
-        path2 += '/';
-    }
-    return path1 === path2;
+/**
+ * 3. The target directory to install the plugin
+ */
+const name = getThisPluginName();
+if (name === null) {
+    process.exit(1);
 }
-
 const targetPath = `${targetDir}/${name}`;
 
-
-function copyDirectory(srcDir, dstDir) {
-    if (!fs.existsSync(dstDir)) {
-        fs.mkdirSync(dstDir);
-        log(`Created directory ${dstDir}`);
-    }
-    //将 distDir 下的所有文件复制到 targetPath
-    fs.readdir(srcDir, { withFileTypes: true }, (err, files) => {
-        if (err) {
-            error('Error reading source directory:', err);
-            return;
-        }
-
-        // 遍历源目录中的所有文件和子目录
-        files.forEach((file) => {
-            const src = path.join(srcDir, file.name);
-            const dst = path.join(dstDir, file.name);
-
-            // 判断当前项是文件还是目录
-            if (file.isDirectory()) {
-                // 如果是目录,则递归调用复制函数复制子目录
-                copyDirectory(src, dst);
-            } else {
-                // 如果是文件,则复制文件到目标目录
-                fs.copyFile(src, dst, (err) => {
-                if (err) {
-                    error('Error copying file:' + err);
-                } else {
-                    log(`Copied file: ${src} --> ${dst}`);
-                }
-                });
-            }
-        });
-        log(`Copied ${distDir} to ${targetPath}`);
-    });
-}
+/**
+ * 4. Copy the compiled plugin code to the target directory
+ */
 copyDirectory(distDir, targetPath);
-
-
diff --git a/scripts/utils.js b/scripts/utils.js
new file mode 100644
index 0000000..210b6b1
--- /dev/null
+++ b/scripts/utils.js
@@ -0,0 +1,182 @@
+/*
+ * Copyright (c) 2024 by frostime. All Rights Reserved.
+ * @Author       : frostime
+ * @Date         : 2024-09-06 17:42:57
+ * @FilePath     : /scripts/utils.js
+ * @LastEditTime : 2024-09-06 19:23:12
+ * @Description  : 
+ */
+// common.js
+import fs from 'fs';
+import path from 'node:path';
+import http from 'node:http';
+import readline from 'node:readline';
+
+// Logging functions
+export const log = (info) => console.log(`\x1B[36m%s\x1B[0m`, info);
+export const error = (info) => console.log(`\x1B[31m%s\x1B[0m`, info);
+
+// HTTP POST headers
+export const POST_HEADER = {
+    "Content-Type": "application/json",
+};
+
+// Fetch function compatible with older Node.js versions
+export async function myfetch(url, options) {
+    return new Promise((resolve, reject) => {
+        let req = http.request(url, options, (res) => {
+            let data = '';
+            res.on('data', (chunk) => {
+                data += chunk;
+            });
+            res.on('end', () => {
+                resolve({
+                    ok: true,
+                    status: res.statusCode,
+                    json: () => JSON.parse(data)
+                });
+            });
+        });
+        req.on('error', (e) => {
+            reject(e);
+        });
+        req.end();
+    });
+}
+
+/**
+ * Fetch SiYuan workspaces from port 6806
+ * @returns {Promise<Object | null>}
+ */
+export async function getSiYuanDir() {
+    let url = 'http://127.0.0.1:6806/api/system/getWorkspaces';
+    let conf = {};
+    try {
+        let response = await myfetch(url, {
+            method: 'POST',
+            headers: POST_HEADER
+        });
+        if (response.ok) {
+            conf = await response.json();
+        } else {
+            error(`\tHTTP-Error: ${response.status}`);
+            return null;
+        }
+    } catch (e) {
+        error(`\tError: ${e}`);
+        error("\tPlease make sure SiYuan is running!!!");
+        return null;
+    }
+    return conf?.data; // 保持原始返回值
+}
+
+/**
+ * Choose target workspace
+ * @param {{path: string}[]} workspaces
+ * @returns {string} The path of the selected workspace
+ */
+export async function chooseTarget(workspaces) {
+    let count = workspaces.length;
+    log(`>>> Got ${count} SiYuan ${count > 1 ? 'workspaces' : 'workspace'}`);
+    workspaces.forEach((workspace, i) => {
+        log(`\t[${i}] ${workspace.path}`);
+    });
+
+    if (count === 1) {
+        return `${workspaces[0].path}/data/plugins`;
+    } else {
+        const rl = readline.createInterface({
+            input: process.stdin,
+            output: process.stdout
+        });
+        let index = await new Promise((resolve) => {
+            rl.question(`\tPlease select a workspace[0-${count - 1}]: `, (answer) => {
+                resolve(answer);
+            });
+        });
+        rl.close();
+        return `${workspaces[index].path}/data/plugins`;
+    }
+}
+
+/**
+ * Check if two paths are the same
+ * @param {string} path1
+ * @param {string} path2
+ * @returns {boolean}
+ */
+export function cmpPath(path1, path2) {
+    path1 = path1.replace(/\\/g, '/');
+    path2 = path2.replace(/\\/g, '/');
+    if (path1[path1.length - 1] !== '/') {
+        path1 += '/';
+    }
+    if (path2[path2.length - 1] !== '/') {
+        path2 += '/';
+    }
+    return path1 === path2;
+}
+
+export function getThisPluginName() {
+    if (!fs.existsSync('./plugin.json')) {
+        process.chdir('../');
+        if (!fs.existsSync('./plugin.json')) {
+            error('Failed! plugin.json not found');
+            return null;
+        }
+    }
+
+    const plugin = JSON.parse(fs.readFileSync('./plugin.json', 'utf8'));
+    const name = plugin?.name;
+    if (!name) {
+        error('Failed! Please set plugin name in plugin.json');
+        return null;
+    }
+
+    return name;
+}
+
+export function copyDirectory(srcDir, dstDir) {
+    if (!fs.existsSync(dstDir)) {
+        fs.mkdirSync(dstDir);
+        log(`Created directory ${dstDir}`);
+    }
+
+    fs.readdirSync(srcDir, { withFileTypes: true }).forEach((file) => {
+        const src = path.join(srcDir, file.name);
+        const dst = path.join(dstDir, file.name);
+
+        if (file.isDirectory()) {
+            copyDirectory(src, dst);
+        } else {
+            fs.copyFileSync(src, dst);
+            log(`Copied file: ${src} --> ${dst}`);
+        }
+    });
+    log(`All files copied!`);
+}
+
+
+export function makeSymbolicLink(srcPath, targetPath) {
+    if (!fs.existsSync(targetPath)) {
+        // fs.symlinkSync(srcPath, targetPath, 'junction');
+        //Go 1.23 no longer supports junctions as symlinks
+        //Please refer to https://github.com/siyuan-note/siyuan/issues/12399
+        fs.symlinkSync(srcPath, targetPath, 'dir');
+        log(`Done! Created symlink ${targetPath}`);
+        return;
+    }
+
+    //Check the existed target path
+    let isSymbol = fs.lstatSync(targetPath).isSymbolicLink();
+    if (!isSymbol) {
+        error(`Failed! ${targetPath} already exists and is not a symbolic link`);
+        return;
+    }
+    let existedPath = fs.readlinkSync(targetPath);
+    if (cmpPath(existedPath, srcPath)) {
+        log(`Good! ${targetPath} is already linked to ${srcPath}`);
+    } else {
+        error(`Error! Already exists symbolic link ${targetPath}\nBut it links to ${existedPath}`);
+    }
+}

From 63cc3ac0c2de3fcdcca3e4078373b9a23237bcd0 Mon Sep 17 00:00:00 2001
From: frostime <frostime@foxmail.com>
Date: Sun, 8 Sep 2024 14:05:54 +0800
Subject: [PATCH 76/82] =?UTF-8?q?=F0=9F=94=A7=20ci:=20add=20a=20script=20t?=
 =?UTF-8?q?o=20update=20version?=
MIME-Version: 1.0
Content-Type: text/plain; charset=UTF-8
Content-Transfer-Encoding: 8bit

---
 package.json              |   1 +
 scripts/update_version.js | 141 ++++++++++++++++++++++++++++++++++++++
 2 files changed, 142 insertions(+)
 create mode 100644 scripts/update_version.js

diff --git a/package.json b/package.json
index 971fd74..03a2a88 100644
--- a/package.json
+++ b/package.json
@@ -11,6 +11,7 @@
     "make-link": "node --no-warnings ./scripts/make_dev_link.js",
     "make-link-win": "powershell.exe -NoProfile -ExecutionPolicy Bypass -File ./scripts/elevate.ps1 -scriptPath ./scripts/make_dev_link.js",
     "dev": "vite build --watch",
+    "update-version": "node --no-warnings ./scripts/update_version.js",
     "build": "vite build",
     "make-install": "vite build && node --no-warnings ./scripts/make_install.js"
   },
diff --git a/scripts/update_version.js b/scripts/update_version.js
new file mode 100644
index 0000000..775c98a
--- /dev/null
+++ b/scripts/update_version.js
@@ -0,0 +1,141 @@
+// const fs = require('fs');
+// const path = require('path');
+// const readline = require('readline');
+import fs from 'node:fs';
+import path from 'node:path';
+import readline from 'node:readline';
+
+// Utility to read JSON file
+function readJsonFile(filePath) {
+    return new Promise((resolve, reject) => {
+        fs.readFile(filePath, 'utf8', (err, data) => {
+            if (err) return reject(err);
+            try {
+                const jsonData = JSON.parse(data);
+                resolve(jsonData);
+            } catch (e) {
+                reject(e);
+            }
+        });
+    });
+}
+
+// Utility to write JSON file
+function writeJsonFile(filePath, jsonData) {
+    return new Promise((resolve, reject) => {
+        fs.writeFile(filePath, JSON.stringify(jsonData, null, 2), 'utf8', (err) => {
+            if (err) return reject(err);
+            resolve();
+        });
+    });
+}
+
+// Utility to prompt the user for input
+function promptUser(query) {
+    const rl = readline.createInterface({
+        input: process.stdin,
+        output: process.stdout
+    });
+    return new Promise((resolve) => rl.question(query, (answer) => {
+        rl.close();
+        resolve(answer);
+    }));
+}
+
+// Function to parse the version string
+function parseVersion(version) {
+    const [major, minor, patch] = version.split('.').map(Number);
+    return { major, minor, patch };
+}
+
+// Function to auto-increment version parts
+function incrementVersion(version, type) {
+    let { major, minor, patch } = parseVersion(version);
+
+    switch (type) {
+        case 'major':
+            major++;
+            minor = 0;
+            patch = 0;
+            break;
+        case 'minor':
+            minor++;
+            patch = 0;
+            break;
+        case 'patch':
+            patch++;
+            break;
+        default:
+            break;
+    }
+
+    return `${major}.${minor}.${patch}`;
+}
+
+// Main script
+(async function () {
+    try {
+        const pluginJsonPath = path.join(process.cwd(), 'plugin.json');
+        const packageJsonPath = path.join(process.cwd(), 'package.json');
+
+        // Read both JSON files
+        const pluginData = await readJsonFile(pluginJsonPath);
+        const packageData = await readJsonFile(packageJsonPath);
+
+        // Get the current version from both files (assuming both have the same version)
+        const currentVersion = pluginData.version || packageData.version;
+        console.log(`\n🌟  Current version: \x1b[36m${currentVersion}\x1b[0m\n`);
+
+        // Calculate potential new versions for auto-update
+        const newPatchVersion = incrementVersion(currentVersion, 'patch');
+        const newMinorVersion = incrementVersion(currentVersion, 'minor');
+        const newMajorVersion = incrementVersion(currentVersion, 'major');
+
+        // Prompt the user with formatted options
+        console.log('🔄  How would you like to update the version?\n');
+        console.log(`   1️⃣  Auto update \x1b[33mpatch\x1b[0m version   (new version: \x1b[32m${newPatchVersion}\x1b[0m)`);
+        console.log(`   2️⃣  Auto update \x1b[33mminor\x1b[0m version   (new version: \x1b[32m${newMinorVersion}\x1b[0m)`);
+        console.log(`   3️⃣  Auto update \x1b[33mmajor\x1b[0m version   (new version: \x1b[32m${newMajorVersion}\x1b[0m)`);
+        console.log(`   4️⃣  Input version \x1b[33mmanually\x1b[0m`);
+        // Press 0 to skip version update
+        console.log('   0️⃣  Quit without updating\n');
+
+        const updateChoice = await promptUser('👉  Please choose (1/2/3/4): ');
+
+        let newVersion;
+
+        switch (updateChoice.trim()) {
+            case '1':
+                newVersion = newPatchVersion;
+                break;
+            case '2':
+                newVersion = newMinorVersion;
+                break;
+            case '3':
+                newVersion = newMajorVersion;
+                break;
+            case '4':
+                newVersion = await promptUser('✍️  Please enter the new version (in a.b.c format): ');
+                break;
+            case '0':
+                console.log('\n🛑  Skipping version update.');
+                return;
+            default:
+                console.log('\n❌  Invalid option, no version update.');
+                return;
+        }
+
+        // Update the version in both plugin.json and package.json
+        pluginData.version = newVersion;
+        packageData.version = newVersion;
+
+        // Write the updated JSON back to files
+        await writeJsonFile(pluginJsonPath, pluginData);
+        await writeJsonFile(packageJsonPath, packageData);
+
+        console.log(`\n✅  Version successfully updated to: \x1b[32m${newVersion}\x1b[0m\n`);
+
+    } catch (error) {
+        console.error('❌  Error:', error);
+    }
+})();

From 63ee8d92dc5a3deed82a929b1a9c0199c6d08b93 Mon Sep 17 00:00:00 2001
From: frostime <frostime@foxmail.com>
Date: Tue, 10 Sep 2024 10:55:59 +0800
Subject: [PATCH 77/82] =?UTF-8?q?=F0=9F=94=A7=20ci:=20tsconfig,=20include?=
 =?UTF-8?q?=20svelte?=
MIME-Version: 1.0
Content-Type: text/plain; charset=UTF-8
Content-Transfer-Encoding: 8bit

---
 tsconfig.json | 3 ++-
 1 file changed, 2 insertions(+), 1 deletion(-)

diff --git a/tsconfig.json b/tsconfig.json
index e196929..0fcc1ad 100644
--- a/tsconfig.json
+++ b/tsconfig.json
@@ -47,7 +47,8 @@
         "src/**/*.ts",
         "src/**/*.d.ts",
         "src/**/*.tsx",
-        "src/**/*.vue"
+        "src/**/*.vue",
+        "src/**/*.svelte"
     ],
     "references": [
         {

From 19cae2eabdbd93f9cea368092da11cd85d855de2 Mon Sep 17 00:00:00 2001
From: frostime <frostime@foxmail.com>
Date: Fri, 27 Sep 2024 18:15:23 +0800
Subject: [PATCH 78/82] =?UTF-8?q?=F0=9F=93=9D=20doc:=20introduce=20to=20si?=
 =?UTF-8?q?yuan-plugin-cli?=
MIME-Version: 1.0
Content-Type: text/plain; charset=UTF-8
Content-Transfer-Encoding: 8bit

---
 README.md       | 8 ++++++++
 README_zh_CN.md | 7 +++++++
 2 files changed, 15 insertions(+)

diff --git a/README.md b/README.md
index f0b2a1c..209f668 100644
--- a/README.md
+++ b/README.md
@@ -18,6 +18,14 @@
 4. Provides a github action template to automatically generate package.zip and upload to new release
 
 
+> ![TIP]
+> You can also use our maintained [siyuan-plugin-cli](https://www.npmjs.com/package/siyuan-plugin-cli) command-line tool to directly build plugins in your local terminal.
+>
+> Additionally, for the `make-link` related commands mentioned in this plugin, all future updates will be made in [siyuan-plugin-cli](https://www.npmjs.com/package/siyuan-plugin-cli).
+>
+> The built-in `make-link` scripts may also be removed in a future version, in favor of using the `siyuan-plugin-cli` tool, aiming to simplify the workload of maintaining multiple plugin templates.
+
+
 ## Get started
 
 1. Use the <kbd>Use this template</kbd> button to make a copy of this repo as a template. Note that the repository name should match the plugin name, and the default branch must be `main`.
diff --git a/README_zh_CN.md b/README_zh_CN.md
index e60f454..bc8667e 100644
--- a/README_zh_CN.md
+++ b/README_zh_CN.md
@@ -26,6 +26,13 @@
 5. 执行 `pnpm run dev` 进行实时编译
 6.  在思源中打开集市并在下载选项卡中启用插件
 
+> ![TIP]
+> 你也可以使用我们维护的 [siyuan-plugin-cli](https://www.npmjs.com/package/siyuan-plugin-cli) 命令行工具,在本地终端中直接构建插件。
+> 
+> 此外,对于本插件以下提及到的 `make-link` 相关的命令,后续所有更新将在 [siyuan-plugin-cli](https://www.npmjs.com/package/siyuan-plugin-cli) 中进行。
+> 
+> 模板内置的 `make-link` 脚本也可能会在未来某个版本中移除,转而使用 `siyuan-plugin-cli` 工具,意在简化同时维护多个插件模板的工作量。
+
 ### 设置 make-link 命令的目标目录
 
 make-link 命令会创建符号链接将你的 `dev` 目录绑定到思源的插件目录下。你可以有三种方式来配置目标的思源工作空间并创建符号链接:

From 4aee720da54a90582c29759741ea099747ba5311 Mon Sep 17 00:00:00 2001
From: frostime <frostime@foxmail.com>
Date: Fri, 27 Sep 2024 18:25:34 +0800
Subject: [PATCH 79/82] =?UTF-8?q?=F0=9F=93=9D=20doc:=20tip?=
MIME-Version: 1.0
Content-Type: text/plain; charset=UTF-8
Content-Transfer-Encoding: 8bit

---
 README.md       | 2 +-
 README_zh_CN.md | 2 +-
 2 files changed, 2 insertions(+), 2 deletions(-)

diff --git a/README.md b/README.md
index 209f668..648dcb7 100644
--- a/README.md
+++ b/README.md
@@ -18,7 +18,7 @@
 4. Provides a github action template to automatically generate package.zip and upload to new release
 
 
-> ![TIP]
+> [!TIP]
 > You can also use our maintained [siyuan-plugin-cli](https://www.npmjs.com/package/siyuan-plugin-cli) command-line tool to directly build plugins in your local terminal.
 >
 > Additionally, for the `make-link` related commands mentioned in this plugin, all future updates will be made in [siyuan-plugin-cli](https://www.npmjs.com/package/siyuan-plugin-cli).
diff --git a/README_zh_CN.md b/README_zh_CN.md
index bc8667e..0ac9fca 100644
--- a/README_zh_CN.md
+++ b/README_zh_CN.md
@@ -26,7 +26,7 @@
 5. 执行 `pnpm run dev` 进行实时编译
 6.  在思源中打开集市并在下载选项卡中启用插件
 
-> ![TIP]
+> [!TIP]
 > 你也可以使用我们维护的 [siyuan-plugin-cli](https://www.npmjs.com/package/siyuan-plugin-cli) 命令行工具,在本地终端中直接构建插件。
 > 
 > 此外,对于本插件以下提及到的 `make-link` 相关的命令,后续所有更新将在 [siyuan-plugin-cli](https://www.npmjs.com/package/siyuan-plugin-cli) 中进行。

From 591cf2e95e345712ce6735543f953f791a78cdc3 Mon Sep 17 00:00:00 2001
From: frostime <frostime@foxmail.com>
Date: Wed, 16 Oct 2024 14:43:56 +0800
Subject: [PATCH 80/82] =?UTF-8?q?=F0=9F=94=A7=20ci:=20=E6=9B=B4=E6=96=B0?=
 =?UTF-8?q?=20vite=20=E6=89=93=E5=8C=85=E8=84=9A=E6=9C=AC?=
MIME-Version: 1.0
Content-Type: text/plain; charset=UTF-8
Content-Transfer-Encoding: 8bit

---
 .github/workflows/release.yml |   2 +-
 package.json                  |  19 +++---
 plugin.json                   |   8 ++-
 public/i18n/zh_CN.json        |  20 +++++++
 public/i18n/zh_CN.yaml        |  21 -------
 src/hello.svelte              |   8 +++
 src/index.scss                |  26 ---------
 src/libs/dialog.ts            |  23 +++++---
 src/libs/promise-pool.ts      |  48 +++++++++++++++
 vite.config.ts                | 106 ++++++++++++----------------------
 10 files changed, 146 insertions(+), 135 deletions(-)
 create mode 100644 public/i18n/zh_CN.json
 delete mode 100644 public/i18n/zh_CN.yaml
 create mode 100644 src/libs/promise-pool.ts

diff --git a/.github/workflows/release.yml b/.github/workflows/release.yml
index 36af5e0..49834e5 100644
--- a/.github/workflows/release.yml
+++ b/.github/workflows/release.yml
@@ -59,4 +59,4 @@ jobs:
           artifactErrorsFailBuild: true
           artifacts: "package.zip"
           token: ${{ secrets.GITHUB_TOKEN }}
-          prerelease: true
+          prerelease: false
diff --git a/package.json b/package.json
index 03a2a88..2b7cdec 100644
--- a/package.json
+++ b/package.json
@@ -1,6 +1,6 @@
 {
   "name": "plugin-sample-vite-svelte",
-  "version": "0.3.5",
+  "version": "0.3.6",
   "type": "module",
   "description": "This is a sample plugin based on vite and svelte for Siyuan (https://b3log.org/siyuan)",
   "repository": "",
@@ -8,29 +8,30 @@
   "author": "frostime",
   "license": "MIT",
   "scripts": {
+    "dev": "cross-env NODE_ENV=development VITE_SOURCEMAP=inline vite build --watch",
+    "build": "cross-env NODE_ENV=production vite build",
     "make-link": "node --no-warnings ./scripts/make_dev_link.js",
     "make-link-win": "powershell.exe -NoProfile -ExecutionPolicy Bypass -File ./scripts/elevate.ps1 -scriptPath ./scripts/make_dev_link.js",
-    "dev": "vite build --watch",
     "update-version": "node --no-warnings ./scripts/update_version.js",
-    "build": "vite build",
     "make-install": "vite build && node --no-warnings ./scripts/make_install.js"
   },
   "devDependencies": {
-    "@sveltejs/vite-plugin-svelte": "^3.0.0",
+    "@sveltejs/vite-plugin-svelte": "^3.1.0",
     "@tsconfig/svelte": "^4.0.1",
     "@types/node": "^20.3.0",
+    "cross-env": "^7.0.3",
     "fast-glob": "^3.2.12",
-    "glob": "^7.2.3",
+    "glob": "^10.0.0",
     "js-yaml": "^4.1.0",
     "minimist": "^1.2.8",
     "rollup-plugin-livereload": "^2.0.5",
     "sass": "^1.63.3",
-    "siyuan": "0.9.9",
-    "svelte": "^4.2.0",
+    "siyuan": "1.0.4",
+    "svelte": "^4.2.19",
     "ts-node": "^10.9.1",
     "typescript": "^5.1.3",
-    "vite": "^5.0.0",
+    "vite": "^5.2.9",
     "vite-plugin-static-copy": "^1.0.2",
     "vite-plugin-zip-pack": "^1.0.5"
   }
-}
+}
\ No newline at end of file
diff --git a/plugin.json b/plugin.json
index 1af2cfe..44e3e10 100644
--- a/plugin.json
+++ b/plugin.json
@@ -2,7 +2,7 @@
   "name": "plugin-sample-vite-svelte",
   "author": "frostime",
   "url": "https://github.com/siyuan-note/plugin-sample-vite-svelte",
-  "version": "0.3.5",
+  "version": "0.3.6",
   "minAppVersion": "3.0.12",
   "backends": [
     "windows",
@@ -37,6 +37,8 @@
     ]
   },
   "keywords": [
-    "plugin", "sample", "插件样例"
+    "plugin",
+    "sample",
+    "插件样例"
   ]
-}
+}
\ No newline at end of file
diff --git a/public/i18n/zh_CN.json b/public/i18n/zh_CN.json
new file mode 100644
index 0000000..6600f6a
--- /dev/null
+++ b/public/i18n/zh_CN.json
@@ -0,0 +1,20 @@
+{
+    "addTopBarIcon": "使用插件添加一个顶栏按钮",
+    "cancel": "取消",
+    "save": "保存",
+    "byeMenu": "再见,菜单!",
+    "helloPlugin": "你好,插件!",
+    "byePlugin": "再见,插件!",
+    "showDialog": "弹出一个对话框",
+    "removedData": "数据已删除",
+    "confirmRemove": "确认删除 ${name} 中的数据?",
+    "insertEmoji": "插入表情",
+    "removeSpace": "移除空格",
+    "getTab": "在日志中打印出已打开的所有自定义页签",
+    "name": "思源",
+    "hello": {
+        "makesure": "使用这个模板之前,请阅读<a href=\"https://github.com/siyuan-note/plugin-sample\">官方教程</a>, 确保自己已经理解了插件的基本开发流程。"
+    },
+    "hintTitle": "关于",
+    "hintDesc": "<a href='https://github.com/siyuan-note/plugin-sample-vite-svelte'>🔗 plugin-sample-vite-svelte</a><br>💻 @frostime<br>💻 @88250<br>💻 @zxkmm"
+}
\ No newline at end of file
diff --git a/public/i18n/zh_CN.yaml b/public/i18n/zh_CN.yaml
deleted file mode 100644
index 16099a3..0000000
--- a/public/i18n/zh_CN.yaml
+++ /dev/null
@@ -1,21 +0,0 @@
----
-addTopBarIcon: 使用插件添加一个顶栏按钮
-cancel: 取消
-save: 保存
-byeMenu: 再见,菜单!
-helloPlugin: 你好,插件!
-byePlugin: 再见,插件!
-showDialog: 弹出一个对话框
-removedData: 数据已删除
-confirmRemove: 确认删除 ${name} 中的数据?
-insertEmoji: 插入表情
-removeSpace: 移除空格
-getTab: 在日志中打印出已打开的所有自定义页签
-name: 思源
-hello:
-  makesure: 使用这个模板之前,请阅读<a href="https://github.com/siyuan-note/plugin-sample">官方教程</a>,
-    确保自己已经理解了插件的基本开发流程。
-hintTitle: 关于
-hintDesc: "<a href='https://github.com/siyuan-note/plugin-sample-vite-svelte'>\U0001F517
-  plugin-sample-vite-svelte</a><br>\U0001F4BB @frostime<br>\U0001F4BB @88250<br>\U0001F4BB
-  @zxkmm"
diff --git a/src/hello.svelte b/src/hello.svelte
index cc9f0dd..967c7f6 100644
--- a/src/hello.svelte
+++ b/src/hello.svelte
@@ -1,3 +1,11 @@
+<!--
+ Copyright (c) 2024 by frostime. All Rights Reserved.
+ Author       : frostime
+ Date         : 2023-11-19 12:30:45
+ FilePath     : /src/hello.svelte
+ LastEditTime : 2024-10-16 14:37:50
+ Description  : 
+-->
 <script lang="ts">
     import { onDestroy, onMount } from "svelte";
     import { version, sql as query } from "@/api";
diff --git a/src/index.scss b/src/index.scss
index c8efd4f..e69de29 100644
--- a/src/index.scss
+++ b/src/index.scss
@@ -1,26 +0,0 @@
-#helloPanel {
-    border: 1px rgb(189, 119, 119) dashed;
-}
-
-.plugin-sample {
-    &__custom-tab {
-        background-color: var(--b3-theme-background);
-        height: 100%;
-        width: 100%;
-        display: flex;
-        justify-content: center;
-        align-items: center;
-    }
-
-    &__custom-dock {
-        display: flex;
-        justify-content: center;
-        align-items: center;
-    }
-
-    &__time {
-        background: var(--b3-card-info-background);
-        border-radius: 4px;
-        padding: 2px 8px;
-    }
-}
\ No newline at end of file
diff --git a/src/libs/dialog.ts b/src/libs/dialog.ts
index 7e86f21..d0fe582 100644
--- a/src/libs/dialog.ts
+++ b/src/libs/dialog.ts
@@ -3,7 +3,7 @@
  * @Author       : frostime
  * @Date         : 2024-03-23 21:37:33
  * @FilePath     : /src/libs/dialog.ts
- * @LastEditTime : 2024-07-19 15:34:39
+ * @LastEditTime : 2024-10-16 14:31:04
  * @Description  : Kits about dialogs
  */
 import { Dialog } from "siyuan";
@@ -135,7 +135,10 @@ export const simpleDialog = (args: {
         destroyCallback: args.callback
     });
     dialog.element.querySelector(".dialog-content").appendChild(args.ele);
-    return dialog;
+    return {
+        dialog,
+        close: dialog.destroy.bind(dialog)
+    };
 }
 
 
@@ -147,9 +150,15 @@ export const svelteDialog = (args: {
     let container = document.createElement('div')
     container.style.display = 'contents';
     let component = args.constructor(container);
-    simpleDialog({...args, ele: container, callback: () => {
-        component.$destroy();
-        if (args.callback) args.callback();;
-    }});
-    return component;
+    const { dialog, close } = simpleDialog({
+        ...args, ele: container, callback: () => {
+            component.$destroy();
+            if (args.callback) args.callback();
+        }
+    });
+    return {
+        component,
+        dialog,
+        close
+    }
 }
diff --git a/src/libs/promise-pool.ts b/src/libs/promise-pool.ts
new file mode 100644
index 0000000..e20b0b8
--- /dev/null
+++ b/src/libs/promise-pool.ts
@@ -0,0 +1,48 @@
+export default class PromiseLimitPool<T> {
+    private maxConcurrent: number;
+    private currentRunning = 0;
+    private queue: (() => void)[] = [];
+    private promises: Promise<T>[] = [];
+
+    constructor(maxConcurrent: number) {
+        this.maxConcurrent = maxConcurrent;
+    }
+
+    add(fn: () => Promise<T>): void {
+        const promise = new Promise<T>((resolve, reject) => {
+            const run = async () => {
+                try {
+                    this.currentRunning++;
+                    const result = await fn();
+                    resolve(result);
+                } catch (error) {
+                    reject(error);
+                } finally {
+                    this.currentRunning--;
+                    this.next();
+                }
+            };
+
+            if (this.currentRunning < this.maxConcurrent) {
+                run();
+            } else {
+                this.queue.push(run);
+            }
+        });
+        this.promises.push(promise);
+    }
+
+    async awaitAll(): Promise<T[]> {
+        return Promise.all(this.promises);
+    }
+
+    /**
+     * Handles the next task in the queue.
+     */
+    private next(): void {
+        if (this.queue.length > 0 && this.currentRunning < this.maxConcurrent) {
+            const nextRun = this.queue.shift()!;
+            nextRun();
+        }
+    }
+}
diff --git a/vite.config.ts b/vite.config.ts
index 809da57..7deacce 100644
--- a/vite.config.ts
+++ b/vite.config.ts
@@ -1,6 +1,5 @@
 import { resolve } from "path"
 import { defineConfig, loadEnv } from "vite"
-import minimist from "minimist"
 import { viteStaticCopy } from "vite-plugin-static-copy"
 import livereload from "rollup-plugin-livereload"
 import { svelte } from "@sveltejs/vite-plugin-svelte"
@@ -9,13 +8,15 @@ import fg from 'fast-glob';
 
 import vitePluginYamlI18n from './yaml-plugin';
 
-const args = minimist(process.argv.slice(2))
-const isWatch = args.watch || args.w || false
-const devDistDir = "dev"
-const distDir = isWatch ? devDistDir : "dist"
+const env = process.env;
+const isSrcmap = env.VITE_SOURCEMAP === 'inline';
+const isDev = env.NODE_ENV === 'development';
 
-console.log("isWatch=>", isWatch)
-console.log("distDir=>", distDir)
+const outputDir = isDev ? "dev" : "dist";
+
+console.log("isDev=>", isDev);
+console.log("isSrcmap=>", isSrcmap);
+console.log("outputDir=>", outputDir);
 
 export default defineConfig({
     resolve: {
@@ -29,92 +30,61 @@ export default defineConfig({
 
         vitePluginYamlI18n({
             inDir: 'public/i18n',
-            outDir: `${distDir}/i18n`
+            outDir: `${outputDir}/i18n`
         }),
 
         viteStaticCopy({
             targets: [
-                {
-                    src: "./README*.md",
-                    dest: "./",
-                },
-                {
-                    src: "./plugin.json",
-                    dest: "./",
-                },
-                {
-                    src: "./preview.png",
-                    dest: "./",
-                },
-                {
-                    src: "./icon.png",
-                    dest: "./",
-                }
+                { src: "./README*.md", dest: "./" },
+                { src: "./plugin.json", dest: "./" },
+                { src: "./preview.png", dest: "./" },
+                { src: "./icon.png", dest: "./" }
             ],
         }),
     ],
 
-    // https://github.com/vitejs/vite/issues/1930
-    // https://vitejs.dev/guide/env-and-mode.html#env-files
-    // https://github.com/vitejs/vite/discussions/3058#discussioncomment-2115319
-    // 在这里自定义变量
     define: {
-        "process.env.DEV_MODE": `"${isWatch}"`,
-        "process.env.NODE_ENV": JSON.stringify(process.env.NODE_ENV)
+        "process.env.DEV_MODE": JSON.stringify(isDev),
+        "process.env.NODE_ENV": JSON.stringify(env.NODE_ENV)
     },
 
     build: {
-        // 输出路径
-        outDir: distDir,
+        outDir: outputDir,
         emptyOutDir: false,
-
-        // 构建后是否生成 source map 文件
-        sourcemap: isWatch ? 'inline' : false,
-
-        // 设置为 false 可以禁用最小化混淆
-        // 或是用来指定是应用哪种混淆器
-        // boolean | 'terser' | 'esbuild'
-        // 不压缩,用于调试
-        minify: !isWatch,
+        minify: true,
+        sourcemap: isSrcmap ? 'inline' : false,
 
         lib: {
-            // Could also be a dictionary or array of multiple entry points
             entry: resolve(__dirname, "src/index.ts"),
-            // the proper extensions will be added
             fileName: "index",
             formats: ["cjs"],
         },
         rollupOptions: {
             plugins: [
-                ...(
-                    isWatch ? [
-                        livereload(devDistDir),
-                        {
-                            //监听静态资源文件
-                            name: 'watch-external',
-                            async buildStart() {
-                                const files = await fg([
-                                    'public/i18n/**',
-                                    './README*.md',
-                                    './plugin.json'
-                                ]);
-                                for (let file of files) {
-                                    this.addWatchFile(file);
-                                }
+                ...(isDev ? [
+                    livereload(outputDir),
+                    {
+                        name: 'watch-external',
+                        async buildStart() {
+                            const files = await fg([
+                                'public/i18n/**',
+                                './README*.md',
+                                './plugin.json'
+                            ]);
+                            for (let file of files) {
+                                this.addWatchFile(file);
                             }
                         }
-                    ] : [
-                        zipPack({
-                            inDir: './dist',
-                            outDir: './',
-                            outFileName: 'package.zip'
-                        })
-                    ]
-                )
+                    }
+                ] : [
+                    zipPack({
+                        inDir: './dist',
+                        outDir: './',
+                        outFileName: 'package.zip'
+                    })
+                ])
             ],
 
-            // make sure to externalize deps that shouldn't be bundled
-            // into your library
             external: ["siyuan", "process"],
 
             output: {

From 38b19fdb888f5f26004b747739f2403b2087c120 Mon Sep 17 00:00:00 2001
From: frostime <frostime@foxmail.com>
Date: Thu, 24 Oct 2024 14:51:01 +0800
Subject: [PATCH 81/82] =?UTF-8?q?=F0=9F=94=A7=20ci:=20update=20vite=20scri?=
 =?UTF-8?q?pt,=20auto=20remove=20trivial=20files=20under=20`dist/`?=
MIME-Version: 1.0
Content-Type: text/plain; charset=UTF-8
Content-Transfer-Encoding: 8bit

---
 public/i18n/README.md | 12 ++++++++
 vite.config.ts        | 64 ++++++++++++++++++++++++++++++++++++++++++-
 2 files changed, 75 insertions(+), 1 deletion(-)
 create mode 100644 public/i18n/README.md

diff --git a/public/i18n/README.md b/public/i18n/README.md
new file mode 100644
index 0000000..af8de98
--- /dev/null
+++ b/public/i18n/README.md
@@ -0,0 +1,12 @@
+思源支持的 i18n 文件范围,可以在控制台 `siyuan.config.langs` 中查看。以下是目前(2024-10-24)支持的语言方案:
+
+The range of i18n files supported by SiYuan can be viewed in the console under `siyuan.config.langs`. Below are the language schemes currently supported as of now (October 24, 2024) :
+
+```js
+>>> siyuan.config.langs.map( lang => lang.name)
+['de_DE', 'en_US', 'es_ES', 'fr_FR', 'he_IL', 'it_IT', 'ja_JP', 'pl_PL', 'ru_RU', 'zh_CHT', 'zh_CN']
+```
+
+在插件开发中,默认使用 JSON 格式作为国际化(i18n)的载体文件。如果您更喜欢使用 YAML 语法,可以将 JSON 文件替换为 YAML 文件(例如 `en_US.yaml`),并在其中编写 i18n 文本。本模板提供了相关的 Vite 插件,可以在编译时自动将 YAML 文件转换为 JSON 文件(请参见 `/yaml-plugin.js`)。本 MD 文件 和 YAML 文件会在  `npm run build` 时自动从 `dist` 目录下删除,仅保留必要的 JSON 文件共插件系统使用。
+
+In plugin development, JSON format is used by default as the carrier file for internationalization (i18n). If you prefer to use YAML syntax, you can replace the JSON file with a YAML file (e.g., `en_US.yaml`) and write the i18n text within it. This template provides a related Vite plugin that can automatically convert YAML files to JSON files during the compilation process (see `/yaml-plugin.js`). This markdown file and YAML files will be automatically removed from the `dist` directory during `npm run build`, leaving only the necessary JSON files for plugin system to use.
diff --git a/vite.config.ts b/vite.config.ts
index 7deacce..cb7511b 100644
--- a/vite.config.ts
+++ b/vite.config.ts
@@ -41,6 +41,7 @@ export default defineConfig({
                 { src: "./icon.png", dest: "./" }
             ],
         }),
+
     ],
 
     define: {
@@ -77,6 +78,11 @@ export default defineConfig({
                         }
                     }
                 ] : [
+                    // Clean up unnecessary files under dist dir
+                    cleanupDistFiles({
+                        patterns: ['i18n/*.yaml', 'i18n/*.md'],
+                        distDir: outputDir
+                    }),
                     zipPack({
                         inDir: './dist',
                         outDir: './',
@@ -98,4 +104,60 @@ export default defineConfig({
             },
         },
     }
-})
+});
+
+
+/**
+ * Clean up some dist files after compiled
+ * @author frostime
+ * @param options:
+ * @returns 
+ */
+function cleanupDistFiles(options: { patterns: string[], distDir: string }) {
+    const {
+        patterns,
+        distDir
+    } = options;
+
+    return {
+        name: 'rollup-plugin-cleanup',
+        enforce: 'post',
+        writeBundle: {
+            sequential: true,
+            order: 'post' as 'post',
+            async handler() {
+                const fg = await import('fast-glob');
+                const fs = await import('fs');
+                // const path = await import('path');
+
+                // 使用 glob 语法,确保能匹配到文件
+                const distPatterns = patterns.map(pat => `${distDir}/${pat}`);
+                console.debug('Cleanup searching patterns:', distPatterns);
+
+                const files = await fg.default(distPatterns, {
+                    dot: true,
+                    absolute: true,
+                    onlyFiles: false
+                });
+
+                // console.info('Files to be cleaned up:', files);
+
+                for (const file of files) {
+                    try {
+                        if (fs.default.existsSync(file)) {
+                            const stat = fs.default.statSync(file);
+                            if (stat.isDirectory()) {
+                                fs.default.rmSync(file, { recursive: true });
+                            } else {
+                                fs.default.unlinkSync(file);
+                            }
+                            console.log(`Cleaned up: ${file}`);
+                        }
+                    } catch (error) {
+                        console.error(`Failed to clean up ${file}:`, error);
+                    }
+                }
+            }
+        }
+    };
+}

From 777f31761c98d2dbc63e54213b9f290031cfbf9b Mon Sep 17 00:00:00 2001
From: Frostime <frostime@foxmail.com>
Date: Tue, 10 Dec 2024 18:00:25 +0800
Subject: [PATCH 82/82] Update plugin.json

---
 plugin.json | 7 ++++---
 1 file changed, 4 insertions(+), 3 deletions(-)

diff --git a/plugin.json b/plugin.json
index 44e3e10..fd63a3c 100644
--- a/plugin.json
+++ b/plugin.json
@@ -8,9 +8,10 @@
     "windows",
     "linux",
     "darwin",
-    "docker",
     "ios",
-    "android"
+    "android",
+    "harmony",
+    "docker"
   ],
   "frontends": [
     "desktop",
@@ -41,4 +42,4 @@
     "sample",
     "插件样例"
   ]
-}
\ No newline at end of file
+}