diff --git a/.eslintignore b/.eslintignore new file mode 100644 index 0000000..f06235c --- /dev/null +++ b/.eslintignore @@ -0,0 +1,2 @@ +node_modules +dist diff --git a/.eslintrc.cjs b/.eslintrc.cjs new file mode 100644 index 0000000..0781605 --- /dev/null +++ b/.eslintrc.cjs @@ -0,0 +1,38 @@ +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/.github/workflows/release.yml b/.github/workflows/release.yml index 49834e5..a6b0329 100644 --- a/.github/workflows/release.yml +++ b/.github/workflows/release.yml @@ -1,62 +1,62 @@ name: Create Release on Tag Push on: - push: - tags: - - "v*" + push: + tags: + - "v*" jobs: - build: - runs-on: ubuntu-latest - steps: - # Checkout - - name: Checkout - uses: actions/checkout@v3 + build: + runs-on: ubuntu-latest + steps: + # Checkout + - name: Checkout + uses: actions/checkout@v3 - # Install Node.js - - name: Install Node.js - uses: actions/setup-node@v3 - with: - node-version: 20 - registry-url: "https://registry.npmjs.org" + # Install Node.js + - name: Install Node.js + uses: actions/setup-node@v3 + with: + node-version: 18 + registry-url: "https://registry.npmjs.org" - # Install pnpm - - name: Install pnpm - uses: pnpm/action-setup@v4 - id: pnpm-install - with: - version: 8 - run_install: false + # Install pnpm + - name: Install pnpm + uses: pnpm/action-setup@v2 + id: pnpm-install + with: + version: 8 + run_install: false - # Get pnpm store directory - - name: Get pnpm store directory - id: pnpm-cache - shell: bash - run: | - echo "STORE_PATH=$(pnpm store path)" >> $GITHUB_OUTPUT + # Get pnpm store directory + - name: Get pnpm store directory + id: pnpm-cache + shell: bash + run: | + echo "STORE_PATH=$(pnpm store path)" >> $GITHUB_OUTPUT - # Setup pnpm cache - - name: Setup pnpm cache - uses: actions/cache@v3 - with: - path: ${{ steps.pnpm-cache.outputs.STORE_PATH }} - key: ${{ runner.os }}-pnpm-store-${{ hashFiles('**/pnpm-lock.yaml') }} - restore-keys: | - ${{ runner.os }}-pnpm-store- + # Setup pnpm cache + - name: Setup pnpm cache + uses: actions/cache@v3 + with: + path: ${{ steps.pnpm-cache.outputs.STORE_PATH }} + key: ${{ runner.os }}-pnpm-store-${{ hashFiles('**/pnpm-lock.yaml') }} + restore-keys: | + ${{ runner.os }}-pnpm-store- - # Install dependencies - - name: Install dependencies - run: pnpm install + # Install dependencies + - name: Install dependencies + run: pnpm install - # Build for production, 这一步会生成一个 package.zip - - name: Build for production - run: pnpm build + # Build for production, 这一步会生成一个 package.zip + - name: Build for production + run: pnpm build - - name: Release - uses: ncipollo/release-action@v1 - with: - allowUpdates: true - artifactErrorsFailBuild: true - artifacts: "package.zip" - token: ${{ secrets.GITHUB_TOKEN }} - prerelease: false + - name: Release + uses: ncipollo/release-action@v1 + with: + allowUpdates: true + artifactErrorsFailBuild: true + artifacts: 'package.zip' + token: ${{ secrets.GITHUB_TOKEN }} + prerelease: true diff --git a/.gitignore b/.gitignore index c2a2064..6d641ec 100644 --- a/.gitignore +++ b/.gitignore @@ -1,11 +1,8 @@ .idea .vscode .DS_Store -pnpm-lock.yaml -package-lock.json package.zip node_modules dev dist build -tmp diff --git a/CHANGELOG.md b/CHANGELOG.md index af04807..e69de29 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,112 +0,0 @@ -# Changelog - -## 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 - -* [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 - -* [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) - -## 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 - -* [Deprecated `loaded-protyle` use `loaded-protyle-static` instead](https://github.com/siyuan-note/siyuan/issues/9468) - -## 0.2.5 2023-10-10 - -* [Add plugin event bus `open-menu-doctree`](https://github.com/siyuan-note/siyuan/issues/9351) - -## 0.2.4 2023-09-19 - -* Supports use in windows -* [Add plugin function `transaction`](https://github.com/siyuan-note/siyuan/issues/9172) - -## 0.2.3 2023-09-05 - -* [Add plugin function `transaction`](https://github.com/siyuan-note/siyuan/issues/9172) -* [Plugin API add openWindow and command.globalCallback](https://github.com/siyuan-note/siyuan/issues/9032) - -## 0.2.2 2023-08-29 - -* [Add plugin event bus `destroy-protyle`](https://github.com/siyuan-note/siyuan/issues/9033) -* [Add plugin event bus `loaded-protyle-dynamic`](https://github.com/siyuan-note/siyuan/issues/9021) - -## 0.2.1 2023-08-21 - -* [Plugin API add getOpenedTab method](https://github.com/siyuan-note/siyuan/issues/9002) -* [Plugin API custom.fn => custom.id in openTab](https://github.com/siyuan-note/siyuan/issues/8944) - -## 0.2.0 2023-08-15 - -* [Add plugin event bus `open-siyuan-url-plugin` and `open-siyuan-url-block`](https://github.com/siyuan-note/siyuan/pull/8927) - - -## 0.1.12 2023-08-01 - -* Upgrade siyuan to 0.7.9 - -## 0.1.11 - -* [Add `input-search` event bus to plugins](https://github.com/siyuan-note/siyuan/issues/8725) - - -## 0.1.10 - -* [Add `bind this` example for eventBus in plugins](https://github.com/siyuan-note/siyuan/issues/8668) -* [Add `open-menu-breadcrumbmore` event bus to plugins](https://github.com/siyuan-note/siyuan/issues/8666) - -## 0.1.9 - -* [Add `open-menu-xxx` event bus for plugins ](https://github.com/siyuan-note/siyuan/issues/8617) - -## 0.1.8 - -* [Add protyleSlash to the plugin](https://github.com/siyuan-note/siyuan/issues/8599) -* [Add plugin API protyle](https://github.com/siyuan-note/siyuan/issues/8445) - -## 0.1.7 - -* [Support build js and json](https://github.com/siyuan-note/plugin-sample/pull/8) - -## 0.1.6 - -* add `fetchPost` example diff --git a/README.md b/README.md index 648dcb7..d3edd63 100644 --- a/README.md +++ b/README.md @@ -3,77 +3,33 @@ [中文版](./README_zh_CN.md) -> 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) - - - 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) - > - > **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 -> [!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 Use this template 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. +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` -### Setting the Target Directory for the make-link Command +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. -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: +3. Install NodeJS and pnpm, then run pnpm i in the command line under your repo folder +4. Create development symbolic links + - Open `./scripts/make_dev_link.js` file, set `targetDir` to your SiYuan plugin directory `/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 -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 - > node --no-warnings ./scripts/make_dev_link.js + Done! Created symlink H:/SiYuanDevSpace/data/plugins/plugin-sample-vite-svelte + ``` - "targetDir" is empty, try to get SiYuan directory automatically.... - Got 2 SiYuan workspaces - [0] H:\Media\SiYuan - [1] H:\临时文件夹\SiYuanDevSpace - Please select a workspace[0-1]: 0 - Got target directory: H:\Media\SiYuan/data/plugins - Done! Created symlink H:\Media\SiYuan/data/plugins/plugin-sample-vite-svelte - ``` -2. **Manually Configure Target Directory** - - Open the `./scripts/make_dev_link.js` file, change `targetDir` to the SiYuan plugin directory `/data/plugins`. - - Run the `pnpm run make-link` command. If you see a message similar to the one below, it indicates successful creation: - -3. **Set Environment Variable to Create Symbolic Link** - - Set the system environment variable `SIYUAN_PLUGIN_DIR` to the path `workspace/data/plugins`. - -### 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. +5. Execute pnpm run dev for real-time compilation +6. Open SiYuan marketplace and enable plugin in downloaded tab ## I18n @@ -83,11 +39,9 @@ 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 - * public/i18n/*.json language configuration files + * src/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. +* Finally, declare the language supported by the plugin in the `i18n` field in plugin.json It is recommended that the plugin supports at least English and Simplified Chinese, so that more people can use it more conveniently. @@ -99,10 +53,7 @@ conveniently. "name": "plugin-sample-vite-svelte", "author": "frostime", "url": "https://github.com/siyuan-note/plugin-sample-vite-svelte", - "version": "0.1.3", - "minAppVersion": "2.8.8", - "backends": ["windows", "linux", "darwin"], - "frontends": ["desktop"], + "version": "1.0.0", "displayName": { "en_US": "Plugin sample with vite and svelte", "zh_CN": "插件样例 vite + svelte 版" @@ -115,17 +66,15 @@ conveniently. "en_US": "README_en_US.md", "zh_CN": "README.md" }, + "i18n": [ + "en_US", + "zh_CN" + ], "funding": { - "openCollective": "", - "patreon": "", - "github": "", "custom": [ - "https://ld246.com/sponsor" + "https://afdian.net/a/frostime" ] - }, - "keywords": [ - "sample", "示例" - ] + } } ``` @@ -134,22 +83,6 @@ conveniently. * `author`: Plugin author name * `url`: Plugin repo URL * `version`: Plugin version number, it is recommended to follow the [semver](https://semver.org/) specification -* `minAppVersion`: Minimum version number of SiYuan required to use this plugin -* `backends`: Backend environment required by the plugin, optional values are `windows`, `linux`, `darwin`, `docker`, `android`, `ios` and `all` - * `windows`: Windows desktop - * `linux`: Linux desktop - * `darwin`: macOS desktop - * `docker`: Docker - * `android`: Android APP - * `ios`: iOS APP - * `all`: All environments -* `frontends`: Frontend environment required by the plugin, optional values are `desktop`, `desktop-window`, `mobile`, `browser-desktop`, `browser-mobile` and `all` - * `desktop`: Desktop - * `desktop-window`: Desktop window converted from tab - * `mobile`: Mobile APP - * `browser-desktop`: Desktop browser - * `browser-mobile`: Mobile browser - * `all`: All environments * `displayName`: Template display name, mainly used for display in the marketplace list, supports multiple languages * `default`: Default language, must exist * `zh_CN`, `en_US` and other languages: optional, it is recommended to provide at least Chinese and English @@ -159,25 +92,25 @@ conveniently. * `readme`: readme file name, mainly used to display in the marketplace details page, supports multiple languages * `default`: Default language, must exist * `zh_CN`, `en_US` and other languages: optional, it is recommended to provide at least Chinese and English +* `i18n`: Plugin supported language list * `funding`: Plugin sponsorship information * `openCollective`: Open Collective name * `patreon`: Patreon name * `github`: GitHub login name * `custom`: Custom sponsorship link list -* `keywords`: Search keyword list, used for marketplace search function ## Package No matter which method is used to compile and package, we finally need to generate a package.zip, which contains at least the following files: -* i18n/* -* icon.png (160*160) -* index.css +* icon.png * index.js * plugin.json -* preview.png (1024*768) +* preview.png * README*.md +* index.css (optional) +* i18n/* (optional) ## List on the marketplace @@ -229,50 +162,3 @@ The github action is included in this sample, you can use it to publish your new prerelease: true # change this to false ``` - -## How to remove svelte dependencies - -> Pure vite without svelte: https://github.com/frostime/plugin-sample-vite - -This plugin is packaged in vite and provides a dependency on the svelte framework. However, in practice some developers may not want to use svelte and only want to use the vite package. - -In fact you can use this template without using svelte without any modifications at all. The compilation-related parts of the svelte compilation are loaded into the vite workflow as plugins, so even if you don't have svelte in your project, it won't matter much. - -If you insist on removing all svelte dependencies so that they do not pollute your workspace, you can perform the following steps. 1. - -1. delete the - ```json - { - "@sveltejs/vite-plugin-svelte": "^2.0.3", - "@tsconfig/svelte": "^4.0.1", - "svelte": "^3.57.0" - } - ``` -2. delete the `svelte.config.js` file -3. delete the following line from the `vite.config.js` file - - Line 6: `import { svelte } from "@sveltejs/vite-plugin-svelte"` - - 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 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 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 - diff --git a/README_zh_CN.md b/README_zh_CN.md index 0ac9fca..dbcf283 100644 --- a/README_zh_CN.md +++ b/README_zh_CN.md @@ -1,78 +1,30 @@ - -# 使用 vite + svelte 的思源笔记插件示例 - [English](./README.md) - -> 本例同 [siyuan/plugin-sample](https://github.com/siyuan-note/plugin-sample) [v0.3.5](https://github.com/siyuan-note/plugin-sample/tree/v0.3.5) +# 使用 vite + svelte 的思源笔记插件示例 1. 使用 vite 打包 2. 使用符号链接、而不是把项目放到插件目录下的模式进行开发 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并上传到新版本中 ## 开始 -1. 通过 Use this template 按钮将该库文件复制到你自己的库中,请注意库名和插件名称一致,默认分支必须为 `main` +1. 通过 Use this template 按钮将该库文件复制到你自己的库中,请注意库名必须和插件名称一致,默认分支必须为 `main` 2. 将你的库克隆到本地开发文件夹中 * 注意: 同 `plugin-sample` 不同, 本样例并不推荐直接把代码下载到 `{workspace}/data/plugins/` 3. 安装 [NodeJS](https://nodejs.org/en/download) 和 [pnpm](https://pnpm.io/installation),然后在开发文件夹下执行 `pnpm i` 安装所需要的依赖 -4. 运行 `pnpm run make-link` 命令创建符号链接 (Windows 下的开发者请参阅下方「Windows 下的 make-link」小节) -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` 目录绑定到思源的插件目录下。你可以有三种方式来配置目标的思源工作空间并创建符号链接: - -1. **选择工作空间** - - 打开思源笔记, 确保思源内核正在运行 - - 运行 `pnpm run make-link`, 脚本会自动检测所有思源的工作空间, 请在命令行中手动输入序号以选择工作空间 - ```bash - >>> pnpm run make-link - > plugin-sample-vite-svelte@0.0.3 make-link H:\SrcCode\开源项目\plugin-sample-vite-svelte - > node --no-warnings ./scripts/make_dev_link.js - - "targetDir" is empty, try to get SiYuan directory automatically.... - Got 2 SiYuan workspaces - [0] H:\Media\SiYuan - [1] H:\临时文件夹\SiYuanDevSpace - Please select a workspace[0-1]: 0 - Got target directory: H:\Media\SiYuan/data/plugins - Done! Created symlink H:\Media\SiYuan/data/plugins/plugin-sample-vite-svelte - ``` -2. **手动配置目标目录** +4. 创建开发需要的符号链接 - 打开 `./scripts/make_dev_link.js` 文件,更改 `targetDir` 为思源的插件目录 `/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 -3. **设置环境变量创建符号链接** - - 设置系统的环境变量 `SIYUAN_PLUGIN_DIR` 为 `工作空间/data/plugins` 的路径 - - -### 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 脚本执行权限 + Done! Created symlink H:/SiYuanDevSpace/data/plugins/plugin-sample-vite-svelte + ``` +5. 执行 `pnpm run dev` 进行实时编译 +6. 在思源中打开集市并在下载选项卡中启用插件 ## 国际化 @@ -80,14 +32,11 @@ make-link 命令会创建符号链接将你的 `dev` 目录绑定到思源的插 国际化方面我们主要考虑的是支持多语言,具体需要完成以下工作: * 插件自身的元信息,比如插件描述和自述文件 - * plugin.json 中的 `description` 和 `readme` 字段,以及对应的 README*.md 文件 + * plugin.json 中的 `description` 和 `readme` 字段,以及对应的 README*.md 文件 * 插件中使用的文本,比如按钮文字和提示信息 - * public/i18n/*.json 语言配置文件 - * 代码中使用 `this.i18.key` 获取文本 + * src/i18n/*.json 语言配置文件 + * 代码中使用 `this.i18.key` 获取文本 * 最后在 plugin.json 中的 `i18n` 字段中声明该插件支持的语言 -* yaml 支持 - * 本模板特别支持基于 Yaml 语法的 I18n,见 `public/i18n/zh_CN.yaml` - * 编译时,会自动把定义的 yaml 文件翻译成 json 文件放到 dist 或 dev 目录下 建议插件至少支持英文和简体中文,这样可以方便更多人使用。 @@ -98,10 +47,7 @@ make-link 命令会创建符号链接将你的 `dev` 目录绑定到思源的插 "name": "plugin-sample-vite-svelte", "author": "frostime", "url": "https://github.com/siyuan-note/plugin-sample-vite-svelte", - "version": "0.1.3", - "minAppVersion": "2.8.8", - "backends": ["windows", "linux", "darwin"], - "frontends": ["desktop"], + "version": "1.0.0", "displayName": { "en_US": "Plugin sample with vite and svelte", "zh_CN": "插件样例 vite + svelte 版" @@ -114,17 +60,15 @@ make-link 命令会创建符号链接将你的 `dev` 目录绑定到思源的插 "en_US": "README_en_US.md", "zh_CN": "README.md" }, + "i18n": [ + "en_US", + "zh_CN" + ], "funding": { - "openCollective": "", - "patreon": "", - "github": "", "custom": [ - "https://ld246.com/sponsor" + "https://afdian.net/a/frostime" ] - }, - "keywords": [ - "sample", "示例" - ] + } } ``` @@ -132,49 +76,33 @@ make-link 命令会创建符号链接将你的 `dev` 目录绑定到思源的插 * `author`:插件作者名 * `url`:插件仓库地址 * `version`:插件版本号,建议遵循 [semver](https://semver.org/lang/zh-CN/) 规范 -* `minAppVersion`:插件支持的最低思源笔记版本号 -* `backends`:插件需要的后端环境,可选值为 `windows`, `linux`, `darwin`, `docker`, `android`, `ios` and `all` - * `windows`:Windows 桌面端 - * `linux`:Linux 桌面端 - * `darwin`:macOS 桌面端 - * `docker`:Docker 端 - * `android`:Android 端 - * `ios`:iOS 端 - * `all`:所有环境 -* `frontends`:插件需要的前端环境,可选值为 `desktop`, `desktop-window`, `mobile`, `browser-desktop`, `browser-mobile` and `all` - * `desktop`:桌面端 - * `desktop-window`:桌面端页签转换的独立窗口 - * `mobile`:移动端 - * `browser-desktop`:桌面端浏览器 - * `browser-mobile`:移动端浏览器 - * `all`:所有环境 * `displayName`:模板显示名称,主要用于模板集市列表中显示,支持多语言 - * `default`:默认语言,必须存在 - * `zh_CN`、`en_US` 等其他语言:可选,建议至少提供中文和英文 + * `default`:默认语言,必须存在 + * `zh_CN`、`en_US` 等其他语言:可选,建议至少提供中文和英文 * `description`:插件描述,主要用于插件集市列表中显示,支持多语言 - * `default`:默认语言,必须存在 - * `zh_CN`、`en_US` 等其他语言:可选,建议至少提供中文和英文 + * `default`:默认语言,必须存在 + * `zh_CN`、`en_US` 等其他语言:可选,建议至少提供中文和英文 * `readme`:自述文件名,主要用于插件集市详情页中显示,支持多语言 - * `default`:默认语言,必须存在 - * `zh_CN`、`en_US` 等其他语言:可选,建议至少提供中文和英文 + * `default`:默认语言,必须存在 + * `zh_CN`、`en_US` 等其他语言:可选,建议至少提供中文和英文 +* `i18n`:插件支持的语言列表 * `funding`:插件赞助信息 - * `openCollective`:Open Collective 名称 - * `patreon`:Patreon 名称 - * `github`:GitHub 登录名 - * `custom`:自定义赞助链接列表 -* `keywords`:搜索关键字列表,用于集市搜索功能 + * `openCollective`:Open Collective 名称 + * `patreon`:Patreon 名称 + * `github`:GitHub 登录名 + * `custom`:自定义赞助链接列表 ## 打包 无论使用何种方式编译打包,我们最终需要生成一个 package.zip,它至少包含如下文件: -* i18n/* -* icon.png (160*160) -* index.css +* icon.png * index.js * plugin.json -* preview.png (1024*768) +* preview.png * README*.md +* index.css (optional) +* i18n/* (optional) ## 上架集市 @@ -223,49 +151,4 @@ PR 社区集市仓库。 prerelease: true # 把这个改为 false ``` -## 如何去掉 svelte 依赖 -> 无 Svelte 依赖版: https://github.com/frostime/plugin-sample-vite - -本插件使用 vite 打包,并提供了 svelte 框架依赖。不过实际情况下可能有些开发者并不想要 svelte,只希望使用 vite 打包。 - -实际上你可以完全不做任何修改,就可以在不使用 svelte 的前提下使用这个模板。与 svelte 编译的编译相关的部分是以插件的形式载入到 vite 的工作流中,所以即使你的项目里面没有 svelte,也不会有太大的影响。 - -如果你执意希望删除掉所有 svelte 依赖以免它们污染你的工作空间,可以执行一下步骤: - -1. 删掉 package.json 中的 - ```json - { - "@sveltejs/vite-plugin-svelte": "^2.0.3", - "@tsconfig/svelte": "^4.0.1", - "svelte": "^3.57.0" - } - ``` -2. 删掉 `svelte.config.js` 文件 -3. 删掉 `vite.config.js` 文件中的 - - 第六行: `import { svelte } from "@sveltejs/vite-plugin-svelte"` - - 第二十行: `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 创建日记),请手动为文档添加该属性 diff --git a/package.json b/package.json index 2b7cdec..2f98a3b 100644 --- a/package.json +++ b/package.json @@ -1,37 +1,32 @@ { "name": "plugin-sample-vite-svelte", - "version": "0.3.6", + "version": "0.0.2", "type": "module", - "description": "This is a sample plugin based on vite and svelte for Siyuan (https://b3log.org/siyuan)", + "description": "", "repository": "", "homepage": "", - "author": "frostime", - "license": "MIT", + "author": "", + "license": "GPL-3.0", "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", - "update-version": "node --no-warnings ./scripts/update_version.js", - "make-install": "vite build && node --no-warnings ./scripts/make_install.js" + "make-link": "node ./scripts/make_dev_link.js", + "dev": "vite build --watch", + "build": "vite build" }, "devDependencies": { - "@sveltejs/vite-plugin-svelte": "^3.1.0", + "@sveltejs/vite-plugin-svelte": "^2.0.3", "@tsconfig/svelte": "^4.0.1", - "@types/node": "^20.3.0", - "cross-env": "^7.0.3", + "@types/node": "^20.2.0", "fast-glob": "^3.2.12", - "glob": "^10.0.0", - "js-yaml": "^4.1.0", + "glob": "^7.2.3", "minimist": "^1.2.8", "rollup-plugin-livereload": "^2.0.5", - "sass": "^1.63.3", - "siyuan": "1.0.4", - "svelte": "^4.2.19", + "sass": "^1.62.1", + "siyuan": "^0.7.1", + "svelte": "^3.57.0", "ts-node": "^10.9.1", - "typescript": "^5.1.3", - "vite": "^5.2.9", - "vite-plugin-static-copy": "^1.0.2", + "typescript": "^5.0.4", + "vite": "^4.3.7", + "vite-plugin-static-copy": "^0.15.0", "vite-plugin-zip-pack": "^1.0.5" } -} \ No newline at end of file +} diff --git a/plugin.json b/plugin.json index fd63a3c..60a4ede 100644 --- a/plugin.json +++ b/plugin.json @@ -2,24 +2,7 @@ "name": "plugin-sample-vite-svelte", "author": "frostime", "url": "https://github.com/siyuan-note/plugin-sample-vite-svelte", - "version": "0.3.6", - "minAppVersion": "3.0.12", - "backends": [ - "windows", - "linux", - "darwin", - "ios", - "android", - "harmony", - "docker" - ], - "frontends": [ - "desktop", - "mobile", - "browser-desktop", - "browser-mobile", - "desktop-window" - ], + "version": "0.0.2", "displayName": { "en_US": "Plugin sample with vite and svelte", "zh_CN": "插件样例 vite + svelte 版" @@ -32,14 +15,13 @@ "en_US": "README.md", "zh_CN": "README_zh_CN.md" }, + "i18n": [ + "en_US", + "zh_CN" + ], "funding": { "custom": [ - "" + "https://afdian.net/a/frostime" ] - }, - "keywords": [ - "plugin", - "sample", - "插件样例" - ] -} + } +} \ No newline at end of file diff --git a/pnpm-lock.yaml b/pnpm-lock.yaml new file mode 100644 index 0000000..a0021e2 --- /dev/null +++ b/pnpm-lock.yaml @@ -0,0 +1,955 @@ +lockfileVersion: '6.0' + +devDependencies: + '@sveltejs/vite-plugin-svelte': + specifier: ^2.0.3 + version: 2.0.3(svelte@3.59.1)(vite@4.3.7) + '@tsconfig/svelte': + specifier: ^4.0.1 + version: 4.0.1 + '@types/node': + specifier: ^20.2.0 + version: 20.2.0 + fast-glob: + specifier: ^3.2.12 + version: 3.2.12 + glob: + specifier: ^7.2.3 + version: 7.2.3 + minimist: + specifier: ^1.2.8 + version: 1.2.8 + rollup-plugin-livereload: + specifier: ^2.0.5 + version: 2.0.5 + sass: + specifier: ^1.62.1 + version: 1.62.1 + siyuan: + specifier: ^0.7.1 + version: 0.7.1 + svelte: + specifier: ^3.57.0 + version: 3.59.1 + ts-node: + specifier: ^10.9.1 + version: 10.9.1(@types/node@20.2.0)(typescript@5.0.4) + typescript: + specifier: ^5.0.4 + version: 5.0.4 + vite: + specifier: ^4.3.7 + version: 4.3.7(@types/node@20.2.0)(sass@1.62.1) + vite-plugin-static-copy: + specifier: ^0.15.0 + version: 0.15.0(vite@4.3.7) + vite-plugin-zip-pack: + specifier: ^1.0.5 + version: 1.0.5(vite@4.3.7) + +packages: + + /@cspotcode/source-map-support@0.8.1: + resolution: {integrity: sha512-IchNf6dN4tHoMFIn/7OE8LWZ19Y6q/67Bmf6vnGREv8RSbBVb9LPJxEcnwrcwX6ixSvaiGoomAUvu4YSxXrVgw==} + engines: {node: '>=12'} + dependencies: + '@jridgewell/trace-mapping': 0.3.9 + dev: true + + /@esbuild/android-arm64@0.17.19: + resolution: {integrity: sha512-KBMWvEZooR7+kzY0BtbTQn0OAYY7CsiydT63pVEaPtVYF0hXbUaOyZog37DKxK7NF3XacBJOpYT4adIJh+avxA==} + engines: {node: '>=12'} + cpu: [arm64] + os: [android] + requiresBuild: true + dev: true + optional: true + + /@esbuild/android-arm@0.17.19: + resolution: {integrity: sha512-rIKddzqhmav7MSmoFCmDIb6e2W57geRsM94gV2l38fzhXMwq7hZoClug9USI2pFRGL06f4IOPHHpFNOkWieR8A==} + engines: {node: '>=12'} + cpu: [arm] + os: [android] + requiresBuild: true + dev: true + optional: true + + /@esbuild/android-x64@0.17.19: + resolution: {integrity: sha512-uUTTc4xGNDT7YSArp/zbtmbhO0uEEK9/ETW29Wk1thYUJBz3IVnvgEiEwEa9IeLyvnpKrWK64Utw2bgUmDveww==} + engines: {node: '>=12'} + cpu: [x64] + os: [android] + requiresBuild: true + dev: true + optional: true + + /@esbuild/darwin-arm64@0.17.19: + resolution: {integrity: sha512-80wEoCfF/hFKM6WE1FyBHc9SfUblloAWx6FJkFWTWiCoht9Mc0ARGEM47e67W9rI09YoUxJL68WHfDRYEAvOhg==} + engines: {node: '>=12'} + cpu: [arm64] + os: [darwin] + requiresBuild: true + dev: true + optional: true + + /@esbuild/darwin-x64@0.17.19: + resolution: {integrity: sha512-IJM4JJsLhRYr9xdtLytPLSH9k/oxR3boaUIYiHkAawtwNOXKE8KoU8tMvryogdcT8AU+Bflmh81Xn6Q0vTZbQw==} + engines: {node: '>=12'} + cpu: [x64] + os: [darwin] + requiresBuild: true + dev: true + optional: true + + /@esbuild/freebsd-arm64@0.17.19: + resolution: {integrity: sha512-pBwbc7DufluUeGdjSU5Si+P3SoMF5DQ/F/UmTSb8HXO80ZEAJmrykPyzo1IfNbAoaqw48YRpv8shwd1NoI0jcQ==} + engines: {node: '>=12'} + cpu: [arm64] + os: [freebsd] + requiresBuild: true + dev: true + optional: true + + /@esbuild/freebsd-x64@0.17.19: + resolution: {integrity: sha512-4lu+n8Wk0XlajEhbEffdy2xy53dpR06SlzvhGByyg36qJw6Kpfk7cp45DR/62aPH9mtJRmIyrXAS5UWBrJT6TQ==} + engines: {node: '>=12'} + cpu: [x64] + os: [freebsd] + requiresBuild: true + dev: true + optional: true + + /@esbuild/linux-arm64@0.17.19: + resolution: {integrity: sha512-ct1Tg3WGwd3P+oZYqic+YZF4snNl2bsnMKRkb3ozHmnM0dGWuxcPTTntAF6bOP0Sp4x0PjSF+4uHQ1xvxfRKqg==} + engines: {node: '>=12'} + cpu: [arm64] + os: [linux] + requiresBuild: true + dev: true + optional: true + + /@esbuild/linux-arm@0.17.19: + resolution: {integrity: sha512-cdmT3KxjlOQ/gZ2cjfrQOtmhG4HJs6hhvm3mWSRDPtZ/lP5oe8FWceS10JaSJC13GBd4eH/haHnqf7hhGNLerA==} + engines: {node: '>=12'} + cpu: [arm] + os: [linux] + requiresBuild: true + dev: true + optional: true + + /@esbuild/linux-ia32@0.17.19: + resolution: {integrity: sha512-w4IRhSy1VbsNxHRQpeGCHEmibqdTUx61Vc38APcsRbuVgK0OPEnQ0YD39Brymn96mOx48Y2laBQGqgZ0j9w6SQ==} + engines: {node: '>=12'} + cpu: [ia32] + os: [linux] + requiresBuild: true + dev: true + optional: true + + /@esbuild/linux-loong64@0.17.19: + resolution: {integrity: sha512-2iAngUbBPMq439a+z//gE+9WBldoMp1s5GWsUSgqHLzLJ9WoZLZhpwWuym0u0u/4XmZ3gpHmzV84PonE+9IIdQ==} + engines: {node: '>=12'} + cpu: [loong64] + os: [linux] + requiresBuild: true + dev: true + optional: true + + /@esbuild/linux-mips64el@0.17.19: + resolution: {integrity: sha512-LKJltc4LVdMKHsrFe4MGNPp0hqDFA1Wpt3jE1gEyM3nKUvOiO//9PheZZHfYRfYl6AwdTH4aTcXSqBerX0ml4A==} + engines: {node: '>=12'} + cpu: [mips64el] + os: [linux] + requiresBuild: true + dev: true + optional: true + + /@esbuild/linux-ppc64@0.17.19: + resolution: {integrity: sha512-/c/DGybs95WXNS8y3Ti/ytqETiW7EU44MEKuCAcpPto3YjQbyK3IQVKfF6nbghD7EcLUGl0NbiL5Rt5DMhn5tg==} + engines: {node: '>=12'} + cpu: [ppc64] + os: [linux] + requiresBuild: true + dev: true + optional: true + + /@esbuild/linux-riscv64@0.17.19: + resolution: {integrity: sha512-FC3nUAWhvFoutlhAkgHf8f5HwFWUL6bYdvLc/TTuxKlvLi3+pPzdZiFKSWz/PF30TB1K19SuCxDTI5KcqASJqA==} + engines: {node: '>=12'} + cpu: [riscv64] + os: [linux] + requiresBuild: true + dev: true + optional: true + + /@esbuild/linux-s390x@0.17.19: + resolution: {integrity: sha512-IbFsFbxMWLuKEbH+7sTkKzL6NJmG2vRyy6K7JJo55w+8xDk7RElYn6xvXtDW8HCfoKBFK69f3pgBJSUSQPr+4Q==} + engines: {node: '>=12'} + cpu: [s390x] + os: [linux] + requiresBuild: true + dev: true + optional: true + + /@esbuild/linux-x64@0.17.19: + resolution: {integrity: sha512-68ngA9lg2H6zkZcyp22tsVt38mlhWde8l3eJLWkyLrp4HwMUr3c1s/M2t7+kHIhvMjglIBrFpncX1SzMckomGw==} + engines: {node: '>=12'} + cpu: [x64] + os: [linux] + requiresBuild: true + dev: true + optional: true + + /@esbuild/netbsd-x64@0.17.19: + resolution: {integrity: sha512-CwFq42rXCR8TYIjIfpXCbRX0rp1jo6cPIUPSaWwzbVI4aOfX96OXY8M6KNmtPcg7QjYeDmN+DD0Wp3LaBOLf4Q==} + engines: {node: '>=12'} + cpu: [x64] + os: [netbsd] + requiresBuild: true + dev: true + optional: true + + /@esbuild/openbsd-x64@0.17.19: + resolution: {integrity: sha512-cnq5brJYrSZ2CF6c35eCmviIN3k3RczmHz8eYaVlNasVqsNY+JKohZU5MKmaOI+KkllCdzOKKdPs762VCPC20g==} + engines: {node: '>=12'} + cpu: [x64] + os: [openbsd] + requiresBuild: true + dev: true + optional: true + + /@esbuild/sunos-x64@0.17.19: + resolution: {integrity: sha512-vCRT7yP3zX+bKWFeP/zdS6SqdWB8OIpaRq/mbXQxTGHnIxspRtigpkUcDMlSCOejlHowLqII7K2JKevwyRP2rg==} + engines: {node: '>=12'} + cpu: [x64] + os: [sunos] + requiresBuild: true + dev: true + optional: true + + /@esbuild/win32-arm64@0.17.19: + resolution: {integrity: sha512-yYx+8jwowUstVdorcMdNlzklLYhPxjniHWFKgRqH7IFlUEa0Umu3KuYplf1HUZZ422e3NU9F4LGb+4O0Kdcaag==} + engines: {node: '>=12'} + cpu: [arm64] + os: [win32] + requiresBuild: true + dev: true + optional: true + + /@esbuild/win32-ia32@0.17.19: + resolution: {integrity: sha512-eggDKanJszUtCdlVs0RB+h35wNlb5v4TWEkq4vZcmVt5u/HiDZrTXe2bWFQUez3RgNHwx/x4sk5++4NSSicKkw==} + engines: {node: '>=12'} + cpu: [ia32] + os: [win32] + requiresBuild: true + dev: true + optional: true + + /@esbuild/win32-x64@0.17.19: + resolution: {integrity: sha512-lAhycmKnVOuRYNtRtatQR1LPQf2oYCkRGkSFnseDAKPl8lu5SOsK/e1sXe5a0Pc5kHIHe6P2I/ilntNv2xf3cA==} + engines: {node: '>=12'} + cpu: [x64] + os: [win32] + requiresBuild: true + dev: true + optional: true + + /@jridgewell/resolve-uri@3.1.1: + resolution: {integrity: sha512-dSYZh7HhCDtCKm4QakX0xFpsRDqjjtZf/kjI/v3T3Nwt5r8/qz/M19F9ySyOqU94SXBmeG9ttTul+YnR4LOxFA==} + engines: {node: '>=6.0.0'} + dev: true + + /@jridgewell/sourcemap-codec@1.4.15: + resolution: {integrity: sha512-eF2rxCRulEKXHTRiDrDy6erMYWqNw4LPdQ8UQA4huuxaQsVeRPFl2oM8oDGxMFhJUWZf9McpLtJasDDZb/Bpeg==} + dev: true + + /@jridgewell/trace-mapping@0.3.9: + resolution: {integrity: sha512-3Belt6tdc8bPgAtbcmdtNJlirVoTmEb5e2gC94PnkwEW9jI6CAHUeoG85tjWP5WquqfavoMtMwiG4P926ZKKuQ==} + dependencies: + '@jridgewell/resolve-uri': 3.1.1 + '@jridgewell/sourcemap-codec': 1.4.15 + dev: true + + /@nodelib/fs.scandir@2.1.5: + resolution: {integrity: sha512-vq24Bq3ym5HEQm2NKCr3yXDwjc7vTsEThRDnkp2DK9p1uqLR+DHurm/NOTo0KG7HYHU7eppKZj3MyqYuMBf62g==} + engines: {node: '>= 8'} + dependencies: + '@nodelib/fs.stat': 2.0.5 + run-parallel: 1.2.0 + dev: true + + /@nodelib/fs.stat@2.0.5: + resolution: {integrity: sha512-RkhPPp2zrqDAQA/2jNhnztcPAlv64XdhIp7a7454A5ovI7Bukxgt7MX7udwAu3zg1DcpPU0rz3VV1SeaqvY4+A==} + engines: {node: '>= 8'} + dev: true + + /@nodelib/fs.walk@1.2.8: + resolution: {integrity: sha512-oGB+UxlgWcgQkgwo8GcEGwemoTFt3FIO9ababBmaGwXIoBKZ+GTy0pP185beGg7Llih/NSHSV2XAs1lnznocSg==} + engines: {node: '>= 8'} + dependencies: + '@nodelib/fs.scandir': 2.1.5 + fastq: 1.15.0 + dev: true + + /@sveltejs/vite-plugin-svelte@2.0.3(svelte@3.59.1)(vite@4.3.7): + resolution: {integrity: sha512-o+cguBFdwIGtRbNkYOyqTM7KvRUffxh5bfK4oJsWKG2obu+v/cbpT03tJrGl58C7tRXo/aEC0/axN5FVHBj0nA==} + engines: {node: ^14.18.0 || >= 16} + peerDependencies: + svelte: ^3.54.0 + vite: ^4.0.0 + dependencies: + debug: 4.3.4 + deepmerge: 4.3.1 + kleur: 4.1.5 + magic-string: 0.29.0 + svelte: 3.59.1 + svelte-hmr: 0.15.1(svelte@3.59.1) + vite: 4.3.7(@types/node@20.2.0)(sass@1.62.1) + vitefu: 0.2.4(vite@4.3.7) + transitivePeerDependencies: + - supports-color + dev: true + + /@tsconfig/node10@1.0.9: + resolution: {integrity: sha512-jNsYVVxU8v5g43Erja32laIDHXeoNvFEpX33OK4d6hljo3jDhCBDhx5dhCCTMWUojscpAagGiRkBKxpdl9fxqA==} + dev: true + + /@tsconfig/node12@1.0.11: + resolution: {integrity: sha512-cqefuRsh12pWyGsIoBKJA9luFu3mRxCA+ORZvA4ktLSzIuCUtWVxGIuXigEwO5/ywWFMZ2QEGKWvkZG1zDMTag==} + dev: true + + /@tsconfig/node14@1.0.3: + resolution: {integrity: sha512-ysT8mhdixWK6Hw3i1V2AeRqZ5WfXg1G43mqoYlM2nc6388Fq5jcXyr5mRsqViLx/GJYdoL0bfXD8nmF+Zn/Iow==} + dev: true + + /@tsconfig/node16@1.0.4: + resolution: {integrity: sha512-vxhUy4J8lyeyinH7Azl1pdd43GJhZH/tP2weN8TntQblOY+A0XbT8DJk1/oCPuOOyg/Ja757rG0CgHcWC8OfMA==} + dev: true + + /@tsconfig/svelte@4.0.1: + resolution: {integrity: sha512-B+XlGpmuAQzJqDoBATNCvEPqQg0HkO7S8pM14QDI5NsmtymzRexQ1N+nX2H6RTtFbuFgaZD4I8AAi8voGg0GLg==} + dev: true + + /@types/node@20.2.0: + resolution: {integrity: sha512-3iD2jaCCziTx04uudpJKwe39QxXgSUnpxXSvRQjRvHPxFQfmfP4NXIm/NURVeNlTCc+ru4WqjYGTmpXrW9uMlw==} + dev: true + + /acorn-walk@8.2.0: + resolution: {integrity: sha512-k+iyHEuPgSw6SbuDpGQM+06HQUa04DZ3o+F6CSzXMvvI5KMvnaEqXe+YVe555R9nn6GPt404fos4wcgpw12SDA==} + engines: {node: '>=0.4.0'} + dev: true + + /acorn@8.8.2: + resolution: {integrity: sha512-xjIYgE8HBrkpd/sJqOGNspf8uHG+NOHGOw6a/Urj8taM2EXfdNAH2oFcPeIFfsv3+kz/mJrS5VuMqbNLjCa2vw==} + engines: {node: '>=0.4.0'} + hasBin: true + dev: true + + /anymatch@3.1.3: + resolution: {integrity: sha512-KMReFUr0B4t+D+OBkjR3KYqvocp2XaSzO55UcB6mgQMd3KbcE+mWTyvVV7D/zsdEbNnV6acZUutkiHQXvTr1Rw==} + engines: {node: '>= 8'} + dependencies: + normalize-path: 3.0.0 + picomatch: 2.3.1 + dev: true + + /arg@4.1.3: + resolution: {integrity: sha512-58S9QDqG0Xx27YwPSt9fJxivjYl432YCwfDMfZ+71RAqUrZef7LrKQZ3LHLOwCS4FLNBplP533Zx895SeOCHvA==} + dev: true + + /balanced-match@1.0.2: + resolution: {integrity: sha512-3oSeUO0TMV67hN1AmbXsK4yaqU7tjiHlbxRDZOpH0KW9+CeX4bRAaX0Anxt0tx2MrpRpWwQaPwIlISEJhYU5Pw==} + dev: true + + /binary-extensions@2.2.0: + resolution: {integrity: sha512-jDctJ/IVQbZoJykoeHbhXpOlNBqGNcwXJKJog42E5HDPUwQTSdjCHdihjj0DlnheQ7blbT6dHOafNAiS8ooQKA==} + engines: {node: '>=8'} + dev: true + + /brace-expansion@1.1.11: + resolution: {integrity: sha512-iCuPHDFgrHX7H2vEI/5xpz07zSHB00TpugqhmYtVmMO6518mCuRMoOYFldEBl0g187ufozdaHgWKcYFb61qGiA==} + dependencies: + balanced-match: 1.0.2 + concat-map: 0.0.1 + dev: true + + /braces@3.0.2: + resolution: {integrity: sha512-b8um+L1RzM3WDSzvhm6gIz1yfTbBt6YTlcEKAvsmqCZZFw46z626lVj9j1yEPW33H5H+lBQpZMP1k8l+78Ha0A==} + engines: {node: '>=8'} + dependencies: + fill-range: 7.0.1 + dev: true + + /chokidar@3.5.3: + resolution: {integrity: sha512-Dr3sfKRP6oTcjf2JmUmFJfeVMvXBdegxB0iVQ5eb2V10uFJUCAS8OByZdVAyVb8xXNz3GjjTgj9kLWsZTqE6kw==} + engines: {node: '>= 8.10.0'} + dependencies: + anymatch: 3.1.3 + braces: 3.0.2 + glob-parent: 5.1.2 + is-binary-path: 2.1.0 + is-glob: 4.0.3 + normalize-path: 3.0.0 + readdirp: 3.6.0 + optionalDependencies: + fsevents: 2.3.2 + dev: true + + /concat-map@0.0.1: + resolution: {integrity: sha512-/Srv4dswyQNBfohGpz9o6Yb3Gz3SrUDqBH5rTuhGR7ahtlbYKnVxw2bCFMRljaA7EXHaXZ8wsHdodFvbkhKmqg==} + dev: true + + /core-util-is@1.0.3: + resolution: {integrity: sha512-ZQBvi1DcpJ4GDqanjucZ2Hj3wEO5pZDS89BWbkcrvdxksJorwUDDZamX9ldFkp9aw2lmBDLgkObEA4DWNJ9FYQ==} + dev: true + + /create-require@1.1.1: + resolution: {integrity: sha512-dcKFX3jn0MpIaXjisoRvexIJVEKzaq7z2rZKxf+MSr9TkdmHmsU4m2lcLojrj/FHl8mk5VxMmYA+ftRkP/3oKQ==} + dev: true + + /debug@4.3.4: + resolution: {integrity: sha512-PRWFHuSU3eDtQJPvnNY7Jcket1j0t5OuOsFzPPzsekD52Zl8qUfFIPEiswXqIvHWGVHOgX+7G/vCNNhehwxfkQ==} + engines: {node: '>=6.0'} + peerDependencies: + supports-color: '*' + peerDependenciesMeta: + supports-color: + optional: true + dependencies: + ms: 2.1.2 + dev: true + + /deepmerge@4.3.1: + resolution: {integrity: sha512-3sUqbMEc77XqpdNO7FRyRog+eW3ph+GYCbj+rK+uYyRMuwsVy0rMiVtPn+QJlKFvWP/1PYpapqYn0Me2knFn+A==} + engines: {node: '>=0.10.0'} + dev: true + + /diff@4.0.2: + resolution: {integrity: sha512-58lmxKSA4BNyLz+HHMUzlOEpg09FV+ev6ZMe3vJihgdxzgcwZ8VoEEPmALCZG9LmqfVoNMMKpttIYTVG6uDY7A==} + engines: {node: '>=0.3.1'} + dev: true + + /esbuild@0.17.19: + resolution: {integrity: sha512-XQ0jAPFkK/u3LcVRcvVHQcTIqD6E2H1fvZMA5dQPSOWb3suUbWbfbRf94pjc0bNzRYLfIrDRQXr7X+LHIm5oHw==} + engines: {node: '>=12'} + hasBin: true + requiresBuild: true + optionalDependencies: + '@esbuild/android-arm': 0.17.19 + '@esbuild/android-arm64': 0.17.19 + '@esbuild/android-x64': 0.17.19 + '@esbuild/darwin-arm64': 0.17.19 + '@esbuild/darwin-x64': 0.17.19 + '@esbuild/freebsd-arm64': 0.17.19 + '@esbuild/freebsd-x64': 0.17.19 + '@esbuild/linux-arm': 0.17.19 + '@esbuild/linux-arm64': 0.17.19 + '@esbuild/linux-ia32': 0.17.19 + '@esbuild/linux-loong64': 0.17.19 + '@esbuild/linux-mips64el': 0.17.19 + '@esbuild/linux-ppc64': 0.17.19 + '@esbuild/linux-riscv64': 0.17.19 + '@esbuild/linux-s390x': 0.17.19 + '@esbuild/linux-x64': 0.17.19 + '@esbuild/netbsd-x64': 0.17.19 + '@esbuild/openbsd-x64': 0.17.19 + '@esbuild/sunos-x64': 0.17.19 + '@esbuild/win32-arm64': 0.17.19 + '@esbuild/win32-ia32': 0.17.19 + '@esbuild/win32-x64': 0.17.19 + dev: true + + /fast-glob@3.2.12: + resolution: {integrity: sha512-DVj4CQIYYow0BlaelwK1pHl5n5cRSJfM60UA0zK891sVInoPri2Ekj7+e1CT3/3qxXenpI+nBBmQAcJPJgaj4w==} + engines: {node: '>=8.6.0'} + dependencies: + '@nodelib/fs.stat': 2.0.5 + '@nodelib/fs.walk': 1.2.8 + glob-parent: 5.1.2 + merge2: 1.4.1 + micromatch: 4.0.5 + dev: true + + /fastq@1.15.0: + resolution: {integrity: sha512-wBrocU2LCXXa+lWBt8RoIRD89Fi8OdABODa/kEnyeyjS5aZO5/GNvI5sEINADqP/h8M29UHTHUb53sUu5Ihqdw==} + dependencies: + reusify: 1.0.4 + dev: true + + /fill-range@7.0.1: + resolution: {integrity: sha512-qOo9F+dMUmC2Lcb4BbVvnKJxTPjCm+RRpe4gDuGrzkL7mEVl/djYSu2OdQ2Pa302N4oqkSg9ir6jaLWJ2USVpQ==} + engines: {node: '>=8'} + dependencies: + to-regex-range: 5.0.1 + dev: true + + /fs-extra@11.1.1: + resolution: {integrity: sha512-MGIE4HOvQCeUCzmlHs0vXpih4ysz4wg9qiSAu6cd42lVwPbTM1TjV7RusoyQqMmk/95gdQZX72u+YW+c3eEpFQ==} + engines: {node: '>=14.14'} + dependencies: + graceful-fs: 4.2.11 + jsonfile: 6.1.0 + universalify: 2.0.0 + dev: true + + /fs.realpath@1.0.0: + resolution: {integrity: sha512-OO0pH2lK6a0hZnAdau5ItzHPI6pUlvI7jMVnxUQRtw4owF2wk8lOSabtGDCTP4Ggrg2MbGnWO9X8K1t4+fGMDw==} + dev: true + + /fsevents@2.3.2: + resolution: {integrity: sha512-xiqMQR4xAeHTuB9uWm+fFRcIOgKBMiOBP+eXiyT7jsgVCq1bkVygt00oASowB7EdtpOHaaPgKt812P9ab+DDKA==} + engines: {node: ^8.16.0 || ^10.6.0 || >=11.0.0} + os: [darwin] + requiresBuild: true + dev: true + optional: true + + /glob-parent@5.1.2: + resolution: {integrity: sha512-AOIgSQCepiJYwP3ARnGx+5VnTu2HBYdzbGP45eLw1vr3zB3vZLeyed1sC9hnbcOc9/SrMyM5RPQrkGz4aS9Zow==} + engines: {node: '>= 6'} + dependencies: + is-glob: 4.0.3 + dev: true + + /glob@7.2.3: + resolution: {integrity: sha512-nFR0zLpU2YCaRxwoCJvL6UvCH2JFyFVIvwTLsIf21AuHlMskA1hhTdk+LlYJtOlYt9v6dvszD2BGRqBL+iQK9Q==} + dependencies: + fs.realpath: 1.0.0 + inflight: 1.0.6 + inherits: 2.0.4 + minimatch: 3.1.2 + once: 1.4.0 + path-is-absolute: 1.0.1 + dev: true + + /graceful-fs@4.2.11: + resolution: {integrity: sha512-RbJ5/jmFcNNCcDV5o9eTnBLJ/HszWV0P73bc+Ff4nS/rJj+YaS6IGyiOL0VoBYX+l1Wrl3k63h/KrH+nhJ0XvQ==} + dev: true + + /immediate@3.0.6: + resolution: {integrity: sha512-XXOFtyqDjNDAQxVfYxuF7g9Il/IbWmmlQg2MYKOH8ExIT1qg6xc4zyS3HaEEATgs1btfzxq15ciUiY7gjSXRGQ==} + dev: true + + /immutable@4.3.0: + resolution: {integrity: sha512-0AOCmOip+xgJwEVTQj1EfiDDOkPmuyllDuTuEX+DDXUgapLAsBIfkg3sxCYyCEA8mQqZrrxPUGjcOQ2JS3WLkg==} + dev: true + + /inflight@1.0.6: + resolution: {integrity: sha512-k92I/b08q4wvFscXCLvqfsHCrjrF7yiXsQuIVvVE7N82W3+aqpzuUdBbfhWcy/FZR3/4IgflMgKLOsvPDrGCJA==} + dependencies: + once: 1.4.0 + wrappy: 1.0.2 + dev: true + + /inherits@2.0.4: + resolution: {integrity: sha512-k/vGaX4/Yla3WzyMCvTQOXYeIHvqOKtnqBduzTHpzpQZzAskKMhZ2K+EnBiSM9zGSoIFeMpXKxa4dYeZIQqewQ==} + dev: true + + /is-binary-path@2.1.0: + resolution: {integrity: sha512-ZMERYes6pDydyuGidse7OsHxtbI7WVeUEozgR/g7rd0xUimYNlvZRE/K2MgZTjWy725IfelLeVcEM97mmtRGXw==} + engines: {node: '>=8'} + dependencies: + binary-extensions: 2.2.0 + dev: true + + /is-extglob@2.1.1: + resolution: {integrity: sha512-SbKbANkN603Vi4jEZv49LeVJMn4yGwsbzZworEoyEiutsN3nJYdbO36zfhGJ6QEDpOZIFkDtnq5JRxmvl3jsoQ==} + engines: {node: '>=0.10.0'} + dev: true + + /is-glob@4.0.3: + resolution: {integrity: sha512-xelSayHH36ZgE7ZWhli7pW34hNbNl8Ojv5KVmkJD4hBdD3th8Tfk9vYasLM+mXWOZhFkgZfxhLSnrwRr4elSSg==} + engines: {node: '>=0.10.0'} + dependencies: + is-extglob: 2.1.1 + dev: true + + /is-number@7.0.0: + resolution: {integrity: sha512-41Cifkg6e8TylSpdtTpeLVMqvSBEVzTttHvERD741+pnZ8ANv0004MRL43QKPDlK9cGvNp6NZWZUBlbGXYxxng==} + engines: {node: '>=0.12.0'} + dev: true + + /isarray@1.0.0: + resolution: {integrity: sha512-VLghIWNM6ELQzo7zwmcg0NmTVyWKYjvIeM83yjp0wRDTmUnrM678fQbcKBo6n2CJEF0szoG//ytg+TKla89ALQ==} + dev: true + + /jsonfile@6.1.0: + resolution: {integrity: sha512-5dgndWOriYSm5cnYaJNhalLNDKOqFwyDB/rr1E9ZsGciGvKPs8R2xYGCacuf3z6K1YKDz182fd+fY3cn3pMqXQ==} + dependencies: + universalify: 2.0.0 + optionalDependencies: + graceful-fs: 4.2.11 + dev: true + + /jszip@3.10.1: + resolution: {integrity: sha512-xXDvecyTpGLrqFrvkrUSoxxfJI5AH7U8zxxtVclpsUtMCq4JQ290LY8AW5c7Ggnr/Y/oK+bQMbqK2qmtk3pN4g==} + dependencies: + lie: 3.3.0 + pako: 1.0.11 + readable-stream: 2.3.8 + setimmediate: 1.0.5 + dev: true + + /kleur@4.1.5: + resolution: {integrity: sha512-o+NO+8WrRiQEE4/7nwRJhN1HWpVmJm511pBHUxPLtp0BUISzlBplORYSmTclCnJvQq2tKu/sgl3xVpkc7ZWuQQ==} + engines: {node: '>=6'} + dev: true + + /lie@3.3.0: + resolution: {integrity: sha512-UaiMJzeWRlEujzAuw5LokY1L5ecNQYZKfmyZ9L7wDHb/p5etKaxXhohBcrw0EYby+G/NA52vRSN4N39dxHAIwQ==} + dependencies: + immediate: 3.0.6 + dev: true + + /livereload-js@3.4.1: + resolution: {integrity: sha512-5MP0uUeVCec89ZbNOT/i97Mc+q3SxXmiUGhRFOTmhrGPn//uWVQdCvcLJDy64MSBR5MidFdOR7B9viumoavy6g==} + dev: true + + /livereload@0.9.3: + resolution: {integrity: sha512-q7Z71n3i4X0R9xthAryBdNGVGAO2R5X+/xXpmKeuPMrteg+W2U8VusTKV3YiJbXZwKsOlFlHe+go6uSNjfxrZw==} + engines: {node: '>=8.0.0'} + hasBin: true + dependencies: + chokidar: 3.5.3 + livereload-js: 3.4.1 + opts: 2.0.2 + ws: 7.5.9 + transitivePeerDependencies: + - bufferutil + - utf-8-validate + dev: true + + /magic-string@0.29.0: + resolution: {integrity: sha512-WcfidHrDjMY+eLjlU+8OvwREqHwpgCeKVBUpQ3OhYYuvfaYCUgcbuBzappNzZvg/v8onU3oQj+BYpkOJe9Iw4Q==} + engines: {node: '>=12'} + dependencies: + '@jridgewell/sourcemap-codec': 1.4.15 + dev: true + + /make-error@1.3.6: + resolution: {integrity: sha512-s8UhlNe7vPKomQhC1qFelMokr/Sc3AgNbso3n74mVPA5LTZwkB9NlXf4XPamLxJE8h0gh73rM94xvwRT2CVInw==} + dev: true + + /merge2@1.4.1: + resolution: {integrity: sha512-8q7VEgMJW4J8tcfVPy8g09NcQwZdbwFEqhe/WZkoIzjn/3TGDwtOCYtXGxA3O8tPzpczCCDgv+P2P5y00ZJOOg==} + engines: {node: '>= 8'} + dev: true + + /micromatch@4.0.5: + resolution: {integrity: sha512-DMy+ERcEW2q8Z2Po+WNXuw3c5YaUSFjAO5GsJqfEl7UjvtIuFKO6ZrKvcItdy98dwFI2N1tg3zNIdKaQT+aNdA==} + engines: {node: '>=8.6'} + dependencies: + braces: 3.0.2 + picomatch: 2.3.1 + dev: true + + /minimatch@3.1.2: + resolution: {integrity: sha512-J7p63hRiAjw1NDEww1W7i37+ByIrOWO5XQQAzZ3VOcL0PNybwpfmV/N05zFAzwQ9USyEcX6t3UO+K5aqBQOIHw==} + dependencies: + brace-expansion: 1.1.11 + dev: true + + /minimist@1.2.8: + resolution: {integrity: sha512-2yyAR8qBkN3YuheJanUpWC5U3bb5osDywNB8RzDVlDwDHbocAJveqqj1u8+SVD7jkWT4yvsHCpWqqWqAxb0zCA==} + dev: true + + /ms@2.1.2: + resolution: {integrity: sha512-sGkPx+VjMtmA6MX27oA4FBFELFCZZ4S4XqeGOXCv68tT+jb3vk/RyaKWP0PTKyWtmLSM0b+adUTEvbs1PEaH2w==} + dev: true + + /nanoid@3.3.6: + resolution: {integrity: sha512-BGcqMMJuToF7i1rt+2PWSNVnWIkGCU78jBG3RxO/bZlnZPK2Cmi2QaffxGO/2RvWi9sL+FAiRiXMgsyxQ1DIDA==} + engines: {node: ^10 || ^12 || ^13.7 || ^14 || >=15.0.1} + hasBin: true + dev: true + + /normalize-path@3.0.0: + resolution: {integrity: sha512-6eZs5Ls3WtCisHWp9S2GUy8dqkpGi4BVSz3GaqiE6ezub0512ESztXUwUB6C6IKbQkY2Pnb/mD4WYojCRwcwLA==} + engines: {node: '>=0.10.0'} + dev: true + + /once@1.4.0: + resolution: {integrity: sha512-lNaJgI+2Q5URQBkccEKHTQOPaXdUxnZZElQTZY0MFUAuaEqe1E+Nyvgdz/aIyNi6Z9MzO5dv1H8n58/GELp3+w==} + dependencies: + wrappy: 1.0.2 + dev: true + + /opts@2.0.2: + resolution: {integrity: sha512-k41FwbcLnlgnFh69f4qdUfvDQ+5vaSDnVPFI/y5XuhKRq97EnVVneO9F1ESVCdiVu4fCS2L8usX3mU331hB7pg==} + dev: true + + /pako@1.0.11: + resolution: {integrity: sha512-4hLB8Py4zZce5s4yd9XzopqwVv/yGNhV1Bl8NTmCq1763HeK2+EwVTv+leGeL13Dnh2wfbqowVPXCIO0z4taYw==} + dev: true + + /path-is-absolute@1.0.1: + resolution: {integrity: sha512-AVbw3UJ2e9bq64vSaS9Am0fje1Pa8pbGqTTsmXfaIiMpnr5DlDhfJOuLj9Sf95ZPVDAUerDfEk88MPmPe7UCQg==} + engines: {node: '>=0.10.0'} + dev: true + + /picocolors@1.0.0: + resolution: {integrity: sha512-1fygroTLlHu66zi26VoTDv8yRgm0Fccecssto+MhsZ0D/DGW2sm8E8AjW7NU5VVTRt5GxbeZ5qBuJr+HyLYkjQ==} + dev: true + + /picomatch@2.3.1: + resolution: {integrity: sha512-JU3teHTNjmE2VCGFzuY8EXzCDVwEqB2a8fsIvwaStHhAWJEeVd1o1QD80CU6+ZdEXXSLbSsuLwJjkCBWqRQUVA==} + engines: {node: '>=8.6'} + dev: true + + /postcss@8.4.23: + resolution: {integrity: sha512-bQ3qMcpF6A/YjR55xtoTr0jGOlnPOKAIMdOWiv0EIT6HVPEaJiJB4NLljSbiHoC2RX7DN5Uvjtpbg1NPdwv1oA==} + engines: {node: ^10 || ^12 || >=14} + dependencies: + nanoid: 3.3.6 + picocolors: 1.0.0 + source-map-js: 1.0.2 + dev: true + + /process-nextick-args@2.0.1: + resolution: {integrity: sha512-3ouUOpQhtgrbOa17J7+uxOTpITYWaGP7/AhoR3+A+/1e9skrzelGi/dXzEYyvbxubEF6Wn2ypscTKiKJFFn1ag==} + dev: true + + /queue-microtask@1.2.3: + resolution: {integrity: sha512-NuaNSa6flKT5JaSYQzJok04JzTL1CA6aGhv5rfLW3PgqA+M2ChpZQnAC8h8i4ZFkBS8X5RqkDBHA7r4hej3K9A==} + dev: true + + /readable-stream@2.3.8: + resolution: {integrity: sha512-8p0AUk4XODgIewSi0l8Epjs+EVnWiK7NoDIEGU0HhE7+ZyY8D1IMY7odu5lRrFXGg71L15KG8QrPmum45RTtdA==} + dependencies: + core-util-is: 1.0.3 + inherits: 2.0.4 + isarray: 1.0.0 + process-nextick-args: 2.0.1 + safe-buffer: 5.1.2 + string_decoder: 1.1.1 + util-deprecate: 1.0.2 + dev: true + + /readdirp@3.6.0: + resolution: {integrity: sha512-hOS089on8RduqdbhvQ5Z37A0ESjsqz6qnRcffsMU3495FuTdqSm+7bhJ29JvIOsBDEEnan5DPu9t3To9VRlMzA==} + engines: {node: '>=8.10.0'} + dependencies: + picomatch: 2.3.1 + dev: true + + /reusify@1.0.4: + resolution: {integrity: sha512-U9nH88a3fc/ekCF1l0/UP1IosiuIjyTh7hBvXVMHYgVcfGvt897Xguj2UOLDeI5BG2m7/uwyaLVT6fbtCwTyzw==} + engines: {iojs: '>=1.0.0', node: '>=0.10.0'} + dev: true + + /rollup-plugin-livereload@2.0.5: + resolution: {integrity: sha512-vqQZ/UQowTW7VoiKEM5ouNW90wE5/GZLfdWuR0ELxyKOJUIaj+uismPZZaICU4DnWPVjnpCDDxEqwU7pcKY/PA==} + engines: {node: '>=8.3'} + dependencies: + livereload: 0.9.3 + transitivePeerDependencies: + - bufferutil + - utf-8-validate + dev: true + + /rollup@3.21.7: + resolution: {integrity: sha512-KXPaEuR8FfUoK2uHwNjxTmJ18ApyvD6zJpYv9FOJSqLStmt6xOY84l1IjK2dSolQmoXknrhEFRaPRgOPdqCT5w==} + engines: {node: '>=14.18.0', npm: '>=8.0.0'} + hasBin: true + optionalDependencies: + fsevents: 2.3.2 + dev: true + + /run-parallel@1.2.0: + resolution: {integrity: sha512-5l4VyZR86LZ/lDxZTR6jqL8AFE2S0IFLMP26AbjsLVADxHdhB/c0GUsH+y39UfCi3dzz8OlQuPmnaJOMoDHQBA==} + dependencies: + queue-microtask: 1.2.3 + dev: true + + /safe-buffer@5.1.2: + resolution: {integrity: sha512-Gd2UZBJDkXlY7GbJxfsE8/nvKkUEU1G38c1siN6QP6a9PT9MmHB8GnpscSmMJSoF8LOIrt8ud/wPtojys4G6+g==} + dev: true + + /sass@1.62.1: + resolution: {integrity: sha512-NHpxIzN29MXvWiuswfc1W3I0N8SXBd8UR26WntmDlRYf0bSADnwnOjsyMZ3lMezSlArD33Vs3YFhp7dWvL770A==} + engines: {node: '>=14.0.0'} + hasBin: true + dependencies: + chokidar: 3.5.3 + immutable: 4.3.0 + source-map-js: 1.0.2 + dev: true + + /setimmediate@1.0.5: + resolution: {integrity: sha512-MATJdZp8sLqDl/68LfQmbP8zKPLQNV6BIZoIgrscFDQ+RsvK/BxeDQOgyxKKoh0y/8h3BqVFnCqQ/gd+reiIXA==} + dev: true + + /siyuan@0.7.1: + resolution: {integrity: sha512-Q7OZqpJ8h+axLDkn6afoAdKD6mHPAona/jsoUpf8UgFqHuOscNS6ub8RudhTCPKppDnQzBy5o35xhlecxnrbjQ==} + dev: true + + /source-map-js@1.0.2: + resolution: {integrity: sha512-R0XvVJ9WusLiqTCEiGCmICCMplcCkIwwR11mOSD9CR5u+IXYdiseeEuXCVAjS54zqwkLcPNnmU4OeJ6tUrWhDw==} + engines: {node: '>=0.10.0'} + dev: true + + /string_decoder@1.1.1: + resolution: {integrity: sha512-n/ShnvDi6FHbbVfviro+WojiFzv+s8MPMHBczVePfUpDJLwoLT0ht1l4YwBCbi8pJAveEEdnkHyPyTP/mzRfwg==} + dependencies: + safe-buffer: 5.1.2 + dev: true + + /svelte-hmr@0.15.1(svelte@3.59.1): + resolution: {integrity: sha512-BiKB4RZ8YSwRKCNVdNxK/GfY+r4Kjgp9jCLEy0DuqAKfmQtpL38cQK3afdpjw4sqSs4PLi3jIPJIFp259NkZtA==} + engines: {node: ^12.20 || ^14.13.1 || >= 16} + peerDependencies: + svelte: '>=3.19.0' + dependencies: + svelte: 3.59.1 + dev: true + + /svelte@3.59.1: + resolution: {integrity: sha512-pKj8fEBmqf6mq3/NfrB9SLtcJcUvjYSWyePlfCqN9gujLB25RitWK8PvFzlwim6hD/We35KbPlRteuA6rnPGcQ==} + engines: {node: '>= 8'} + dev: true + + /to-regex-range@5.0.1: + resolution: {integrity: sha512-65P7iz6X5yEr1cwcgvQxbbIw7Uk3gOy5dIdtZ4rDveLqhrdJP+Li/Hx6tyK0NEb+2GCyneCMJiGqrADCSNk8sQ==} + engines: {node: '>=8.0'} + dependencies: + is-number: 7.0.0 + dev: true + + /ts-node@10.9.1(@types/node@20.2.0)(typescript@5.0.4): + resolution: {integrity: sha512-NtVysVPkxxrwFGUUxGYhfux8k78pQB3JqYBXlLRZgdGUqTO5wU/UyHop5p70iEbGhB7q5KmiZiU0Y3KlJrScEw==} + hasBin: true + peerDependencies: + '@swc/core': '>=1.2.50' + '@swc/wasm': '>=1.2.50' + '@types/node': '*' + typescript: '>=2.7' + peerDependenciesMeta: + '@swc/core': + optional: true + '@swc/wasm': + optional: true + dependencies: + '@cspotcode/source-map-support': 0.8.1 + '@tsconfig/node10': 1.0.9 + '@tsconfig/node12': 1.0.11 + '@tsconfig/node14': 1.0.3 + '@tsconfig/node16': 1.0.4 + '@types/node': 20.2.0 + acorn: 8.8.2 + acorn-walk: 8.2.0 + arg: 4.1.3 + create-require: 1.1.1 + diff: 4.0.2 + make-error: 1.3.6 + typescript: 5.0.4 + v8-compile-cache-lib: 3.0.1 + yn: 3.1.1 + dev: true + + /typescript@5.0.4: + resolution: {integrity: sha512-cW9T5W9xY37cc+jfEnaUvX91foxtHkza3Nw3wkoF4sSlKn0MONdkdEndig/qPBWXNkmplh3NzayQzCiHM4/hqw==} + engines: {node: '>=12.20'} + hasBin: true + dev: true + + /universalify@2.0.0: + resolution: {integrity: sha512-hAZsKq7Yy11Zu1DE0OzWjw7nnLZmJZYTDZZyEFHZdUhV8FkH5MCfoU1XMaxXovpyW5nq5scPqq0ZDP9Zyl04oQ==} + engines: {node: '>= 10.0.0'} + dev: true + + /util-deprecate@1.0.2: + resolution: {integrity: sha512-EPD5q1uXyFxJpCrLnCc1nHnq3gOa6DZBocAIiI2TaSCA7VCJ1UJDMagCzIkXNsUYfD1daK//LTEQ8xiIbrHtcw==} + dev: true + + /v8-compile-cache-lib@3.0.1: + resolution: {integrity: sha512-wa7YjyUGfNZngI/vtK0UHAN+lgDCxBPCylVXGp0zu59Fz5aiGtNXaq3DhIov063MorB+VfufLh3JlF2KdTK3xg==} + dev: true + + /vite-plugin-static-copy@0.15.0(vite@4.3.7): + resolution: {integrity: sha512-Ww+/Ug9guV45oIfIi/lA2z8v3K+lLHV9zCJqTVO4FTdqrJoZBj68VgGBSH1fi0N4q/EHW32RsL3ympi4Wlsq5w==} + engines: {node: ^14.18.0 || >=16.0.0} + peerDependencies: + vite: ^3.0.0 || ^4.0.0 + dependencies: + chokidar: 3.5.3 + fast-glob: 3.2.12 + fs-extra: 11.1.1 + picocolors: 1.0.0 + vite: 4.3.7(@types/node@20.2.0)(sass@1.62.1) + dev: true + + /vite-plugin-zip-pack@1.0.5(vite@4.3.7): + resolution: {integrity: sha512-AY6F3GJL///Dc3d7fQhalJvWb08e0oIATVqHLwDAiK2/S1NCxbAFXfT4m2V+LtJRjHrRkHa0zbZvAI6/lb6vTQ==} + peerDependencies: + vite: '>=2.x' + dependencies: + jszip: 3.10.1 + vite: 4.3.7(@types/node@20.2.0)(sass@1.62.1) + dev: true + + /vite@4.3.7(@types/node@20.2.0)(sass@1.62.1): + resolution: {integrity: sha512-MTIFpbIm9v7Hh5b0wSBgkcWzSBz7SAa6K/cBTwS4kUiQJfQLFlZZRJRQgqunCVzhTPCk674tW+0Qaqh3Q00dBg==} + engines: {node: ^14.18.0 || >=16.0.0} + hasBin: true + peerDependencies: + '@types/node': '>= 14' + less: '*' + sass: '*' + stylus: '*' + sugarss: '*' + terser: ^5.4.0 + peerDependenciesMeta: + '@types/node': + optional: true + less: + optional: true + sass: + optional: true + stylus: + optional: true + sugarss: + optional: true + terser: + optional: true + dependencies: + '@types/node': 20.2.0 + esbuild: 0.17.19 + postcss: 8.4.23 + rollup: 3.21.7 + sass: 1.62.1 + optionalDependencies: + fsevents: 2.3.2 + dev: true + + /vitefu@0.2.4(vite@4.3.7): + resolution: {integrity: sha512-fanAXjSaf9xXtOOeno8wZXIhgia+CZury481LsDaV++lSvcU2R9Ch2bPh3PYFyoHW+w9LqAeYRISVQjUIew14g==} + peerDependencies: + vite: ^3.0.0 || ^4.0.0 + peerDependenciesMeta: + vite: + optional: true + dependencies: + vite: 4.3.7(@types/node@20.2.0)(sass@1.62.1) + dev: true + + /wrappy@1.0.2: + resolution: {integrity: sha512-l4Sp/DRseor9wL6EvV2+TuQn63dMkPjZ/sp9XkghTEbV9KlPS1xUsZ3u7/IQO4wxtcFB4bgpQPRcR3QCvezPcQ==} + dev: true + + /ws@7.5.9: + resolution: {integrity: sha512-F+P9Jil7UiSKSkppIiD94dN07AwvFixvLIj1Og1Rl9GGMuNipJnV9JzjD6XuqmAeiswGvUmNLjr5cFuXwNS77Q==} + engines: {node: '>=8.3.0'} + peerDependencies: + bufferutil: ^4.0.1 + utf-8-validate: ^5.0.2 + peerDependenciesMeta: + bufferutil: + optional: true + utf-8-validate: + optional: true + dev: true + + /yn@3.1.1: + resolution: {integrity: sha512-Ux4ygGWsu2c7isFWe8Yu1YluJmqVhxqK2cLXNQA5AcC3QfbGNpM7fu0Y8b/z16pXLnFxZYvWhd3fhBY9DLmC6Q==} + engines: {node: '>=6'} + dev: true diff --git a/public/i18n/README.md b/public/i18n/README.md deleted file mode 100644 index af8de98..0000000 --- a/public/i18n/README.md +++ /dev/null @@ -1,12 +0,0 @@ -思源支持的 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/public/i18n/zh_CN.json b/public/i18n/zh_CN.json deleted file mode 100644 index 6600f6a..0000000 --- a/public/i18n/zh_CN.json +++ /dev/null @@ -1,20 +0,0 @@ -{ - "addTopBarIcon": "使用插件添加一个顶栏按钮", - "cancel": "取消", - "save": "保存", - "byeMenu": "再见,菜单!", - "helloPlugin": "你好,插件!", - "byePlugin": "再见,插件!", - "showDialog": "弹出一个对话框", - "removedData": "数据已删除", - "confirmRemove": "确认删除 ${name} 中的数据?", - "insertEmoji": "插入表情", - "removeSpace": "移除空格", - "getTab": "在日志中打印出已打开的所有自定义页签", - "name": "思源", - "hello": { - "makesure": "使用这个模板之前,请阅读官方教程, 确保自己已经理解了插件的基本开发流程。" - }, - "hintTitle": "关于", - "hintDesc": "🔗 plugin-sample-vite-svelte
💻 @frostime
💻 @88250
💻 @zxkmm" -} \ No newline at end of file diff --git a/scripts/elevate.ps1 b/scripts/elevate.ps1 deleted file mode 100644 index 151b8ba..0000000 --- a/scripts/elevate.ps1 +++ /dev/null @@ -1,24 +0,0 @@ -# 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 2be3d2b..893d9f9 100644 --- a/scripts/make_dev_link.js +++ b/scripts/make_dev_link.js @@ -1,66 +1,55 @@ -/* - * 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 { log, error, getSiYuanDir, chooseTarget, getThisPluginName, makeSymbolicLink } from './utils.js'; -let targetDir = ''; -/** - * 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....'); - let res = await getSiYuanDir(); +//************************************ Write you dir here ************************************ - 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) { - targetDir = env; - log(`\tGot target directory from environment variable "SIYUAN_PLUGIN_DIR": ${targetDir}`); - } else { - error('\tCan not get SiYuan directory from environment variable "SIYUAN_PLUGIN_DIR", failed!'); - process.exit(1); - } - } else { - targetDir = await chooseTarget(res); - } +//Please write the "workspace/data/plugins" directory here +//请在这里填写你的 "workspace/data/plugins" 目录 +const targetDir = ''; +//Like this +// const targetDir = `H:\\SiYuanDevSpace\\data\\plugins`; +//******************************************************************************************** - 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'); + console.log(`Failed! plugin directory not exists: "${targetDir}"`); + console.log(`Please set the plugin directory in scripts/make_dev_link.js`); process.exit(1); } -/** - * 2. The dev directory, which contains the compiled plugin code - */ -const devDir = `${process.cwd()}/dev`; + +//check if plugin.json exists +if (!fs.existsSync('./plugin.json')) { + console.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 === '') { + console.log('Failed! Please set plugin name in plugin.json'); + process.exit(1); +} + +//dev directory +const devDir = `./dev`; +//mkdir if not exists if (!fs.existsSync(devDir)) { fs.mkdirSync(devDir); } - -/** - * 3. The target directory to make symbolic link to dev directory - */ -const name = getThisPluginName(); -if (name === null) { +const targetPath = `${targetDir}/${name}`; +//如果已经存在,就退出 +if (fs.existsSync(targetPath)) { + console.log('Failed! Target directory already exists'); process.exit(1); } -const targetPath = `${targetDir}/${name}`; -/** - * 4. Make symbolic link - */ -makeSymbolicLink(devDir, targetPath); +//创建软链接 +fs.symlinkSync(`${process.cwd()}/dev`, targetPath, 'junction'); +console.log(`Done! Created symlink ${targetPath}`); + diff --git a/scripts/make_install.js b/scripts/make_install.js deleted file mode 100644 index cb2a4ac..0000000 --- a/scripts/make_install.js +++ /dev/null @@ -1,57 +0,0 @@ -/* - * 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 { log, error, getSiYuanDir, chooseTarget, copyDirectory, getThisPluginName } from './utils.js'; - -let targetDir = ''; - -/** - * 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....'); - 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}`); -} -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); -} - -/** - * 2. The dist directory, which contains the compiled plugin code - */ -const distDir = `${process.cwd()}/dist`; -if (!fs.existsSync(distDir)) { - fs.mkdirSync(distDir); -} - -/** - * 3. The target directory to install the plugin - */ -const name = getThisPluginName(); -if (name === null) { - process.exit(1); -} -const targetPath = `${targetDir}/${name}`; - -/** - * 4. Copy the compiled plugin code to the target directory - */ -copyDirectory(distDir, targetPath); diff --git a/scripts/update_version.js b/scripts/update_version.js deleted file mode 100644 index 775c98a..0000000 --- a/scripts/update_version.js +++ /dev/null @@ -1,141 +0,0 @@ -// 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); - } -})(); diff --git a/scripts/utils.js b/scripts/utils.js deleted file mode 100644 index 210b6b1..0000000 --- a/scripts/utils.js +++ /dev/null @@ -1,182 +0,0 @@ -/* - * 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} - */ -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}`); - } -} diff --git a/src/api.ts b/src/api.ts index 0595371..e8a007a 100644 --- a/src/api.ts +++ b/src/api.ts @@ -6,10 +6,14 @@ * API 文档见 [API_zh_CN.md](https://github.com/siyuan-note/siyuan/blob/master/API_zh_CN.md) */ -import { fetchPost, fetchSyncPost, IWebSocketData } from "siyuan"; +import { + Block, Notebook, NotebookConf, NotebookId, DocumentId, BlockId, + doOperation, PreviousID, ParentID, BlockType, BlockSubType +} from "sy-dtype"; +import { fetchSyncPost, IWebSocketData } from "siyuan"; -export async function request(url: string, data: any) { +async function request(url: string, data: any) { let response: IWebSocketData = await fetchSyncPost(url, data); let res = response.code === 0 ? response.data : null; return res; @@ -18,8 +22,11 @@ export async function request(url: string, data: any) { // **************************************** Noteboook **************************************** +export type ReslsNotebooks = { + notebooks: Notebook[]; +} -export async function lsNotebooks(): Promise { +export async function lsNotebooks(): Promise { let url = '/api/notebook/lsNotebooks'; return request(url, ''); } @@ -54,8 +61,14 @@ export async function removeNotebook(notebook: NotebookId) { return request(url, { notebook: notebook }); } +export type ResGetNotebookConf = { + box: string; + conf: NotebookConf; + name: string; +} -export async function getNotebookConf(notebook: NotebookId): Promise { + +export async function getNotebookConf(notebook: NotebookId): Promise { let data = { notebook: notebook }; let url = '/api/notebook/getNotebookConf'; return request(url, data); @@ -69,7 +82,7 @@ export async function setNotebookConf(notebook: NotebookId, conf: NotebookConf): } -// **************************************** File Tree **************************************** +// **************************************** Document **************************************** export async function createDocWithMd(notebook: NotebookId, path: string, markdown: string): Promise { let data = { notebook: notebook, @@ -131,19 +144,13 @@ export async function getHPathByID(id: BlockId): Promise { return request(url, data); } - -export async function getIDsByHPath(notebook: NotebookId, path: string): Promise { - let data = { - notebook: notebook, - path: path - }; - let url = '/api/filetree/getIDsByHPath'; - return request(url, data); +// **************************************** Asset Files **************************************** +export type ResUpload = { + errFiles: string[]; + succMap: { [key: string]: string }; } -// **************************************** Asset Files **************************************** - -export async function upload(assetsDirPath: string, files: any[]): Promise { +export async function upload(assetsDirPath: string, files: any[]): Promise { let form = new FormData(); form.append('assetsDirPath', assetsDirPath); for (let file of files) { @@ -154,57 +161,45 @@ export async function upload(assetsDirPath: string, files: any[]): Promise { - let payload = { +export async function insertBlock(dataType: DataType, data: string, previousID: BlockId): Promise { + let data1 = { dataType: dataType, data: data, - nextID: nextID, - previousID: previousID, - parentID: parentID + previousID: previousID } let url = '/api/block/insertBlock'; - return request(url, payload); + return request(url, data1); } -export async function prependBlock(dataType: DataType, data: string, parentID: BlockId | DocumentId): Promise { - let payload = { - dataType: dataType, - data: data, - parentID: parentID - } - let url = '/api/block/prependBlock'; - return request(url, payload); -} - - -export async function appendBlock(dataType: DataType, data: string, parentID: BlockId | DocumentId): Promise { - let payload = { +export async function appendBlock(dataType: DataType, data: string, parentID: BlockId | DocumentId): Promise { + let data1 = { dataType: dataType, data: data, parentID: parentID } let url = '/api/block/appendBlock'; - return request(url, payload); + return request(url, data1); } -export async function updateBlock(dataType: DataType, data: string, id: BlockId): Promise { - let payload = { +export async function updateBlock(dataType: DataType, data: string, id: BlockId): Promise { + let data1 = { dataType: dataType, data: data, id: id } let url = '/api/block/updateBlock'; - return request(url, payload); + return request(url, data1); } -export async function deleteBlock(id: BlockId): Promise { +export async function deleteBlock(id: BlockId): Promise { let data = { id: id } @@ -213,7 +208,7 @@ export async function deleteBlock(id: BlockId): Promise { } -export async function moveBlock(id: BlockId, previousID?: PreviousID, parentID?: ParentID): Promise { +export async function moveBlock(id: BlockId, previousID: PreviousID | null = null, parentID: ParentID | null = null): Promise { let data = { id: id, previousID: previousID, @@ -224,25 +219,12 @@ 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 type ResGetBlockKramdown = { + id: BlockId; + kramdown: string; } - -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 { +export async function getBlockKramdown(id: BlockId): Promise { let data = { id: id } @@ -250,8 +232,12 @@ export async function getBlockKramdown(id: BlockId): Promise { +export type ChildBlock = { + id: BlockId; + type: BlockType; + subtype?: BlockSubType; +} +export async function getChildBlocks(id: BlockId): Promise { let data = { id: id } @@ -259,16 +245,6 @@ export async function getChildBlocks(id: BlockId): Promise return request(url, data); } -export async function transferBlockRef(fromID: BlockId, toID: BlockId, refIDs: BlockId[]) { - let data = { - fromID: fromID, - toID: toID, - refIDs: refIDs - } - let url = '/api/block/transferBlockRef'; - return request(url, data); -} - // **************************************** Attributes **************************************** export async function setBlockAttrs(id: BlockId, attrs: { [key: string]: string }) { let data = { @@ -306,7 +282,11 @@ export async function getBlockByID(blockId: string): Promise { // **************************************** Template **************************************** -export async function render(id: DocumentId, path: string): Promise { +export type ResGetTemplates = { + content: string; + path: string; +} +export async function render(id: DocumentId, path: string): Promise { let data = { id: id, path: path @@ -328,35 +308,9 @@ export async function getFile(path: string): Promise { path: path } let url = '/api/file/getFile'; - return new Promise((resolve, _) => { - fetchPost(url, data, (content: any) => { - resolve(content) - }); - }); + return request(url, data); } - -/** - * fetchPost will secretly convert data into json, this func merely return Blob - * @param endpoint - * @returns - */ -export const getFileBlob = async (path: string): Promise => { - 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); @@ -378,8 +332,11 @@ export async function removeFile(path: string) { } - -export async function readDir(path: string): Promise { +export type ResReadDir = { + isDir: boolean; + name: string; +} +export async function readDir(path: string): Promise { let data = { path: path } @@ -388,9 +345,11 @@ export async function readDir(path: string): Promise { } -// **************************************** Export **************************************** - -export async function exportMdContent(id: DocumentId): Promise { +export type ResExportMdContent = { + hPath: string; + content: string; +} +export async function exportMdContent(id: DocumentId): Promise { let data = { id: id } @@ -398,17 +357,6 @@ export async function exportMdContent(id: DocumentId): Promise { - let data = { - paths: paths, - name: name - } - let url = '/api/export/exportResources'; - return request(url, data); -} - -// **************************************** Convert **************************************** - export type PandocArgs = string; export async function pandoc(args: PandocArgs[]) { let data = { @@ -418,52 +366,13 @@ export async function pandoc(args: PandocArgs[]) { return request(url, data); } -// **************************************** Notification **************************************** - -// /api/notification/pushMsg -// { -// "msg": "test", -// "timeout": 7000 -// } -export async function pushMsg(msg: string, timeout: number = 7000) { - let payload = { - msg: msg, - timeout: timeout - }; - let url = "/api/notification/pushMsg"; - return request(url, payload); -} - -export async function pushErrMsg(msg: string, timeout: number = 7000) { - let payload = { - msg: msg, - timeout: timeout - }; - let url = "/api/notification/pushErrMsg"; - return request(url, payload); -} - -// **************************************** Network **************************************** -export async function forwardProxy( - url: string, method: string = 'GET', payload: any = {}, - headers: any[] = [], timeout: number = 7000, contentType: string = "text/html" -): Promise { - let data = { - url: url, - method: method, - timeout: timeout, - contentType: contentType, - headers: headers, - payload: payload - } - let url1 = '/api/network/forwardProxy'; - return request(url1, data); -} - // **************************************** System **************************************** - -export async function bootProgress(): Promise { +export type ResBootProgress = { + progress: number; + details: string; +} +export async function bootProgress(): Promise { return request('/api/system/bootProgress', {}); } diff --git a/src/dock.svelte b/src/dock.svelte new file mode 100644 index 0000000..a380d69 --- /dev/null +++ b/src/dock.svelte @@ -0,0 +1,23 @@ + + +
+
+ + + +
+
+ {text} +
+
diff --git a/src/hello.svelte b/src/hello.svelte index 967c7f6..b619b3f 100644 --- a/src/hello.svelte +++ b/src/hello.svelte @@ -1,63 +1,62 @@ -
-
appId:
-
-
${app?.appId}
-
-
-
API demo:
-
-
- System current time: {time} +
+
+
+

Hello {name} v{ver}

+
+
+ {time_str} +
-
-
-
Protyle demo: id = {blockID}
-
-
+ +
+

{@html i18n.makesure}

+
+
+ diff --git a/public/i18n/en_US.json b/src/i18n/en_US.json similarity index 50% rename from public/i18n/en_US.json rename to src/i18n/en_US.json index 7a11256..7e9425a 100644 --- a/public/i18n/en_US.json +++ b/src/i18n/en_US.json @@ -5,16 +5,8 @@ "byeMenu": "Bye, Menu!", "helloPlugin": "Hello, Plugin!", "byePlugin": "Bye, Plugin!", - "showDialog": "Show dialog", - "removedData": "Data deleted", - "confirmRemove": "Confirm to delete the data in ${name}?", - "insertEmoji": "Insert Emoji", - "removeSpace": "Remove Space", - "getTab": "Print out all opened custom tabs in the debugger", "name": "SiYuan", "hello": { "makesure": "Before using this template, please read the offical sample, make sure that you've known about the pipeline for plugin developing." - }, - "hintTitle":"About", - "hintDesc":"plugin-sample-vite-svelte
@frostime
@88250
@zxkmm" + } } \ No newline at end of file diff --git a/src/i18n/zh_CN.json b/src/i18n/zh_CN.json new file mode 100644 index 0000000..7eaa3eb --- /dev/null +++ b/src/i18n/zh_CN.json @@ -0,0 +1,12 @@ +{ + "addTopBarIcon": "使用插件添加一个顶栏按钮", + "cancel": "取消", + "save": "保存", + "byeMenu": "再见,菜单!", + "helloPlugin": "你好,插件!", + "byePlugin": "再见,插件!", + "name": "思源", + "hello": { + "makesure": "使用这个模板之前,请阅读官方教程, 确保自己已经理解了插件的基本开发流程。" + } +} \ No newline at end of file diff --git a/src/index.scss b/src/index.scss index e69de29..cb1be95 100644 --- a/src/index.scss +++ b/src/index.scss @@ -0,0 +1,3 @@ +#helloPanel { + border: 1px rgb(189, 119, 119) dashed; +} \ No newline at end of file diff --git a/src/index.ts b/src/index.ts index 5b50c49..f68a2d4 100644 --- a/src/index.ts +++ b/src/index.ts @@ -1,936 +1,159 @@ -import { - Plugin, - showMessage, - confirm, - Dialog, - Menu, - openTab, - adaptHotkey, - getFrontend, - getBackend, - IModel, - Protyle, - openWindow, - IOperation, - Constants, - openMobileFileById, - lockScreen, - ICard, - ICardData -} from "siyuan"; -import "@/index.scss"; +/** + * Copyright (c) 2023 frostime. All rights reserved. + * https://github.com/frostime/sy-plugin-template-vite + */ +import { Plugin, showMessage, confirm, Dialog, Menu, isMobile, openTab } from "siyuan"; +import "./index.scss"; -import HelloExample from "@/hello.svelte"; -import SettingExample from "@/setting-example.svelte"; - -import { SettingUtils } from "./libs/setting-utils"; -import { svelteDialog } from "./libs/dialog"; +import HelloExample from "./hello.svelte"; +import DockExample from "./dock.svelte"; +import SettingPannel from "./libs/setting-panel.svelte"; const STORAGE_NAME = "menu-config"; const TAB_TYPE = "custom_tab"; const DOCK_TYPE = "dock_tab"; -export default class PluginSample extends Plugin { +export default class SamplePlugin extends Plugin { - customTab: () => IModel; - private isMobile: boolean; - private blockIconEventBindThis = this.blockIconEvent.bind(this); - private settingUtils: SettingUtils; + counter: { [key: string]: number } = { + hello: 0, + }; + private customTab: () => any; async onload() { - this.data[STORAGE_NAME] = { readonlyText: "Readonly" }; - - console.log("loading plugin-sample", this.i18n); - - const frontEnd = getFrontend(); - this.isMobile = frontEnd === "mobile" || frontEnd === "browser-mobile"; - // 图标的制作参见帮助文档 - this.addIcons(` - - - - -`); + showMessage("Hello SiYuan Plugin"); + console.log(this.i18n.helloPlugin); const topBarElement = this.addTopBar({ - icon: "iconFace", + icon: "iconEmoji", title: this.i18n.addTopBarIcon, - position: "right", + position: "left", callback: () => { - if (this.isMobile) { - this.addMenu(); - } else { - let rect = topBarElement.getBoundingClientRect(); - // 如果被隐藏,则使用更多按钮 - if (rect.width === 0) { - rect = document.querySelector("#barMore").getBoundingClientRect(); - } - if (rect.width === 0) { - rect = document.querySelector("#barPlugins").getBoundingClientRect(); - } - this.addMenu(rect); - } + this.addMenu(topBarElement.getBoundingClientRect()); } }); - const statusIconTemp = document.createElement("template"); - statusIconTemp.innerHTML = `
- - - -
`; - statusIconTemp.content.firstElementChild.addEventListener("click", () => { - confirm("⚠️", this.i18n.confirmRemove.replace("${name}", this.name), () => { - this.removeData(STORAGE_NAME).then(() => { - this.data[STORAGE_NAME] = { readonlyText: "Readonly" }; - showMessage(`[${this.name}]: ${this.i18n.removedData}`); - }); - }); + let div = document.createElement("div"); + new HelloExample({ + target: div, + props: { + name: this.i18n.name, + i18n: this.i18n.hello + } }); - this.addStatusBar({ - element: statusIconTemp.content.firstElementChild as HTMLElement, - }); - - this.addCommand({ - langKey: "showDialog", - hotkey: "⇧⌘O", - callback: () => { - this.showDialog(); - }, - fileTreeCallback: (file: any) => { - console.log(file, "fileTreeCallback"); - }, - editorCallback: (protyle: any) => { - console.log(protyle, "editorCallback"); - }, - dockCallback: (element: HTMLElement) => { - console.log(element, "dockCallback"); - }, - }); - this.addCommand({ - langKey: "getTab", - hotkey: "⇧⌘M", - globalCallback: () => { - console.log(this.getOpenedTab()); + this.customTab = this.addTab({ + type: TAB_TYPE, + init() { + this.element.appendChild(div); + console.log(this.element); }, + destroy() { + console.log("destroy tab:", TAB_TYPE); + } }); this.addDock({ config: { position: "LeftBottom", size: { width: 200, height: 0 }, - icon: "iconSaving", + icon: "iconEmoji", title: "Custom Dock", - hotkey: "⌥⌘W", }, data: { text: "This is my custom dock" }, type: DOCK_TYPE, - resize() { - console.log(DOCK_TYPE + " resize"); - }, - update() { - console.log(DOCK_TYPE + " update"); - }, - init: (dock) => { - if (this.isMobile) { - dock.element.innerHTML = `
- -
Custom Dock
-
-
- ${dock.data.text} -
-
`; - } else { - dock.element.innerHTML = `
-
- - - -
-
- ${dock.data.text} -
-
`; - } + init() { + this.component = new DockExample({ + target: this.element, + props: { + text: this.data.text, + } + }); }, destroy() { console.log("destroy dock:", DOCK_TYPE); + this.component.$destroy(); } }); - this.settingUtils = new SettingUtils({ - plugin: this, name: STORAGE_NAME - }); - this.settingUtils.addItem({ - key: "Input", - value: "", - type: "textinput", - 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"); - console.log(value); - } - } - }); - this.settingUtils.addItem({ - key: "InputArea", - value: "", - type: "textarea", - title: "Readonly text", - description: "Input description", - // Called when focus is lost and content changes - action: { - callback: () => { - // Read data in real time - let value = this.settingUtils.take("InputArea"); - console.log(value); - } - } - }); - this.settingUtils.addItem({ - key: "Check", - value: true, - type: "checkbox", - title: "Checkbox text", - description: "Check description", - action: { - callback: () => { - // Return data and save it in real time - let value = !this.settingUtils.get("Check"); - this.settingUtils.set("Check", value); - console.log(value); - } - } - }); - this.settingUtils.addItem({ - key: "Select", - value: 1, - type: "select", - 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({ - key: "Slider", - value: 50, - type: "slider", - title: "Slider text", - description: "Slider description", - direction: "column", - slider: { - min: 0, - max: 100, - step: 1, - }, - action:{ - callback: () => { - // Read data in real time - let value = this.settingUtils.take("Slider"); - console.log(value); - } - } - }); - this.settingUtils.addItem({ - key: "Btn", - value: "", - type: "button", - title: "Button", - description: "Button description", - button: { - label: "Button", - callback: () => { - showMessage("Button clicked"); - } - } - }); - 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: "", - type: "hint", - title: this.i18n.hintTitle, - 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: `
${this.i18n.insertEmoji}😊
`, - id: "insertEmoji", - callback(protyle: Protyle) { - protyle.insert("😊"); - } - }]; - - 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); } - onLayoutReady() { - // 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, - props: { - app: this.app, - } - }); - this.customTab = this.addTab({ - type: TAB_TYPE, - init() { - this.element.appendChild(tabDiv); - console.log(this.element); - }, - beforeDestroy() { - console.log("before destroy tab:", TAB_TYPE); - }, - destroy() { - console.log("destroy tab:", TAB_TYPE); - } - }); - } - - async onunload() { - console.log(this.i18n.byePlugin); - showMessage("Goodbye SiYuan 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 - */ - openDIYSetting(): void { - let dialog = new Dialog({ - title: "SettingPannel", - content: `
`, - width: "800px", - destroyCallback: (options) => { - console.log("destroyCallback", options); - //You'd better destroy the component when the dialog is closed - pannel.$destroy(); - } - }); - let pannel = new SettingExample({ - target: dialog.element.querySelector("#SettingPanel"), - }); - } - - private eventBusPaste(event: any) { - // 如果需异步处理请调用 preventDefault, 否则会进行默认处理 - event.preventDefault(); - // 如果使用了 preventDefault,必须调用 resolve,否则程序会卡死 - event.detail.resolve({ - textPlain: event.detail.textPlain.trim(), - }); - } - - private eventBusLog({ detail }: any) { + private wsEvent({ detail }: any) { console.log(detail); } - private blockIconEvent({ detail }: any) { - detail.menu.addItem({ - iconHTML: "", - label: this.i18n.removeSpace, - click: () => { - const doOperations: IOperation[] = []; - detail.blockElements.forEach((item: HTMLElement) => { - const editElement = item.querySelector('[contenteditable="true"]'); - if (editElement) { - editElement.textContent = editElement.textContent.replace(/ /g, ""); - doOperations.push({ - id: item.dataset.nodeId, - data: item.outerHTML, - action: "update" - }); - } - }); - detail.protyle.getInstance().transaction(doOperations); - } - }); - } - - private showDialog() { - // let dialog = new Dialog({ - // title: `SiYuan ${Constants.SIYUAN_VERSION}`, - // content: `
`, - // 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}`, - width: this.isMobile ? "92vw" : "720px", - constructor: (container: HTMLElement) => { - return new HelloExample({ - target: container, - props: { - app: this.app, - } - }); - } - }); - } - - private addMenu(rect?: DOMRect) { + private async addMenu(rect: DOMRect) { const menu = new Menu("topBarSample", () => { console.log(this.i18n.byeMenu); }); + menu.addItem({ + icon: "iconHelp", + label: "Confirm", + click() { + confirm("Confirm", "Is this a confirm?", () => { + showMessage("confirm"); + }, () => { + showMessage("cancel"); + }); + } + }); + menu.addItem({ + icon: "iconFeedback", + label: "Message", + click: () => { + showMessage(this.i18n.helloPlugin); + } + }); menu.addItem({ icon: "iconInfo", - label: "Dialog(open help first)", - accelerator: this.commands[0].customHotkey, - click: () => { - this.showDialog(); - } + label: "Dialog", + click: () => this.openHelloInDialog() }); - if (!this.isMobile) { - menu.addItem({ - icon: "iconFace", - label: "Open Custom Tab", - click: () => { - const tab = openTab({ - app: this.app, - custom: { - icon: "iconFace", - title: "Custom Tab", - data: { - text: "This is my custom tab", - }, - id: this.name + TAB_TYPE + menu.addItem({ + icon: "iconLayoutBottom", + label: "Open Tab", + click: () => { + openTab({ + custom: { + icon: "iconEmoji", + title: "Custom Tab", + data: { + text: "This is my custom tab", }, - }); - console.log(tab); - } - }); - menu.addItem({ - icon: "iconImage", - label: "Open Asset Tab(open help first)", - click: () => { - const tab = openTab({ - app: this.app, - asset: { - path: "assets/paragraph-20210512165953-ag1nib4.svg" - } - }); - console.log(tab); - } - }); - menu.addItem({ - icon: "iconFile", - label: "Open Doc Tab(open help first)", - click: async () => { - const tab = await openTab({ - app: this.app, - doc: { - id: "20200812220555-lj3enxa", - } - }); - console.log(tab); - } - }); - menu.addItem({ - icon: "iconSearch", - label: "Open Search Tab", - click: () => { - const tab = openTab({ - app: this.app, - search: { - k: "SiYuan" - } - }); - console.log(tab); - } - }); - menu.addItem({ - icon: "iconRiffCard", - label: "Open Card Tab", - click: () => { - const tab = openTab({ - app: this.app, - card: { - type: "all" - } - }); - console.log(tab); - } - }); - menu.addItem({ - icon: "iconLayout", - label: "Open Float Layer(open help first)", - click: () => { - this.addFloatLayer({ - ids: ["20210428212840-8rqwn5o", "20201225220955-l154bn4"], - defIds: ["20230415111858-vgohvf3", "20200813131152-0wk5akh"], - x: window.innerWidth - 768 - 120, - y: 32 - }); - } - }); - menu.addItem({ - icon: "iconOpenWindow", - label: "Open Doc Window(open help first)", - click: () => { - openWindow({ - doc: {id: "20200812220555-lj3enxa"} - }); - } - }); - } else { - menu.addItem({ - icon: "iconFile", - label: "Open Doc(open help first)", - click: () => { - openMobileFileById(this.app, "20200812220555-lj3enxa"); - } - }); - } - menu.addItem({ - icon: "iconLock", - label: "Lockscreen", - click: () => { - lockScreen(this.app); + fn: this.customTab + }, + }); } }); menu.addItem({ - icon: "iconScrollHoriz", - label: "Event Bus", - type: "submenu", - submenu: [{ - icon: "iconSelect", - label: "On ws-main", - click: () => { - this.eventBus.on("ws-main", this.eventBusLog); - } - }, { - icon: "iconClose", - label: "Off ws-main", - click: () => { - this.eventBus.off("ws-main", this.eventBusLog); - } - }, { - icon: "iconSelect", - label: "On click-blockicon", - click: () => { - this.eventBus.on("click-blockicon", this.blockIconEventBindThis); - } - }, { - icon: "iconClose", - label: "Off click-blockicon", - click: () => { - this.eventBus.off("click-blockicon", this.blockIconEventBindThis); - } - }, { - icon: "iconSelect", - label: "On click-pdf", - click: () => { - this.eventBus.on("click-pdf", this.eventBusLog); - } - }, { - icon: "iconClose", - label: "Off click-pdf", - click: () => { - this.eventBus.off("click-pdf", this.eventBusLog); - } - }, { - icon: "iconSelect", - label: "On click-editorcontent", - click: () => { - this.eventBus.on("click-editorcontent", this.eventBusLog); - } - }, { - icon: "iconClose", - label: "Off click-editorcontent", - click: () => { - this.eventBus.off("click-editorcontent", this.eventBusLog); - } - }, { - icon: "iconSelect", - label: "On click-editortitleicon", - click: () => { - this.eventBus.on("click-editortitleicon", this.eventBusLog); - } - }, { - icon: "iconClose", - label: "Off click-editortitleicon", - click: () => { - this.eventBus.off("click-editortitleicon", this.eventBusLog); - } - }, { - icon: "iconSelect", - label: "On 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", - click: () => { - this.eventBus.on("open-noneditableblock", this.eventBusLog); - } - }, { - icon: "iconClose", - label: "Off open-noneditableblock", - click: () => { - this.eventBus.off("open-noneditableblock", this.eventBusLog); - } - }, { - icon: "iconSelect", - label: "On loaded-protyle-static", - click: () => { - this.eventBus.on("loaded-protyle-static", this.eventBusLog); - } - }, { - icon: "iconClose", - label: "Off loaded-protyle-static", - click: () => { - this.eventBus.off("loaded-protyle-static", this.eventBusLog); - } - }, { - icon: "iconSelect", - label: "On loaded-protyle-dynamic", - click: () => { - this.eventBus.on("loaded-protyle-dynamic", this.eventBusLog); - } - }, { - icon: "iconClose", - label: "Off loaded-protyle-dynamic", - click: () => { - this.eventBus.off("loaded-protyle-dynamic", this.eventBusLog); - } - }, { - icon: "iconSelect", - label: "On 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", - click: () => { - this.eventBus.on("destroy-protyle", this.eventBusLog); - } - }, { - icon: "iconClose", - label: "Off destroy-protyle", - click: () => { - this.eventBus.off("destroy-protyle", this.eventBusLog); - } - }, { - icon: "iconSelect", - label: "On open-menu-doctree", - click: () => { - this.eventBus.on("open-menu-doctree", this.eventBusLog); - } - }, { - icon: "iconClose", - label: "Off open-menu-doctree", - click: () => { - this.eventBus.off("open-menu-doctree", this.eventBusLog); - } - }, { - icon: "iconSelect", - label: "On open-menu-blockref", - click: () => { - this.eventBus.on("open-menu-blockref", this.eventBusLog); - } - }, { - icon: "iconClose", - label: "Off open-menu-blockref", - click: () => { - this.eventBus.off("open-menu-blockref", this.eventBusLog); - } - }, { - icon: "iconSelect", - label: "On open-menu-fileannotationref", - click: () => { - this.eventBus.on("open-menu-fileannotationref", this.eventBusLog); - } - }, { - icon: "iconClose", - label: "Off open-menu-fileannotationref", - click: () => { - this.eventBus.off("open-menu-fileannotationref", this.eventBusLog); - } - }, { - icon: "iconSelect", - label: "On open-menu-tag", - click: () => { - this.eventBus.on("open-menu-tag", this.eventBusLog); - } - }, { - icon: "iconClose", - label: "Off open-menu-tag", - click: () => { - this.eventBus.off("open-menu-tag", this.eventBusLog); - } - }, { - icon: "iconSelect", - label: "On open-menu-link", - click: () => { - this.eventBus.on("open-menu-link", this.eventBusLog); - } - }, { - icon: "iconClose", - label: "Off open-menu-link", - click: () => { - this.eventBus.off("open-menu-link", this.eventBusLog); - } - }, { - icon: "iconSelect", - label: "On open-menu-image", - click: () => { - this.eventBus.on("open-menu-image", this.eventBusLog); - } - }, { - icon: "iconClose", - label: "Off open-menu-image", - click: () => { - this.eventBus.off("open-menu-image", this.eventBusLog); - } - }, { - icon: "iconSelect", - label: "On open-menu-av", - click: () => { - this.eventBus.on("open-menu-av", this.eventBusLog); - } - }, { - icon: "iconClose", - label: "Off open-menu-av", - click: () => { - this.eventBus.off("open-menu-av", this.eventBusLog); - } - }, { - icon: "iconSelect", - label: "On open-menu-content", - click: () => { - this.eventBus.on("open-menu-content", this.eventBusLog); - } - }, { - icon: "iconClose", - label: "Off open-menu-content", - click: () => { - this.eventBus.off("open-menu-content", this.eventBusLog); - } - }, { - icon: "iconSelect", - label: "On open-menu-breadcrumbmore", - click: () => { - this.eventBus.on("open-menu-breadcrumbmore", this.eventBusLog); - } - }, { - icon: "iconClose", - label: "Off open-menu-breadcrumbmore", - click: () => { - this.eventBus.off("open-menu-breadcrumbmore", this.eventBusLog); - } - }, { - icon: "iconSelect", - label: "On 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", - click: () => { - this.eventBus.on("input-search", this.eventBusLog); - } - }, { - icon: "iconClose", - label: "Off input-search", - click: () => { - this.eventBus.off("input-search", this.eventBusLog); - } - }, { - icon: "iconSelect", - label: "On paste", - click: () => { - this.eventBus.on("paste", this.eventBusPaste); - } - }, { - icon: "iconClose", - label: "Off paste", - click: () => { - this.eventBus.off("paste", this.eventBusPaste); - } - }, { - icon: "iconSelect", - label: "On open-siyuan-url-plugin", - click: () => { - this.eventBus.on("open-siyuan-url-plugin", this.eventBusLog); - } - }, { - icon: "iconClose", - label: "Off open-siyuan-url-plugin", - click: () => { - this.eventBus.off("open-siyuan-url-plugin", this.eventBusLog); - } - }, { - icon: "iconSelect", - label: "On open-siyuan-url-block", - click: () => { - this.eventBus.on("open-siyuan-url-block", this.eventBusLog); - } - }, { - icon: "iconClose", - label: "Off open-siyuan-url-block", - click: () => { - this.eventBus.off("open-siyuan-url-block", this.eventBusLog); - } - }] + icon: "iconTrashcan", + label: "Remove Data", + click: () => { + this.removeData(STORAGE_NAME); + } + }); + menu.addItem({ + icon: "iconSelect", + label: "On ws-main", + click: () => { + this.eventBus.on("ws-main", this.wsEvent); + } + }); + menu.addItem({ + icon: "iconClose", + label: "Off ws-main", + click: () => { + this.eventBus.off("ws-main", this.wsEvent); + } }); menu.addSeparator(); - menu.addItem({ - icon: "iconSettings", - label: "Official Setting Dialog", - click: () => { - this.openSetting(); - } - }); - menu.addItem({ - icon: "iconSettings", - label: "A custom setting dialog (by svelte)", - click: () => { - this.openDIYSetting(); - } - }); menu.addItem({ icon: "iconSparkles", - label: this.data[STORAGE_NAME].readonlyText || "Readonly", + label: this.data[STORAGE_NAME] || "Readonly", type: "readonly", }); - if (this.isMobile) { + if (isMobile()) { menu.fullscreen(); } else { menu.open({ @@ -940,4 +163,44 @@ export default class PluginSample extends Plugin { }); } } + + openSetting(): void { + let dialog = new Dialog({ + title: "SettingPannel", + content: `
`, + width: "600px", + destroyCallback: (options) => { + console.log("destroyCallback", options); + //You must destroy the component when the dialog is closed + pannel.$destroy(); + } + }); + let pannel = new SettingPannel({ + target: dialog.element.querySelector("#SettingPanel"), + }); + } + + private openHelloInDialog() { + this.counter.hello++; + let dialog = new Dialog({ + title: "Hello World", + content: `
`, + destroyCallback(options) { + //You must destroy the component when the dialog is closed + hello.$destroy(); + }, + }); + let hello = new HelloExample({ + target: dialog.element.querySelector("#helloPanel"), + props: { + name: this.i18n.name, + i18n: this.i18n.hello + } + }); + } + + async onunload() { + showMessage("Goodbye SiYuan Plugin"); + console.log("onunload"); + } } diff --git a/src/libs/b3-list.svelte b/src/libs/b3-list.svelte new file mode 100644 index 0000000..edbab36 --- /dev/null +++ b/src/libs/b3-list.svelte @@ -0,0 +1,8 @@ +
    +
  • + + + + +
  • +
diff --git a/src/libs/components/Form/form-input.svelte b/src/libs/components/Form/form-input.svelte deleted file mode 100644 index cbf5a7e..0000000 --- a/src/libs/components/Form/form-input.svelte +++ /dev/null @@ -1,118 +0,0 @@ - - -{#if type === "checkbox"} - - -{:else if type === "textinput"} - - -{:else if type === "textarea"} -
-
-
-
- -
`, - 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((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: `
-
-
-
-
-
- -
`, - 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((resolve) => { - let newargs = { - ...args, confirm: (ele: HTMLElement) => { - resolve(ele); - }, cancel: (ele: HTMLElement) => { - resolve(ele); - } - }; - 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: `
`, - width: args.width, - height: args.height, - destroyCallback: args.callback - }); - dialog.element.querySelector(".dialog-content").appendChild(args.ele); - return { - dialog, - close: dialog.destroy.bind(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); - const { dialog, close } = simpleDialog({ - ...args, ele: container, callback: () => { - component.$destroy(); - if (args.callback) args.callback(); - } - }); - return { - component, - dialog, - close - } -} diff --git a/src/libs/index.d.ts b/src/libs/index.d.ts deleted file mode 100644 index 27a27ed..0000000 --- a/src/libs/index.d.ts +++ /dev/null @@ -1,43 +0,0 @@ -/* - * 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 16:39:54 - * @Description : - */ -type TSettingItemType = "checkbox" | "select" | "textinput" | "textarea" | "number" | "slider" | "button" | "hint" | "custom"; - -interface ISettingItemCore { - type: TSettingItemType; - key: string; - value: any; - placeholder?: string; - slider?: { - min: number; - max: number; - step: number; - }; - options?: { [key: string | number]: string }; - button?: { - label: string; - callback: () => void; - } -} - -interface ISettingItem extends ISettingItemCore { - title: string; - description: string; - direction?: "row" | "column"; -} - - -//Interface for setting-utils -interface ISettingUtilsItem extends ISettingItem { - action?: { - callback: () => void; - } - createElement?: (currentVal: any) => HTMLElement; - getEleVal?: (ele: HTMLElement) => any; - setEleVal?: (ele: HTMLElement, val: any) => void; -} diff --git a/src/libs/promise-pool.ts b/src/libs/promise-pool.ts deleted file mode 100644 index e20b0b8..0000000 --- a/src/libs/promise-pool.ts +++ /dev/null @@ -1,48 +0,0 @@ -export default class PromiseLimitPool { - private maxConcurrent: number; - private currentRunning = 0; - private queue: (() => void)[] = []; - private promises: Promise[] = []; - - constructor(maxConcurrent: number) { - this.maxConcurrent = maxConcurrent; - } - - add(fn: () => Promise): void { - const promise = new Promise((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 { - 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/src/libs/setting-item.svelte b/src/libs/setting-item.svelte new file mode 100644 index 0000000..74e08b5 --- /dev/null +++ b/src/libs/setting-item.svelte @@ -0,0 +1,90 @@ + + + diff --git a/src/libs/setting-panel.svelte b/src/libs/setting-panel.svelte new file mode 100644 index 0000000..ccf424e --- /dev/null +++ b/src/libs/setting-panel.svelte @@ -0,0 +1,99 @@ + + + + +
+
+
+

This setting panel is provided by a svelte component

+
+ + See: +
/lib/setting-pannel.svelte
+
+
+
+
+ { + showMessage( + `Checkbox changed: ${event.detail.key} = ${event.detail.value}` + ); + }} + /> + { + showMessage( + `Input changed: ${event.detail.key} = ${event.detail.value}` + ); + }} + /> + { + showMessage("Button clicked"); + }} + /> + { + showMessage( + `Select changed: ${event.detail.key} = ${event.detail.value}` + ); + }} + /> + { + showMessage( + `Slide changed: ${event.detail.key} = ${event.detail.value}` + ); + }} + /> +
diff --git a/src/libs/setting-utils.ts b/src/libs/setting-utils.ts deleted file mode 100644 index ae316e2..0000000 --- a/src/libs/setting-utils.ts +++ /dev/null @@ -1,397 +0,0 @@ -/* - * Copyright (c) 2023 by frostime. All Rights Reserved. - * @Author : frostime - * @Date : 2023-12-17 18:28:19 - * @FilePath : /src/libs/setting-utils.ts - * @LastEditTime : 2024-05-01 17:44:16 - * @Description : - */ - -import { Plugin, Setting } from 'siyuan'; - - -/** - * 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; - }; - 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 = () => null; - break; - } - return getter; -} - - -/** - * 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; - default: - setter = () => {}; - break; - } - return setter; - -} - - -export class SettingUtils { - plugin: Plugin; - name: string; - file: string; - - settings: Map = new Map(); - elements: Map = new Map(); - - 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: args.width, - height: args.height, - confirmCallback: () => { - for (let key of this.settings.keys()) { - this.updateValueFromElement(key); - } - let data = this.dump(); - if (args.callback !== undefined) { - args.callback(data); - } - this.plugin.data[this.name] = data; - this.save(data); - }, - destroyCallback: () => { - //Restore the original value - for (let key of this.settings.keys()) { - this.updateElementFromValue(key); - } - } - }); - } - - 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(); - return data; - } - - async save(data?: any) { - data = data ?? this.dump(); - await this.plugin.saveData(this.file, this.dump()); - console.debug('Save config:', data); - return data; - } - - /** - * read the data after saving - * @param key key name - * @returns setting item value - */ - get(key: string) { - return this.settings.get(key)?.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) { - item.value = value; - 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(); - } - } - - /** - * 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 item = this.settings.get(key); - let element = this.elements.get(key) as any; - if (!element) { - return - } - if (apply) { - this.updateValueFromElement(key); - } - return item.getEleVal(element); - } - - /** - * 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, true); - await this.save(); - return value; - } - - /** - * 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 - */ - dump(): Object { - let data: any = {}; - for (let [key, item] of this.settings) { - if (item.type === 'button') continue; - data[key] = item.value; - } - return data; - } - - 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); - } - if (item.setEleVal === undefined) { - item.setEleVal = createDefaultSetter(item.type); - } - - 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, - direction: item?.direction, - createActionElement: () => { - this.updateElementFromValue(item.key); - let element = this.getElement(item.key); - return element; - } - }); - } else { - 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); - this.elements.set(item.key, element); - return element; - } - }); - } - } - - 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'); - element.type = 'checkbox'; - element.checked = item.value; - element.className = "b3-switch fn__flex-center"; - itemElement = element; - element.onchange = item.action?.callback ?? (() => { }); - break; - case 'select': - let selectElement: HTMLSelectElement = document.createElement('select'); - selectElement.className = "b3-select fn__flex-center fn__size200"; - let options = item?.options ?? {}; - for (let val in options) { - let optionElement = document.createElement('option'); - let text = options[val]; - optionElement.value = val; - optionElement.text = text; - selectElement.appendChild(optionElement); - } - selectElement.value = item.value; - selectElement.onchange = item.action?.callback ?? (() => { }); - itemElement = selectElement; - break; - case 'slider': - let sliderElement: HTMLInputElement = document.createElement('input'); - sliderElement.type = 'range'; - sliderElement.className = 'b3-slider fn__size200 b3-tooltips b3-tooltips__n'; - sliderElement.ariaLabel = item.value; - sliderElement.min = item.slider?.min.toString() ?? '0'; - sliderElement.max = item.slider?.max.toString() ?? '100'; - sliderElement.step = item.slider?.step.toString() ?? '1'; - sliderElement.value = item.value; - sliderElement.onchange = () => { - sliderElement.ariaLabel = sliderElement.value; - item.action?.callback(); - } - itemElement = sliderElement; - break; - case 'textinput': - let textInputElement: HTMLInputElement = document.createElement('input'); - textInputElement.className = 'b3-text-field fn__flex-center fn__size200'; - textInputElement.value = item.value; - textInputElement.onchange = item.action?.callback ?? (() => { }); - itemElement = textInputElement; - textInputElement.addEventListener('keydown', preventEnterConfirm); - break; - case 'textarea': - let textareaElement: HTMLTextAreaElement = document.createElement('textarea'); - textareaElement.className = "b3-text-field fn__block"; - textareaElement.value = item.value; - textareaElement.onchange = item.action?.callback ?? (() => { }); - 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; - numberElement.addEventListener('keydown', preventEnterConfirm); - break; - case 'button': - 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 ?? (() => { }); - itemElement = buttonElement; - break; - case 'hint': - let hintElement: HTMLElement = document.createElement('div'); - hintElement.className = 'b3-label fn__flex-center'; - itemElement = hintElement; - break; - } - return itemElement; - } - - /** - * return the setting element - * @param key key name - * @returns element - */ - getElement(key: string) { - // let item = this.settings.get(key); - let element = this.elements.get(key) as any; - return element; - } - - 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 = 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; - item.setEleVal(element, item.value); - } -} \ No newline at end of file diff --git a/src/setting-example.svelte b/src/setting-example.svelte deleted file mode 100644 index 2a2c809..0000000 --- a/src/setting-example.svelte +++ /dev/null @@ -1,139 +0,0 @@ - - -
-
    - {#each groups as group} - -
  • { - focusGroup = group; - }} - on:keydown={() => {}} - > - {group} -
  • - {/each} -
-
- { console.debug("Click:", detail.key); }} - > -
- 💡 This is our default settings. -
-
- { console.debug("Click:", detail.key); }} - > - -
-
- - - diff --git a/src/siyuan.d.ts b/src/siyuan.d.ts new file mode 100644 index 0000000..fbca339 --- /dev/null +++ b/src/siyuan.d.ts @@ -0,0 +1,185 @@ +/* + * Copyright (c) 2023, SiYuan, frostime, Terwer . All rights reserved. + */ + +declare module "siyuan" { + type TEventBus = "ws-main" + + interface IObject { + [key: string]: string; + } + + interface IWebSocketData { + cmd: string + callback?: string + data: any + msg: string + code: number + sid: string + } + + declare interface IPluginDockTab { + position: "LeftTop" | "LeftBottom" | "RightTop" | "RightBottom" | "BottomLeft" | "BottomRight", + size: { width: number, height: number }, + icon: string, + hotkey?: string, + title: string, + } + + interface IMenuItemOption { + label?: string, + click?: (element: HTMLElement) => void, + type?: "separator" | "submenu" | "readonly", + accelerator?: string, + action?: string, + id?: string, + submenu?: IMenuItemOption[] + disabled?: boolean + icon?: string + iconHTML?: string + current?: boolean + bind?: (element: HTMLElement) => void + } + + export function fetchPost(url: string, data?: any, cb?: (response: IWebSocketData) => void, headers?: IObject): void; + + export function fetchSyncPost(url: string, data?: any): Promise; + + export function fetchGet(url: string, cb: (response: IWebSocketData) => void): void; + + export function openTab(options: { + custom?: { + title: string, + icon: string, + data?: any + fn?: () => any, + } // card 和自定义页签 必填 + position?: "right" | "bottom", + keepCursor?: boolean // 是否跳转到新 tab 上 + removeCurrentTab?: boolean // 在当前页签打开时需移除原有页签 + afterOpen?: () => void // 打开后回调 + }): void + + export function isMobile(): boolean; + + export function adaptHotkey(hotkey: string): string; + + export function confirm(title: string, text: string, confirmCB?: () => void, cancelCB?: () => void): void; + + /** + * @param timeout - ms. 0: manual close;-1: always show; 6000: default + * @param {string} [type=info] + */ + export function showMessage(text: string, timeout?: number, type?: "info" | "error", id?: string): void; + + export class App { + plugins: Plugin[]; + } + + export abstract class Plugin { + eventBus: EventBus; + i18n: IObject; + data: any; + name: string; + + constructor(options: { + app: App, + id: string, + name: string, + i18n: IObject + }) + + onload(): void; + + onunload(): void; + + /* + * @param {string} [options.position=right] + */ + addTopBar(options: { + icon: string, + title: string, + callback: (evt: MouseEvent) => void + position?: "right" | "left" + }): HTMLDivElement; + + openSetting(): void + + // registerCommand(command: IPluginCommand): void; + + // registerSettingRender(settingRender: SettingRender): void; + + loadData(storageName: string): Promise; + + saveData(storageName: string, content: any): Promise; + + removeData(storageName: string): Promise; + + addTab(options: { + type: string, + destroy?: () => void, + resize?: () => void, + update?: () => void, + init: () => void + }): () => any + + addDock(options: { + config: IPluginDockTab, + data: any, + type: string, + destroy?: () => void, + resize?: () => void, + update?: () => void, + init: () => void + }): any + } + + export class EventBus { + on(type: TEventBus, listener: (event: CustomEvent) => void): void; + + once(type: TEventBus, listener: (event: CustomEvent) => void): void; + + off(type: TEventBus, listener: (event: CustomEvent) => void): void; + + emit(type: TEventBus, detail?: any): boolean; + } + + export class Dialog { + + element: HTMLElement; + + constructor(options: { + title?: string, + transparent?: boolean, + content: string, + width?: string + height?: string, + destroyCallback?: (options?: IObject) => void + disableClose?: boolean + disableAnimation?: boolean + }); + + destroy(options?: IObject): void; + + bindInput(inputElement: HTMLInputElement | HTMLTextAreaElement, enterEvent?: () => void): void; + } + + export class Menu { + constructor(id?: string, closeCB?: () => void); + + showSubMenu(subMenuElement: HTMLElement): void; + + addItem(options: IMenuItemOption): HTMLElement; + + addSeparator(): void; + + open(options: { x: number, y: number, h?: number, w?: number, isLeft?: boolean }): void; + + /* + * @param {string} [position=all] + */ + fullscreen(position?: "bottom" | "all"): void; + + close(): void; + } +} \ No newline at end of file diff --git a/src/sy-dtype.d.ts b/src/sy-dtype.d.ts new file mode 100644 index 0000000..0c3b465 --- /dev/null +++ b/src/sy-dtype.d.ts @@ -0,0 +1,70 @@ +/** + * Copyright (c) 2023 frostime. All rights reserved. + * https://github.com/frostime/sy-plugin-template-vite + */ + +/** + * Frequently used data structures in SiYuan + */ +declare module "sy-dtype" { + + export type DocumentId = string; + export type BlockId = string; + export type NotebookId = string; + export type PreviousID = BlockId; + export type ParentID = BlockId | DocumentId; + + export type Notebook = { + id: NotebookId; + name: string; + icon: string; + sort: number; + closed: boolean; + } + + export type NotebookConf = { + name: string; + closed: boolean; + refCreateSavePath: string; + createDocNameTemplate: string; + dailyNoteSavePath: string; + dailyNoteTemplatePath: string; + } + + export type BlockType = "d" | "s" | "h" | "t" | "i" | "p" | "f" | "audio" | "video" | "other"; + + export 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"; + + export type Block = { + id: BlockId; + parent_id?: BlockId; + root_id: DocumentId; + hash: string; + box: string; + path: string; + hpath: string; + name: string; + alias: string; + memo: string; + tag: string; + content: string; + fcontent?: string; + markdown: string; + length: number; + type: BlockType; + subtype: BlockSubType; + ial?: { [key: string]: string }; + sort: number; + created: string; + updated: string; + } + + export type doOperation = { + action: string; + data: string; + id: BlockId; + parentID: BlockId | DocumentId; + previousID: BlockId; + retData: null; + } +} \ No newline at end of file diff --git a/src/types/api.d.ts b/src/types/api.d.ts deleted file mode 100644 index 3c08859..0000000 --- a/src/types/api.d.ts +++ /dev/null @@ -1,65 +0,0 @@ -interface IResGetNotebookConf { - box: string; - conf: NotebookConf; - name: string; -} - -interface IReslsNotebooks { - notebooks: Notebook[]; -} - -interface IResUpload { - errFiles: string[]; - succMap: { [key: string]: string }; -} - -interface IResdoOperations { - doOperations: doOperation[]; - undoOperations: doOperation[] | null; -} - -interface IResGetBlockKramdown { - id: BlockId; - kramdown: string; -} - -interface IResGetChildBlock { - id: BlockId; - type: BlockType; - subtype?: BlockSubType; -} - -interface IResGetTemplates { - content: string; - path: string; -} - -interface IResReadDir { - isDir: boolean; - isSymlink: boolean; - name: string; -} - -interface IResExportMdContent { - hPath: string; - content: string; -} - -interface IResBootProgress { - progress: number; - details: string; -} - -interface IResForwardProxy { - body: string; - contentType: string; - elapsed: number; - headers: { [key: string]: string }; - status: number; - url: string; -} - -interface IResExportResources { - path: string; -} - diff --git a/src/types/index.d.ts b/src/types/index.d.ts deleted file mode 100644 index f224b3e..0000000 --- a/src/types/index.d.ts +++ /dev/null @@ -1,106 +0,0 @@ -/* - * 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 - */ - - -type DocumentId = string; -type BlockId = string; -type NotebookId = string; -type PreviousID = BlockId; -type ParentID = BlockId | DocumentId; - -type Notebook = { - id: NotebookId; - name: string; - icon: string; - sort: number; - closed: boolean; -} - -type NotebookConf = { - name: string; - closed: boolean; - refCreateSavePath: string; - createDocNameTemplate: string; - dailyNoteSavePath: string; - dailyNoteTemplatePath: string; -} - -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"; - -type Block = { - id: BlockId; - parent_id?: BlockId; - root_id: DocumentId; - hash: string; - box: string; - path: string; - hpath: string; - name: string; - alias: string; - memo: string; - tag: string; - content: string; - fcontent?: string; - markdown: string; - length: number; - type: BlockType; - subtype: BlockSubType; - /** string of { [key: string]: string } - * For instance: "{: custom-type=\"query-code\" id=\"20230613234017-zkw3pr0\" updated=\"20230613234509\"}" - */ - ial?: string; - sort: number; - created: string; - updated: string; -} - -type doOperation = { - action: string; - data: string; - id: BlockId; - parentID: BlockId | DocumentId; - previousID: BlockId; - retData: null; -} - -interface Window { - siyuan: { - config: any; - notebooks: any; - menus: any; - dialogs: any; - blockPanels: any; - storage: any; - user: any; - ws: any; - languages: any; - emojis: any; - }; - Lute: any; -} diff --git a/svelte.config.js b/svelte.config.js index d62a343..7c8df62 100644 --- a/svelte.config.js +++ b/svelte.config.js @@ -1,26 +1,7 @@ -/* - * 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); - } } diff --git a/tsconfig.json b/tsconfig.json index 0fcc1ad..21cb9cb 100644 --- a/tsconfig.json +++ b/tsconfig.json @@ -35,20 +35,14 @@ "node", "vite/client", "svelte" - ], - // "baseUrl": "./src", - "paths": { - "@/*": ["./src/*"], - "@/libs/*": ["./src/libs/*"], - } + ] }, "include": [ "tools/**/*.ts", "src/**/*.ts", "src/**/*.d.ts", "src/**/*.tsx", - "src/**/*.vue", - "src/**/*.svelte" + "src/**/*.vue" ], "references": [ { diff --git a/vite.config.ts b/vite.config.ts index cb7511b..9e9f127 100644 --- a/vite.config.ts +++ b/vite.config.ts @@ -1,96 +1,110 @@ 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" 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 env = process.env; -const isSrcmap = env.VITE_SOURCEMAP === 'inline'; -const isDev = env.NODE_ENV === 'development'; - -const outputDir = isDev ? "dev" : "dist"; - -console.log("isDev=>", isDev); -console.log("isSrcmap=>", isSrcmap); -console.log("outputDir=>", outputDir); +console.log("isWatch=>", isWatch) +console.log("distDir=>", distDir) export default defineConfig({ - resolve: { - alias: { - "@": resolve(__dirname, "src"), - } - }, - plugins: [ svelte(), - vitePluginYamlI18n({ - inDir: 'public/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: "./icon.png", + dest: "./", + }, + { + src: "./preview.png", + dest: "./", + }, + { + src: "./plugin.json", + dest: "./", + }, + { + src: "./src/i18n/**", + dest: "./i18n/", + }, ], }), - ], + // 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": JSON.stringify(isDev), - "process.env.NODE_ENV": JSON.stringify(env.NODE_ENV) + "process.env.DEV_MODE": `"${isWatch}"`, }, build: { - outDir: outputDir, + // 输出路径 + outDir: distDir, emptyOutDir: false, - minify: true, - sourcemap: isSrcmap ? 'inline' : false, + + // 构建后是否生成 source map 文件 + sourcemap: false, + + // 设置为 false 可以禁用最小化混淆 + // 或是用来指定是应用哪种混淆器 + // boolean | 'terser' | 'esbuild' + // 不压缩,用于调试 + minify: !isWatch, 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: [ - ...(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); + ...( + isWatch ? [ + livereload(devDistDir), + { + //监听静态资源文件 + name: 'watch-external', + async buildStart() { + const files = await fg([ + 'src/i18n/*.json', + './README*.md', + './plugin.json' + ]); + for (let file of files) { + this.addWatchFile(file); + } } } - } - ] : [ - // Clean up unnecessary files under dist dir - cleanupDistFiles({ - patterns: ['i18n/*.yaml', 'i18n/*.md'], - distDir: outputDir - }), - 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: { @@ -104,60 +118,4 @@ 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); - } - } - } - } - }; -} +}) diff --git a/yaml-plugin.js b/yaml-plugin.js deleted file mode 100644 index 01c85e2..0000000 --- a/yaml-plugin.js +++ /dev/null @@ -1,60 +0,0 @@ -/* - * 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:53:34 - * @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 }); - } - - //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}`); - } - } - } - }, - }; -}