From eaca7fc1927f32afdc6076139d4b25adcee2e410 Mon Sep 17 00:00:00 2001 From: MassiveBox Date: Mon, 31 Mar 2025 19:14:17 +0200 Subject: [PATCH] Initial Upload --- README.md | 304 ++---------- README_zh_CN.md | 271 ----------- icon.png | Bin 1493 -> 8596 bytes package.json | 6 +- plugin.json | 23 +- preview.png | Bin 12208 -> 75167 bytes public/i18n/en_US.json | 20 - public/i18n/zh_CN.json | 20 - public/webapp/button.js | 56 +++ public/webapp/draw.js | 47 ++ public/webapp/index.css | 91 ++++ public/webapp/index.html | 26 + src/const.ts | 7 + src/editorTab.ts | 75 +++ src/file.ts | 37 ++ src/hello.svelte | 63 --- src/helper.ts | 57 +++ src/index.scss | 0 src/index.ts | 945 +------------------------------------ src/setting-example.svelte | 139 ------ 20 files changed, 469 insertions(+), 1718 deletions(-) delete mode 100644 README_zh_CN.md delete mode 100644 public/i18n/en_US.json delete mode 100644 public/i18n/zh_CN.json create mode 100644 public/webapp/button.js create mode 100644 public/webapp/draw.js create mode 100644 public/webapp/index.css create mode 100644 public/webapp/index.html create mode 100644 src/const.ts create mode 100644 src/editorTab.ts create mode 100644 src/file.ts delete mode 100644 src/hello.svelte create mode 100644 src/helper.ts delete mode 100644 src/index.scss delete mode 100644 src/setting-example.svelte diff --git a/README.md b/README.md index 648dcb7..73c10ff 100644 --- a/README.md +++ b/README.md @@ -1,278 +1,42 @@ -# SiYuan plugin sample with vite and svelte +# SiYuan js-draw Plugin -[中文版](./README_zh_CN.md) +This plugin allows you to embed js-draw whiteboards anywhere in your SiYuan documents. -> 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) +## Usage instructions +1. Install the plugin + - Grab a release from the [Releases page](https://git.massive.box/massivebox/siyuan-jsdraw-plugin/releases) + - Unzip it in the folder `./data/plugins`, relatively to your SiYuan workspace. + > The plugin is not yet available in the official marketplace. I will try to publish it there soon! +2. Insert a drawing in your documents by typing `/Insert Drawing` in your document, and selecting the correct menu entry +3. The whiteboard editor will open in a new tab. Draw as you like, then click the Save button. It will also add a + drawing block to your document. +4. Click the Gear icon > Refresh to refresh the drawing block, if it's still displaying the old drawing. +5. Click the drawing block to open the editor again. +## Planned features +- [ ] Auto-reload drawing blocks on drawing change +- [ ] Rename whiteboards +- [ ] Improve internationalization framework +- [ ] Default background color and grid options +- [ ] Respecting user theme for the editor +- And more! +## Contributing +Contributions are always welcome! Right now, I'm working on the core functionality and fixing bugs. +After that is done, I will need help with the internationalization, as, unfortunately, I don't speak Chinese. +Please [contact me](mailto:box@massive.box) if you'd like to help! -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 +## Thanks to +This project couldn't have been possible without (in no particular order): +- The [SiYuan](https://github.com/siyuan-note/siyuan) project +- [js-draw](https://github.com/personalizedrefrigerator/js-draw) +- [SiYuan plugin sample with vite and svelte](https://github.com/siyuan-note/plugin-sample-vite-svelte) +- [siyuan-drawio-plugin](https://github.com/zt8989/siyuan-drawio-plugin) and + [siyuan-plugin-whiteboard](https://github.com/zuoez02/siyuan-plugin-whiteboard) for inspiration and bits of code - > **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. - -### Setting the Target Directory for the make-link Command - -The `make-link` command creates a symbolic link that binds your `dev` directory to the SiYuan plugin directory. You can configure the target SiYuan workspace and create the symbolic link in three ways: - -1. **Select Workspace** - - Open SiYuan, ensure the SiYuan kernel is running. - - Run `pnpm run make-link`, the script will automatically detect all SiYuan workspaces, please manually enter the number to select the workspace. - ```bash - >>> pnpm run make-link - > plugin-sample-vite-svelte@0.0.3 make-link H:\SrcCode\开源项目\plugin-sample-vite-svelte - > 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. **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. - -## I18n - -In terms of internationalization, our main consideration is to support multiple languages. Specifically, we need to -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 - * Use `this.i18.key` to get the text in the code -* YAML Support - * This template specifically supports I18n based on YAML syntax, see `public/i18n/zh_CN.yaml` - * During compilation, the defined YAML files will be automatically translated into JSON files and placed in the dist or dev directory. - -It is recommended that the plugin supports at least English and Simplified Chinese, so that more people can use it more -conveniently. - -## plugin.json - -```json -{ - "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"], - "displayName": { - "en_US": "Plugin sample with vite and svelte", - "zh_CN": "插件样例 vite + svelte 版" - }, - "description": { - "en_US": "SiYuan plugin sample with vite and svelte", - "zh_CN": "使用 vite 和 svelte 开发的思源插件样例" - }, - "readme": { - "en_US": "README_en_US.md", - "zh_CN": "README.md" - }, - "funding": { - "openCollective": "", - "patreon": "", - "github": "", - "custom": [ - "https://ld246.com/sponsor" - ] - }, - "keywords": [ - "sample", "示例" - ] -} -``` - -* `name`: Plugin name, must be the same as the repo name, and must be unique globally (no duplicate plugin names in the - marketplace) -* `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 -* `description`: Plugin description, 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 -* `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 -* `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 -* index.js -* plugin.json -* preview.png (1024*768) -* README*.md - -## List on the marketplace - -* `pnpm run build` to generate package.zip -* Create a new GitHub release using your new version number as the "Tag version". See here for an - example: https://github.com/siyuan-note/plugin-sample/releases -* Upload the file package.zip as binary attachments -* Publish the release - -If it is the first release, please create a pull request to -the [Community Bazaar](https://github.com/siyuan-note/bazaar) repository and modify the plugins.json file in it. This -file is the index of all community plugin repositories, the format is: - -```json -{ - "repos": [ - "username/reponame" - ] -} -``` - -After the PR is merged, the bazaar will automatically update the index and deploy through GitHub Actions. When releasing -a new version of the plugin in the future, you only need to follow the above steps to create a new release, and you -don't need to PR the community bazaar repo. - -Under normal circumstances, the community bazaar repo will automatically update the index and deploy every hour, -and you can check the deployment status at https://github.com/siyuan-note/bazaar/actions. - -## Use Github Action - -The github action is included in this sample, you can use it to publish your new realse to marketplace automatically: - -1. In your repo setting page `https://github.com/OWNER/REPO/settings/actions`, down to **Workflow Permissions** and open the configuration like this: - - ![](asset/action.png) - -2. Push a tag in the format `v*` and github will automatically create a new release with new bulit package.zip - -3. By default, it will only publish a pre-release, if you don't think this is necessary, change the settings in release.yml - - ```yaml - - name: Release - uses: ncipollo/release-action@v1 - with. - allowUpdates: true - artifactErrorsFailBuild: true - artifacts: 'package.zip' - token: ${{ secrets.GITHUB_TOKEN }} - 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 +Make sure you check them out and support them as well! +## License +The original plugin framework is developed by SiYuan 思源笔记 and licensed under the MIT license. +All changes made by me are copyright MassiveBox 2025, and licensed under the MIT license. \ No newline at end of file diff --git a/README_zh_CN.md b/README_zh_CN.md deleted file mode 100644 index 0ac9fca..0000000 --- a/README_zh_CN.md +++ /dev/null @@ -1,271 +0,0 @@ - -# 使用 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) - -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` -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. **手动配置目标目录** - - 打开 `./scripts/make_dev_link.js` 文件,更改 `targetDir` 为思源的插件目录 `/data/plugins` - - 运行 `pnpm run make-link` 命令, 如果看到类似以下的消息,说明创建成功: - -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 脚本执行权限 - - -## 国际化 - -国际化方面我们主要考虑的是支持多语言,具体需要完成以下工作: - -* 插件自身的元信息,比如插件描述和自述文件 - * plugin.json 中的 `description` 和 `readme` 字段,以及对应的 README*.md 文件 -* 插件中使用的文本,比如按钮文字和提示信息 - * public/i18n/*.json 语言配置文件 - * 代码中使用 `this.i18.key` 获取文本 -* 最后在 plugin.json 中的 `i18n` 字段中声明该插件支持的语言 -* yaml 支持 - * 本模板特别支持基于 Yaml 语法的 I18n,见 `public/i18n/zh_CN.yaml` - * 编译时,会自动把定义的 yaml 文件翻译成 json 文件放到 dist 或 dev 目录下 - -建议插件至少支持英文和简体中文,这样可以方便更多人使用。 - -## plugin.json - -```json -{ - "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"], - "displayName": { - "en_US": "Plugin sample with vite and svelte", - "zh_CN": "插件样例 vite + svelte 版" - }, - "description": { - "en_US": "SiYuan plugin sample with vite and svelte", - "zh_CN": "使用 vite 和 svelte 开发的思源插件样例" - }, - "readme": { - "en_US": "README_en_US.md", - "zh_CN": "README.md" - }, - "funding": { - "openCollective": "", - "patreon": "", - "github": "", - "custom": [ - "https://ld246.com/sponsor" - ] - }, - "keywords": [ - "sample", "示例" - ] -} -``` - -* `name`:插件名称,必须和库名一致,且全局唯一(集市中不能有重名插件) -* `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` 等其他语言:可选,建议至少提供中文和英文 -* `description`:插件描述,主要用于插件集市列表中显示,支持多语言 - * `default`:默认语言,必须存在 - * `zh_CN`、`en_US` 等其他语言:可选,建议至少提供中文和英文 -* `readme`:自述文件名,主要用于插件集市详情页中显示,支持多语言 - * `default`:默认语言,必须存在 - * `zh_CN`、`en_US` 等其他语言:可选,建议至少提供中文和英文 -* `funding`:插件赞助信息 - * `openCollective`:Open Collective 名称 - * `patreon`:Patreon 名称 - * `github`:GitHub 登录名 - * `custom`:自定义赞助链接列表 -* `keywords`:搜索关键字列表,用于集市搜索功能 - -## 打包 - -无论使用何种方式编译打包,我们最终需要生成一个 package.zip,它至少包含如下文件: - -* i18n/* -* icon.png (160*160) -* index.css -* index.js -* plugin.json -* preview.png (1024*768) -* README*.md - -## 上架集市 - -* 执行 `pnpm run build` 生成 package.zip -* 在 GitHub 上创建一个新的发布,使用插件版本号作为 “Tag - version”,示例 https://github.com/siyuan-note/plugin-sample/releases -* 上传 package.zip 作为二进制附件 -* 提交发布 - -如果是第一次发布版本,还需要创建一个 PR 到 [Community Bazaar](https://github.com/siyuan-note/bazaar) 社区集市仓库,修改该库的 -plugins.json。该文件是所有社区插件库的索引,格式为: - -```json -{ - "repos": [ - "username/reponame" - ] -} -``` - -PR 被合并以后集市会通过 GitHub Actions 自动更新索引并部署。后续发布新版本插件时只需要按照上述步骤创建新的发布即可,不需要再 -PR 社区集市仓库。 - -正常情况下,社区集市仓库每隔 1 小时会自动更新索引并部署,可在 https://github.com/siyuan-note/bazaar/actions 查看部署状态。 - -## 使用 Github action 自动发布 - -样例中自带了 github action,可以自动打包发布,请遵循以下操作: - -1. 设置项目 `https://github.com/OWNER/REPO/settings/actions` 页面向下划到 **Workflow Permissions**,打开配置 - - ![](asset/action.png) - -2. 需要发布版本的时候,push 一个格式为 `v*` 的 tag,github 就会自动打包发布 release(包括 package.zip) - -3. 默认使用保守策略进行 pre-release 发布,如果觉得没有必要,可以更改 release.yml 中的设置: - - ```yaml - - name: Release - uses: ncipollo/release-action@v1 - with: - allowUpdates: true - artifactErrorsFailBuild: true - artifacts: 'package.zip' - token: ${{ secrets.GITHUB_TOKEN }} - 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/icon.png b/icon.png index 47d51af493ad1c2baab0b5a3efd8fef8c8172238..15dd63490aeed689f1cda239968c6c52333f965c 100644 GIT binary patch literal 8596 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 YY({$Vm2xq5_osoN!0>?P;};nJ0*&CzP5=M^ diff --git a/package.json b/package.json index 2b7cdec..78f409d 100644 --- a/package.json +++ b/package.json @@ -33,5 +33,9 @@ "vite": "^5.2.9", "vite-plugin-static-copy": "^1.0.2", "vite-plugin-zip-pack": "^1.0.5" + }, + "dependencies": { + "@js-draw/material-icons": "^1.29.0", + "js-draw": "^1.29.0" } -} \ No newline at end of file +} diff --git a/plugin.json b/plugin.json index fd63a3c..8413186 100644 --- a/plugin.json +++ b/plugin.json @@ -1,8 +1,8 @@ { - "name": "plugin-sample-vite-svelte", - "author": "frostime", - "url": "https://github.com/siyuan-note/plugin-sample-vite-svelte", - "version": "0.3.6", + "name": "siyuan-jsdraw-plugin", + "author": "massivebox", + "url": "https://git.massive.box/massivebox/siyuan-jsdraw-plugin", + "version": "0.1.0", "minAppVersion": "3.0.12", "backends": [ "windows", @@ -21,16 +21,13 @@ "desktop-window" ], "displayName": { - "en_US": "Plugin sample with vite and svelte", - "zh_CN": "插件样例 vite + svelte 版" + "en_US": "JS-Draw Whiteboard" }, "description": { - "en_US": "SiYuan plugin sample with vite and svelte", - "zh_CN": "使用 vite 和 svelte 开发的思源插件样例" + "en_US": "Include a whiteboard for freehand drawing anywhere in your documents." }, "readme": { - "en_US": "README.md", - "zh_CN": "README_zh_CN.md" + "en_US": "README.md" }, "funding": { "custom": [ @@ -39,7 +36,9 @@ }, "keywords": [ "plugin", - "sample", - "插件样例" + "drawing", + "freehand", + "tablet", + "whiteboard" ] } diff --git a/preview.png b/preview.png index 95b93c78945df386d6b0aba70b2dd84ff7dc1f47..fb8c7e505231869651e8ec02c943ee78d1020ca3 100644 GIT binary patch literal 75167 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 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/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/public/webapp/button.js b/public/webapp/button.js new file mode 100644 index 0000000..610799d --- /dev/null +++ b/public/webapp/button.js @@ -0,0 +1,56 @@ +function copyEditLink(fileID) { + navigator.clipboard.writeText(getEditLink(fileID)); +} + +function refreshPage() { + window.location.reload(); +} + +function addButton(document, fileID) { + + // Add floating button + const floatingButton = document.createElement('button'); + floatingButton.id = 'floatingButton'; + floatingButton.innerHTML = '⚙️'; + document.body.appendChild(floatingButton); + + // Add popup menu + const popupMenu = document.createElement('div'); + popupMenu.id = 'popupMenu'; + popupMenu.innerHTML = ` + + + + `; + document.body.appendChild(popupMenu); + + // Show/hide floating button on mouse move + document.body.addEventListener('mousemove', () => { + floatingButton.style.display = 'block'; + }); + + document.body.addEventListener('mouseleave', () => { + floatingButton.style.display = 'none'; + }); + + // Toggle popup menu on button click + floatingButton.addEventListener('click', (e) => { + e.stopPropagation(); + popupMenu.style.display = popupMenu.style.display === 'block' ? 'none' : 'block'; + }); + + // Hide popup menu when clicking outside + document.addEventListener('click', () => { + popupMenu.style.display = 'none'; + }); + + // Set CSS variable for correct scaling of SVG + const svg = document.body.querySelector('svg'); + if (svg) { + const viewBox = svg.getAttribute('viewBox')?.split(' ') || []; + const width = parseFloat(viewBox[2]) || svg.clientWidth; + const height = parseFloat(viewBox[3]) || svg.clientHeight; + document.documentElement.style.setProperty('--svg-aspect-ratio', width/height); + } + +} \ No newline at end of file diff --git a/public/webapp/draw.js b/public/webapp/draw.js new file mode 100644 index 0000000..9d9adfc --- /dev/null +++ b/public/webapp/draw.js @@ -0,0 +1,47 @@ +const FALLBACK = "

Nothing here yet! Click me to open the editor.

" +async function getFile(path) { + + const response = await fetch('/api/file/getFile', { + method: 'POST', + headers: {'Content-Type': 'application/json'}, + body: JSON.stringify({path: path}) + }); + + if (!response.ok) { + console.log('Failed to fetch HTML content'); + return null; + } + + const blob = await response.blob(); + const resTxt = await blob.text(); + + // if we got a 404 api response, we will return null + try { + const res = JSON.parse(resTxt); + if(res.code === 404) { + return null; + } + }catch {} + + return resTxt; + +} + +async function getSVG(fileID) { + + const resp = await getFile("/data/assets/" + fileID + '.svg'); + if(resp == null) { + return FALLBACK; + } + return resp; + +} + +function getEditLink(fileID) { + const data = encodeURIComponent( + JSON.stringify({ + id: fileID + }) + ) + return `siyuan://plugins/siyuan-jsdraw-pluginwhiteboard/?icon=iconDraw&title=Drawing&data=${data}`; +} \ No newline at end of file diff --git a/public/webapp/index.css b/public/webapp/index.css new file mode 100644 index 0000000..e1dc21d --- /dev/null +++ b/public/webapp/index.css @@ -0,0 +1,91 @@ +a > div > p { + color: var(--text, black); +} + +html, body { + height: 100%; + width: 100%; + margin: 0; + padding: 0; + overflow: hidden; /* Prevent scrollbars */ +} + +body { + display: flex; + justify-content: center; + align-items: center; + background: transparent; +} + +div { + max-width: min(100vw, 100vh * var(--svg-aspect-ratio)); + max-height: min(100vh, 100vw / var(--svg-aspect-ratio)); + display: flex; + justify-content: center; + align-items: center; +} + +svg { + width: 100%; + height: 100%; + object-fit: contain; + overflow: hidden; +} + +/* Floating button styles */ +#floatingButton { + position: fixed; + bottom: 20px; + right: 20px; + background-color: #007bff; + color: white; + border: none; + border-radius: 50%; + width: 40px; + height: 40px; + font-size: 20px; + cursor: pointer; + display: none; /* Initially hidden */ +} + +/* Popup menu styles */ +#popupMenu { + position: fixed; + bottom: 70px; + right: 20px; + background-color: var(--popup-bg, white); + border: 1px solid var(--popup-border, #ccc); + border-radius: 5px; + padding: 10px; + display: none; /* Initially hidden */ + max-height: calc(100vh - 90px); /* Adjust based on window height */ + overflow-y: auto; /* Add scroll if content overflows */ +} + +#popupMenu button { + display: block; + margin: 5px 0; + padding: 5px 10px; + background-color: var(--button-bg, #f8f9fa); + border: 1px solid var(--button-border, #ccc); + border-radius: 3px; + cursor: pointer; + color: var(--button-text, black); +} + +#popupMenu button:hover { + background-color: var(--button-hover-bg, #e2e6ea); +} + +/* Dark theme styles */ +@media (prefers-color-scheme: dark) { + :root { + --text: white; + --popup-bg: #333; + --popup-border: #555; + --button-bg: #444; + --button-border: #666; + --button-text: #fff; + --button-hover-bg: #555; + } +} \ No newline at end of file diff --git a/public/webapp/index.html b/public/webapp/index.html new file mode 100644 index 0000000..a1fd2f6 --- /dev/null +++ b/public/webapp/index.html @@ -0,0 +1,26 @@ + + + + + + + + + + + \ No newline at end of file diff --git a/src/const.ts b/src/const.ts new file mode 100644 index 0000000..1acaa80 --- /dev/null +++ b/src/const.ts @@ -0,0 +1,7 @@ +export const SVG_MIME = "image/svg+xml"; +export const JSON_MIME = "application/json"; +export const DATA_PATH = "/data/assets"; +export const STORAGE_PATH = "/data/storage/petal/siyuan-jsdraw-plugin"; +export const TOOLBAR_PATH = STORAGE_PATH + "/toolbar.json"; +export const CONFIG_PATH = STORAGE_PATH + "/conf.json"; +export const EMBED_PATH = "/plugins/siyuan-jsdraw-plugin/webapp/?id="; \ No newline at end of file diff --git a/src/editorTab.ts b/src/editorTab.ts new file mode 100644 index 0000000..bef75c7 --- /dev/null +++ b/src/editorTab.ts @@ -0,0 +1,75 @@ +import {ITabModel, openTab, Plugin} from "siyuan" +import Editor, {BaseWidget, EditorEventType} from "js-draw"; +import { MaterialIconProvider } from '@js-draw/material-icons'; +import 'js-draw/styles'; +import {getFile, saveFile} from "@/file"; +import {JSON_MIME, SVG_MIME, TOOLBAR_PATH} from "@/const"; +import {idToPath} from "@/helper"; + +export function openEditorTab(p: Plugin, fileID: string) { + openTab({ + app: p.app, + custom: { + title: 'Drawing', + icon: 'iconDraw', + id: "siyuan-jsdraw-pluginwhiteboard", + data: { id: fileID } + } + }); +} + +async function saveCallback(editor: Editor, fileID: string, saveButton: BaseWidget) { + const svgElem = editor.toSVG(); + try { + saveFile(idToPath(fileID), SVG_MIME, svgElem.outerHTML); + saveButton.setDisabled(true); + setTimeout(() => { // @todo improve save button feedback + saveButton.setDisabled(false); + }, 500); + } catch (error) { + alert("Error saving drawing! Enter developer mode to find the error, and a copy of the current status."); + console.error(error); + console.log("Couldn't save SVG: ", svgElem.outerHTML) + } + +} + +export function createEditor(i: ITabModel) { + + const fileID = i.data.id; + if(fileID == null) { + alert("File ID missing - couldn't open file.") + return; + } + + const editor = new Editor(i.element, { + iconProvider: new MaterialIconProvider(), + }); + + const toolbar = editor.addToolbar(); + + // restore toolbar state + getFile(TOOLBAR_PATH).then(toolbarState => { + if(toolbarState!= null) { + toolbar.deserializeState(toolbarState) + } + }); + // restore drawing + getFile(idToPath(fileID)).then(svg => { + if(svg != null) { + editor.loadFromSVG(svg); + } + }); + + // save logic + const saveButton = toolbar.addSaveButton(() => saveCallback(editor, fileID, saveButton)); + + // save toolbar config on tool change (toolbar state is not saved in SVGs!) + editor.notifier.on(EditorEventType.ToolUpdated, () => { + saveFile(TOOLBAR_PATH, JSON_MIME, toolbar.serializeState()); + }); + + editor.dispatch(editor.setBackgroundStyle({ autoresize: true }), false); + editor.getRootElement().style.height = '100%'; + +} \ No newline at end of file diff --git a/src/file.ts b/src/file.ts new file mode 100644 index 0000000..5ce0d20 --- /dev/null +++ b/src/file.ts @@ -0,0 +1,37 @@ +import {getFileBlob, putFile} from "@/api"; + +function toFile(title: string, content: string, mimeType: string){ + const blob = new Blob([content], { type: mimeType }); + return new File([blob], title, { type: mimeType }); +} + +export function saveFile(path: string, mimeType: string, content: string) { + + const file = toFile(path.split('/').pop(), content, mimeType); + + try { + putFile(path, false, file); + } catch (error) { + console.error("Error saving file:", error); + throw error; + } + +} + +export async function getFile(path: string) { + + const blob = await getFileBlob(path); + const jsonText = await blob.text(); + + // if we got a 404 api response, we will return null + try { + const res = JSON.parse(jsonText); + if(res.code == 404) { + return null; + } + }catch {} + + // js-draw expects a string! + return jsonText; + +} diff --git a/src/hello.svelte b/src/hello.svelte deleted file mode 100644 index 967c7f6..0000000 --- a/src/hello.svelte +++ /dev/null @@ -1,63 +0,0 @@ - - - -
-
appId:
-
-
${app?.appId}
-
-
-
API demo:
-
-
- System current time: {time} -
-
-
-
Protyle demo: id = {blockID}
-
-
-
- diff --git a/src/helper.ts b/src/helper.ts new file mode 100644 index 0000000..18386c7 --- /dev/null +++ b/src/helper.ts @@ -0,0 +1,57 @@ +import { Plugin } from 'siyuan'; +import {DATA_PATH, EMBED_PATH} from "@/const"; + +const drawIcon: string = ` + + + +`; + +export function loadIcons(p: Plugin) { + const icons = drawIcon; + p.addIcons(icons); +} + +export function getMenuHTML(icon: string, text: string): string { + return ` +
+ + + + ${text} +
+ `; +} + +export function generateSiyuanId() { + const now = new Date(); + + const year = now.getFullYear().toString(); + const month = (now.getMonth() + 1).toString().padStart(2, '0'); + const day = now.getDate().toString().padStart(2, '0'); + const hours = now.getHours().toString().padStart(2, '0'); + const minutes = now.getMinutes().toString().padStart(2, '0'); + const seconds = now.getSeconds().toString().padStart(2, '0'); + + const timestamp = `${year}${month}${day}${hours}${minutes}${seconds}`; + + const characters = 'abcdefghijklmnopqrstuvwxyz'; + let random = ''; + for (let i = 0; i < 7; i++) { + random += characters.charAt(Math.floor(Math.random() * characters.length)); + } + + return `${timestamp}-${random}`; +} + +export function idToPath(id: string) { + return DATA_PATH + '/' + id + '.svg'; +} + +// [Edit](siyuan://plugins/siyuan-jsdraw-pluginwhiteboard/?icon=iconDraw&title=Drawing&data={"id":"${id}"}) +// ![Drawing](assets/${id}.svg) +export function getPreviewHTML(id: string): string { + return ` + + ` +} \ No newline at end of file diff --git a/src/index.scss b/src/index.scss deleted file mode 100644 index e69de29..0000000 diff --git a/src/index.ts b/src/index.ts index 5b50c49..e19ecb2 100644 --- a/src/index.ts +++ b/src/index.ts @@ -1,943 +1,44 @@ -import { - Plugin, - showMessage, - confirm, - Dialog, - Menu, - openTab, - adaptHotkey, - getFrontend, - getBackend, - IModel, - Protyle, - openWindow, - IOperation, - Constants, - openMobileFileById, - lockScreen, - ICard, - ICardData -} from "siyuan"; -import "@/index.scss"; +import {Plugin, Protyle} from 'siyuan'; +import {getPreviewHTML, loadIcons, getMenuHTML, generateSiyuanId} from "@/helper"; +import {createEditor, openEditorTab} from "@/editorTab"; -import HelloExample from "@/hello.svelte"; -import SettingExample from "@/setting-example.svelte"; +export default class DrawJSPlugin extends Plugin { + onload() { -import { SettingUtils } from "./libs/setting-utils"; -import { svelteDialog } from "./libs/dialog"; - -const STORAGE_NAME = "menu-config"; -const TAB_TYPE = "custom_tab"; -const DOCK_TYPE = "dock_tab"; - -export default class PluginSample extends Plugin { - - customTab: () => IModel; - private isMobile: boolean; - private blockIconEventBindThis = this.blockIconEvent.bind(this); - private settingUtils: SettingUtils; - - async onload() { - this.data[STORAGE_NAME] = { readonlyText: "Readonly" }; - - console.log("loading plugin-sample", this.i18n); - - const frontEnd = getFrontend(); - this.isMobile = frontEnd === "mobile" || frontEnd === "browser-mobile"; - // 图标的制作参见帮助文档 - this.addIcons(` - - - - -`); - - const topBarElement = this.addTopBar({ - icon: "iconFace", - title: this.i18n.addTopBarIcon, - position: "right", - callback: () => { - if (this.isMobile) { - this.addMenu(); - } else { - let rect = topBarElement.getBoundingClientRect(); - // 如果被隐藏,则使用更多按钮 - if (rect.width === 0) { - rect = document.querySelector("#barMore").getBoundingClientRect(); - } - if (rect.width === 0) { - rect = document.querySelector("#barPlugins").getBoundingClientRect(); - } - this.addMenu(rect); - } + loadIcons(this); + //const id = Math.random().toString(36).substring(7); + this.addTab({ + 'type': "whiteboard", + init() { + createEditor(this); } }); - const statusIconTemp = document.createElement("template"); - statusIconTemp.innerHTML = `
- - - -
`; - statusIconTemp.content.firstElementChild.addEventListener("click", () => { - confirm("⚠️", this.i18n.confirmRemove.replace("${name}", this.name), () => { - this.removeData(STORAGE_NAME).then(() => { - this.data[STORAGE_NAME] = { readonlyText: "Readonly" }; - showMessage(`[${this.name}]: ${this.i18n.removedData}`); - }); - }); - }); - this.addStatusBar({ - element: statusIconTemp.content.firstElementChild as HTMLElement, - }); - - this.addCommand({ - langKey: "showDialog", - hotkey: "⇧⌘O", - callback: () => { - this.showDialog(); - }, - fileTreeCallback: (file: any) => { - console.log(file, "fileTreeCallback"); - }, - editorCallback: (protyle: any) => { - console.log(protyle, "editorCallback"); - }, - dockCallback: (element: HTMLElement) => { - console.log(element, "dockCallback"); - }, - }); - this.addCommand({ - langKey: "getTab", - hotkey: "⇧⌘M", - globalCallback: () => { - console.log(this.getOpenedTab()); - }, - }); - - this.addDock({ - config: { - position: "LeftBottom", - size: { width: 200, height: 0 }, - icon: "iconSaving", - title: "Custom Dock", - 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} -
-
`; - } - }, - destroy() { - console.log("destroy dock:", DOCK_TYPE); - } - }); - - 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("😊"); + id: "insert-drawing", + filter: ["Insert Drawing", "Add drawing", "whiteboard", "freehand", "graphics", "jsdraw"], + html: getMenuHTML("iconDraw", this.i18n.insertDrawing), + callback: (protyle: Protyle) => { + const uid = generateSiyuanId(); + protyle.insert(getPreviewHTML(uid), true, false); + openEditorTab(this, uid); } }]; - 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); - } - }); + // This function is automatically called when the layout is loaded. } - async onunload() { - console.log(this.i18n.byePlugin); - showMessage("Goodbye SiYuan Plugin"); - console.log("onunload"); + onunload() { + // This function is automatically called when the plugin is disabled. } uninstall() { - console.log("uninstall"); + // This function is automatically called when the plugin is uninstalled. } - 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) { - 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) { - const menu = new Menu("topBarSample", () => { - console.log(this.i18n.byeMenu); - }); - menu.addItem({ - icon: "iconInfo", - label: "Dialog(open help first)", - accelerator: this.commands[0].customHotkey, - click: () => { - this.showDialog(); - } - }); - if (!this.isMobile) { - menu.addItem({ - icon: "iconFace", - label: "Open Custom Tab", - click: () => { - const tab = openTab({ - app: this.app, - custom: { - icon: "iconFace", - title: "Custom Tab", - data: { - text: "This is my custom tab", - }, - id: this.name + TAB_TYPE - }, - }); - console.log(tab); - } - }); - menu.addItem({ - icon: "iconImage", - label: "Open Asset Tab(open help first)", - click: () => { - const tab = openTab({ - app: this.app, - asset: { - path: "assets/paragraph-20210512165953-ag1nib4.svg" - } - }); - console.log(tab); - } - }); - menu.addItem({ - icon: "iconFile", - label: "Open Doc Tab(open help first)", - click: async () => { - const tab = await openTab({ - app: this.app, - doc: { - id: "20200812220555-lj3enxa", - } - }); - console.log(tab); - } - }); - menu.addItem({ - icon: "iconSearch", - label: "Open Search Tab", - click: () => { - const tab = openTab({ - app: this.app, - search: { - k: "SiYuan" - } - }); - console.log(tab); - } - }); - menu.addItem({ - icon: "iconRiffCard", - label: "Open Card Tab", - click: () => { - const tab = openTab({ - app: this.app, - card: { - type: "all" - } - }); - console.log(tab); - } - }); - menu.addItem({ - icon: "iconLayout", - label: "Open Float Layer(open help first)", - click: () => { - this.addFloatLayer({ - ids: ["20210428212840-8rqwn5o", "20201225220955-l154bn4"], - defIds: ["20230415111858-vgohvf3", "20200813131152-0wk5akh"], - x: window.innerWidth - 768 - 120, - y: 32 - }); - } - }); - menu.addItem({ - icon: "iconOpenWindow", - label: "Open Doc Window(open help first)", - click: () => { - openWindow({ - doc: {id: "20200812220555-lj3enxa"} - }); - } - }); - } 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); - } - }); - 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); - } - }] - }); - menu.addSeparator(); - menu.addItem({ - icon: "iconSettings", - label: "Official Setting Dialog", - click: () => { - this.openSetting(); - } - }); - menu.addItem({ - icon: "iconSettings", - label: "A custom setting dialog (by svelte)", - click: () => { - this.openDIYSetting(); - } - }); - menu.addItem({ - icon: "iconSparkles", - label: this.data[STORAGE_NAME].readonlyText || "Readonly", - type: "readonly", - }); - if (this.isMobile) { - menu.fullscreen(); - } else { - menu.open({ - x: rect.right, - y: rect.bottom, - isLeft: true, - }); - } - } -} +} \ 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); }} - > - -
-
- - -