Merge pull request 'v1.5.0 Releases' (PR#43) from dev into main
Reviewed-on: #43
This commit is contained in:
commit
f0eb2f3307
|
@ -52,6 +52,10 @@ jobs:
|
|||
version=${{ github.event.release.tag_name }}
|
||||
version=${version#v}
|
||||
sed -i "s/version=.*-SNAPSHOT$/version=$version/1" gradle.properties
|
||||
else
|
||||
build=${{ github.run_number }}
|
||||
echo 当前版本的构建号为: $build
|
||||
sed -i "s/\(version=[0-9]*\.[0-9]*\.[0-9]*\)-SNAPSHOT/\1-build.$build-SNAPSHOT/g" gradle.properties
|
||||
fi
|
||||
./gradlew clean build -x test
|
||||
- name: Archive plugin-starter jar
|
||||
|
|
49
README.md
49
README.md
|
@ -1,5 +1,15 @@
|
|||
# halo-plugin-vditor
|
||||
|
||||
---
|
||||
|
||||
## NEXT VERSION
|
||||
|
||||
- ✨ 使用新的快速插入表单,界面更美观
|
||||
- ✨ 支持渲染其他插件/主题的自定义组件(需要开发者支持)
|
||||
- ✨ 允许用户禁用HTML代码块隐藏的特性
|
||||
|
||||
---
|
||||
|
||||
本插件将Vditor整合进Halo,支持所见即所得编辑模式。 Support English (*Only Editor)!
|
||||
|
||||
编辑器支持数学公式、脑图、图表、流程图、甘特图、时序图、五线谱、graphviz 渲染、plantumlUML图
|
||||
|
@ -16,6 +26,7 @@
|
|||
- 支持字符计数
|
||||
- 支持在前台注入渲染脚本(需在设置中开启)
|
||||
- 更多强大的语法功能请 [->到这<-](https://github.com/Vanessa219/vditor) 查看 (部分功能仍未支持)
|
||||
- 支持独有的自定义语法,详细语法请参考 [这里](https://github.com/justice2001/vditor-halo-render#语法参考)
|
||||
|
||||
## 💻使用方式
|
||||
|
||||
|
@ -29,13 +40,15 @@
|
|||
|
||||
下面是当前已知的兼容性问题
|
||||
|
||||
- 在同时使用ToolBench插件时,数学公式、脑图、图表、流程图、甘特图、时序图、五线谱无法正常渲染。`会尝试修复`
|
||||
- 在同时使用ToolBench插件时,数学公式、脑图、图表、流程图、甘特图、时序图、五线谱无法正常渲染。`由于此插件修改了页面结构,所以不会修复`
|
||||
- Vditor渲染器不会根据主题暗色模式进行改变,当前仅支持跟随系统暗色模式
|
||||
|
||||
## 📒TODO
|
||||
|
||||
> 如果可以支持的功能将会加入到这个TODO列表中,列表中没有的功能也未必是不能支持的,可能只是开发者没有想到
|
||||
|
||||
> 插件的进度在 [Gitea](https://git.mczhengyi.top/zhengyi/halo-plugin-vditor/issues) 进行管理,当前Gitea不支持您评论,如果您想讨论Issue可以直接在Github上打开一个Issue并表明要讨论的Gitea Issue,您的回复将会被同步到Gitea。
|
||||
|
||||
- [x] 能够在Halo中运行Vditor编辑器
|
||||
- [x] 支持数学公式、脑图、图表、流程图、甘特图、时序图、五线谱、graphviz 渲染、plantumlUML图
|
||||
- [x] 添加编辑器默认展示模式设置
|
||||
|
@ -43,23 +56,15 @@
|
|||
- [x] 添加打字机模式
|
||||
- [x] 支持附件选取插入
|
||||
- [x] 支持暗色主题渲染
|
||||
- [ ] `🧪` 支持多媒体渲染
|
||||
- [x] `💻` 拆分配置为编辑器配置与渲染配置两部分
|
||||
- [x] `💻` 添加多国语言支持 `手搓翻译函数实现`
|
||||
- [ ] `💻` 支持代码高亮及复制
|
||||
- [x] `💻` 跟随主题的暗色模式(joe主题)
|
||||
- [ ] 将Vditor前台渲染资源全量引入本地
|
||||
- [ ] 添加配置是否使用CDN加载前台资源
|
||||
- [ ] `🐛` 内置渲染器与ToolBench不兼容
|
||||
- [ ] `📄` 自定义暗色模式触发方式
|
||||
- [ ] `📄` 支持AI编写与修改
|
||||
|
||||
**注释**
|
||||
|
||||
- `🧪` 当前已在某个版本进行实验性发布
|
||||
- `📄` 一个想法,如果你有什么比较好的建议可以在ISSUE中提出
|
||||
- `💻` 计划在下个版本推出
|
||||
- `🐛` 这是一个BUG,但是比较难处理
|
||||
- [x] 支持多媒体渲染
|
||||
- [x] 拆分配置为编辑器配置与渲染配置两部分
|
||||
- [x] 添加多国语言支持 `手搓翻译函数实现`
|
||||
- [x] 跟随主题的暗色模式(joe主题)
|
||||
- [x] 将Vditor前台渲染资源全量引入本地
|
||||
- [ ] 支持代码高亮及复制
|
||||
- [ ] 自定义暗色模式触发方式
|
||||
- [ ] 支持AI编写与修改
|
||||
- [ ] ~~内置渲染器与ToolBench不兼容~~
|
||||
|
||||
## 🙏 鸣谢
|
||||
|
||||
|
@ -98,10 +103,18 @@ cd path/to/plugin-starter
|
|||
|
||||
```bash
|
||||
# 下载依赖包
|
||||
|
||||
# macOS/Linux执行:
|
||||
chmod a+x download_dist.sh
|
||||
./download_dist.sh
|
||||
|
||||
# Windows
|
||||
# 要求安装7Zip并将7Zip的文件夹加入Path环境变量
|
||||
./download_dist.bat
|
||||
```
|
||||
|
||||
|
||||
|
||||
```bash
|
||||
# macOS / Linux
|
||||
./gradlew pnpmInstall
|
||||
|
|
12
build.gradle
12
build.gradle
|
@ -39,3 +39,15 @@ build {
|
|||
// build frontend before build
|
||||
tasks.getByName('compileJava').dependsOn('buildFrontend')
|
||||
}
|
||||
|
||||
halo {
|
||||
version = '2.11.3'
|
||||
superAdminUsername = 'admin'
|
||||
superAdminPassword = 'admin'
|
||||
externalUrl = 'http://localhost:8090'
|
||||
docker {
|
||||
// windows 默认为 npipe:////./pipe/docker_engine
|
||||
url = 'npipe:////./pipe/docker_engine'
|
||||
apiVersion = '1.42'
|
||||
}
|
||||
}
|
|
@ -11,11 +11,12 @@
|
|||
"lint": "eslint . --ext .vue,.js,.jsx,.cjs,.mjs,.ts,.tsx,.cts,.mts --fix --ignore-path .gitignore"
|
||||
},
|
||||
"dependencies": {
|
||||
"@formkit/vue": "^1.4.0",
|
||||
"@halo-dev/api-client": "^2.11.0",
|
||||
"@halo-dev/components": "^1.10.0",
|
||||
"@halo-dev/console-shared": "^2.11.0",
|
||||
"@zhengyi/vditor": "3.9.10",
|
||||
"canvas-confetti": "^1.6.0",
|
||||
"@zhengyi/vditor": "3.9.9",
|
||||
"vue": "^3.3.4"
|
||||
},
|
||||
"devDependencies": {
|
||||
|
|
|
@ -5,6 +5,9 @@ settings:
|
|||
excludeLinksFromLockfile: false
|
||||
|
||||
dependencies:
|
||||
'@formkit/vue':
|
||||
specifier: ^1.4.0
|
||||
version: 1.4.0(typescript@4.7.4)
|
||||
'@halo-dev/api-client':
|
||||
specifier: ^2.11.0
|
||||
version: 2.11.0
|
||||
|
@ -15,8 +18,8 @@ dependencies:
|
|||
specifier: ^2.11.0
|
||||
version: 2.11.0(vue-router@4.2.5)(vue@3.3.12)
|
||||
'@zhengyi/vditor':
|
||||
specifier: 3.9.9
|
||||
version: 3.9.9
|
||||
specifier: 3.9.10
|
||||
version: 3.9.10
|
||||
canvas-confetti:
|
||||
specifier: ^1.6.0
|
||||
version: 1.9.2
|
||||
|
@ -472,6 +475,98 @@ packages:
|
|||
resolution: {integrity: sha512-OfX7E2oUDYxtBvsuS4e/jSn4Q9Qb6DzgeYtsAdkPZ47znpoNsMgZw0+tVijiv3uGNR6dgNlty6r9rzIzHjtd/A==}
|
||||
dev: false
|
||||
|
||||
/@formkit/core@1.4.0:
|
||||
resolution: {integrity: sha512-LQYICzYWTC+ZXPyfizHDRJTBx6WLM3eRk9T4tzU8YPV58AYWPq3E4dbN5CKl7mPApAcJX6NxQOCYuNrdNKZvQA==}
|
||||
dependencies:
|
||||
'@formkit/utils': 1.4.0
|
||||
dev: false
|
||||
|
||||
/@formkit/dev@1.4.0:
|
||||
resolution: {integrity: sha512-y71zSZGAWdqjuj5p7IvptJXgDXYFTZrJSjZ9HeQjNYJ1n3Nwc7latTzJSEQyOHuboSzRWafATYNeB3pqiTvBYg==}
|
||||
dependencies:
|
||||
'@formkit/core': 1.4.0
|
||||
'@formkit/utils': 1.4.0
|
||||
dev: false
|
||||
|
||||
/@formkit/i18n@1.4.0:
|
||||
resolution: {integrity: sha512-NRqw3ummnboUVkxN68jdy/T/mvgnf0/8v76160V3dTdt4OK9Q1Eq0a7pgonm0gBqisezX/0pELRWD0NPgUZSPw==}
|
||||
dependencies:
|
||||
'@formkit/core': 1.4.0
|
||||
'@formkit/utils': 1.4.0
|
||||
'@formkit/validation': 1.4.0
|
||||
dev: false
|
||||
|
||||
/@formkit/inputs@1.4.0:
|
||||
resolution: {integrity: sha512-6IvgjOZnvtYq2oSEXkarDCaabxl4o29FhFmD64d0lC8WTaVLcRPKgo9BsLaVvLYfJgVKDzFyPIMGLFNhTsiJ/Q==}
|
||||
dependencies:
|
||||
'@formkit/core': 1.4.0
|
||||
'@formkit/utils': 1.4.0
|
||||
dev: false
|
||||
|
||||
/@formkit/observer@1.4.0:
|
||||
resolution: {integrity: sha512-sNjLqi+deN2TCnuRDWdOJ1OjTyxavFKg9pVK1E1KRrbQr0pvPDaS4qYIjrIIWg0YMsQiea487r03elLVesMWWw==}
|
||||
dependencies:
|
||||
'@formkit/core': 1.4.0
|
||||
'@formkit/utils': 1.4.0
|
||||
dev: false
|
||||
|
||||
/@formkit/rules@1.4.0:
|
||||
resolution: {integrity: sha512-NEVDjN89Zwx26Ze0gwqd/Bml3d4QIzp3SDps3uND9IE1Ssd3yvvSSjF5jVeHLE7ZMDLUDfOepf5xyqhPE1UbqA==}
|
||||
dependencies:
|
||||
'@formkit/core': 1.4.0
|
||||
'@formkit/utils': 1.4.0
|
||||
'@formkit/validation': 1.4.0
|
||||
dev: false
|
||||
|
||||
/@formkit/themes@1.4.0:
|
||||
resolution: {integrity: sha512-isyHZALh6S3BM+0G5NeRQkI12gydDRXEkI6oGt5uTn3Ju2olLt8RwOXNYgJ9lWSb8HrfuHh7/89SozdIRNVuCA==}
|
||||
peerDependencies:
|
||||
tailwindcss: ^3.2.0
|
||||
unocss: ^0.31.0
|
||||
windicss: ^3.0.0
|
||||
peerDependenciesMeta:
|
||||
tailwindcss:
|
||||
optional: true
|
||||
unocss:
|
||||
optional: true
|
||||
windicss:
|
||||
optional: true
|
||||
dependencies:
|
||||
'@formkit/core': 1.4.0
|
||||
dev: false
|
||||
|
||||
/@formkit/utils@1.4.0:
|
||||
resolution: {integrity: sha512-HAkULL7/0PnRZmMJyFHZ3wxxTYl+tuJrSk13/LQyxB77luwlq8sCvlGaF5cz+0JX70HSVVc0ZJjJT4o0uTVeYQ==}
|
||||
dev: false
|
||||
|
||||
/@formkit/validation@1.4.0:
|
||||
resolution: {integrity: sha512-KF25aU5ouwg+dFryyDGPTEFgik2TzbiT824PkIQJn7Fgepplo3Dj/UvjHmM2u0hBvIE2CS6XtgE0XVhn1kRG+A==}
|
||||
dependencies:
|
||||
'@formkit/core': 1.4.0
|
||||
'@formkit/observer': 1.4.0
|
||||
'@formkit/utils': 1.4.0
|
||||
dev: false
|
||||
|
||||
/@formkit/vue@1.4.0(typescript@4.7.4):
|
||||
resolution: {integrity: sha512-qxLJLG/rc0Mv75h9aaBu1SIFo8AZq1mMCa+u4aWQdXf0k/e8Mfuyhkch25c9pfbzSjEpFzs7G7L5U1gMuTcmtQ==}
|
||||
dependencies:
|
||||
'@formkit/core': 1.4.0
|
||||
'@formkit/dev': 1.4.0
|
||||
'@formkit/i18n': 1.4.0
|
||||
'@formkit/inputs': 1.4.0
|
||||
'@formkit/observer': 1.4.0
|
||||
'@formkit/rules': 1.4.0
|
||||
'@formkit/themes': 1.4.0
|
||||
'@formkit/utils': 1.4.0
|
||||
'@formkit/validation': 1.4.0
|
||||
vue: 3.3.12(typescript@4.7.4)
|
||||
transitivePeerDependencies:
|
||||
- tailwindcss
|
||||
- typescript
|
||||
- unocss
|
||||
- windicss
|
||||
dev: false
|
||||
|
||||
/@halo-dev/api-client@2.11.0:
|
||||
resolution: {integrity: sha512-i3PFETsPdHYnTgk3jORu00t43/rCesmqpdZg38/Hq2AdgxhPkE7rghYGdoZRLdanvVC0HM1Axn18Zd7kdizxVA==}
|
||||
dev: false
|
||||
|
@ -1026,8 +1121,8 @@ packages:
|
|||
resolution: {integrity: sha512-r+aD7wvdXVRyFcw/yI/PbdIqSsC+Bnql2WSKZa8CK+ntpTvc2tybn6Wxsfw96C71QS+Q/MPe8ufwZBjtRd+hhg==, tarball: https://git.mczhengyi.top/api/packages/zhengyi/npm/%40zhengyi%2Fhalo-render/-/1.1.0/halo-render-1.1.0.tgz}
|
||||
dev: false
|
||||
|
||||
/@zhengyi/vditor@3.9.9:
|
||||
resolution: {integrity: sha512-YHlWEbVkDvctAYUAghIISPjTPqxwubLuFlgbm1L6Oq3sGdIt4YZKUJA7Psv/JHaasX/tIvvcgZyUygSjjRBzMw==, tarball: https://git.mczhengyi.top/api/packages/zhengyi/npm/%40zhengyi%2Fvditor/-/3.9.9/vditor-3.9.9.tgz}
|
||||
/@zhengyi/vditor@3.9.10:
|
||||
resolution: {integrity: sha512-8bAUlH4bsuUNbkrxAuxTL0XMNoLre/mab5HF8cR9wND7e/Xj56l2qz7aEARklK6oRtOlCoTYK6JEkLdozUfTkg==, tarball: https://git.mczhengyi.top/api/packages/zhengyi/npm/%40zhengyi%2Fvditor/-/3.9.10/vditor-3.9.10.tgz}
|
||||
dependencies:
|
||||
'@zhengyi/halo-render': 1.1.0
|
||||
diff-match-patch: 1.0.5
|
||||
|
|
|
@ -23,6 +23,7 @@ const lang: I18nLang = {
|
|||
title: "Title",
|
||||
link: "Link",
|
||||
password: "Password",
|
||||
quick_insert: "Quick Insert",
|
||||
};
|
||||
|
||||
export default lang;
|
||||
|
|
|
@ -23,6 +23,7 @@ const lang: I18nLang = {
|
|||
title: "标题",
|
||||
link: "链接",
|
||||
password: "密码",
|
||||
quick_insert: "快速插入",
|
||||
};
|
||||
|
||||
export default lang;
|
||||
|
|
|
@ -23,6 +23,7 @@ const lang: I18nLang = {
|
|||
title: "標題",
|
||||
link: "鏈接",
|
||||
password: "密碼",
|
||||
quick_insert: "快速插入",
|
||||
};
|
||||
|
||||
export default lang;
|
||||
|
|
|
@ -1,75 +0,0 @@
|
|||
<script setup lang="ts">
|
||||
import { VModal, VButton, VSpace } from "@halo-dev/components";
|
||||
import { t } from "@/utils/i18n-utils";
|
||||
import { ref } from "vue";
|
||||
|
||||
const props = defineProps<{
|
||||
open: boolean;
|
||||
}>();
|
||||
const emit = defineEmits<{
|
||||
(event: "done", value: string): void;
|
||||
(event: "close"): void;
|
||||
}>();
|
||||
|
||||
const platform = ref("baidu");
|
||||
const name = ref("");
|
||||
const link = ref("");
|
||||
const password = ref("");
|
||||
|
||||
const generateCode = () => {
|
||||
let code = "\n\n```halo\n";
|
||||
code += `drive:${platform.value}\n`;
|
||||
code += `name: ${name.value}\n`;
|
||||
code += `link: ${link.value}\n`;
|
||||
if (password.value) {
|
||||
code += `password: ${password.value}\n`;
|
||||
}
|
||||
emit("done", code + "```\n\n");
|
||||
};
|
||||
</script>
|
||||
|
||||
<template>
|
||||
<VModal
|
||||
:visible="props.open"
|
||||
:layer-closable="false"
|
||||
:title="t('insert_drive')"
|
||||
@close="emit('close')"
|
||||
>
|
||||
<v-space align="start" direction="column" spacing="xs" style="width: 100%">
|
||||
<label for="type" class="vditor-mde-label">
|
||||
<span>{{ t("platform") }}</span>
|
||||
<select id="platform" v-model="platform" class="vditor-mde-select">
|
||||
<option value="baidu">{{ t("baidu_net_disk") }}</option>
|
||||
<option value="ali">{{ t("ali_drive") }}</option>
|
||||
</select>
|
||||
</label>
|
||||
<label for="name" class="vditor-mde-label">
|
||||
<span>{{ t("title") }}</span>
|
||||
<input id="name" v-model="name" type="text" class="vditor-mde-input" />
|
||||
</label>
|
||||
<label for="link" class="vditor-mde-label">
|
||||
<span>{{ t("link") }}</span>
|
||||
<input id="link" v-model="link" type="text" class="vditor-mde-input" />
|
||||
</label>
|
||||
<label for="password" class="vditor-mde-label">
|
||||
<span>{{ t("password") }}</span>
|
||||
<input
|
||||
id="password"
|
||||
v-model="password"
|
||||
type="text"
|
||||
class="vditor-mde-input"
|
||||
/>
|
||||
</label>
|
||||
</v-space>
|
||||
<template #footer>
|
||||
<v-space align="center" direction="row" spacing="xs">
|
||||
<v-button type="primary" @click="generateCode">
|
||||
{{ t("confirm") }}
|
||||
</v-button>
|
||||
<v-button type="default" @click="emit('close')">
|
||||
{{ t("close") }}
|
||||
</v-button>
|
||||
</v-space>
|
||||
</template>
|
||||
</VModal>
|
||||
</template>
|
|
@ -1,72 +0,0 @@
|
|||
<script setup lang="ts">
|
||||
import { VModal, VButton, VSpace } from "@halo-dev/components";
|
||||
import { t } from "@/utils/i18n-utils";
|
||||
import { ref } from "vue";
|
||||
|
||||
const URL_NOT_SHOW = ["github", "gitee"];
|
||||
|
||||
const props = defineProps<{
|
||||
open: boolean;
|
||||
}>();
|
||||
const emit = defineEmits<{
|
||||
(event: "done", value: string): void;
|
||||
(event: "close"): void;
|
||||
}>();
|
||||
|
||||
const url = ref("");
|
||||
const platform = ref("github");
|
||||
const owner = ref("");
|
||||
const repo = ref("");
|
||||
|
||||
const generateCode = () => {
|
||||
emit(
|
||||
"done",
|
||||
"\n\n```halo\n" +
|
||||
`git:[${url.value}@${platform.value}/${owner.value}/${repo.value}]` +
|
||||
"\n```\n\n"
|
||||
);
|
||||
};
|
||||
</script>
|
||||
|
||||
<template>
|
||||
<VModal
|
||||
:visible="props.open"
|
||||
:layer-closable="false"
|
||||
:title="t('insert_git')"
|
||||
@close="emit('close')"
|
||||
>
|
||||
<v-space align="start" direction="column" spacing="xs" style="width: 100%">
|
||||
<label v-if="URL_NOT_SHOW.indexOf(platform) === -1" for="url">
|
||||
<span>URL</span>
|
||||
<input id="url" v-model="url" type="text" class="vditor-mde-input" />
|
||||
</label>
|
||||
<label for="type" class="vditor-mde-label">
|
||||
<span>{{ t("platform") }}</span>
|
||||
<select id="platform" v-model="platform" class="vditor-mde-select">
|
||||
<option value="github">GitHub</option>
|
||||
<option value="gitee">Gitee</option>
|
||||
</select>
|
||||
</label>
|
||||
<label for="owner" class="vditor-mde-label">
|
||||
<span>{{ t("owner") }}</span>
|
||||
<input id="owner" v-model="owner" type="text" class="vditor-mde-input" />
|
||||
</label>
|
||||
<label for="repo" class="vditor-mde-label">
|
||||
<span>{{ t("repo") }}</span>
|
||||
<input id="repo" v-model="repo" type="text" class="vditor-mde-input" />
|
||||
</label>
|
||||
</v-space>
|
||||
<template #footer>
|
||||
<v-space align="center" direction="row" spacing="xs">
|
||||
<v-button type="primary" @click="generateCode">
|
||||
{{ t("confirm") }}
|
||||
</v-button>
|
||||
<v-button type="default" @click="emit('close')">
|
||||
{{ t("close") }}
|
||||
</v-button>
|
||||
</v-space>
|
||||
</template>
|
||||
</VModal>
|
||||
</template>
|
||||
|
||||
<style scoped></style>
|
|
@ -0,0 +1,89 @@
|
|||
<script setup lang="ts">
|
||||
import { FormKit, FormKitSchema } from "@formkit/vue";
|
||||
import { ref, watch } from "vue";
|
||||
import { t } from "@/utils/i18n-utils";
|
||||
import { VButton, VModal, VSpace } from "@halo-dev/components";
|
||||
import type { Schema } from "@/type/editor";
|
||||
|
||||
const data = ref<{ [key: string]: string }>({});
|
||||
const loadKey = ref("");
|
||||
let idCount = 0;
|
||||
|
||||
const props = defineProps<{
|
||||
open: boolean;
|
||||
schema: Schema;
|
||||
}>();
|
||||
const emit = defineEmits<{
|
||||
(event: "done", value: string | null): void;
|
||||
(event: "close"): void;
|
||||
}>();
|
||||
|
||||
const generateCode = () => {
|
||||
if (props.schema.template) {
|
||||
let code = props.schema.template || "";
|
||||
const formkit = props.schema.formKit;
|
||||
formkit.forEach((form: { [key: string]: string }) => {
|
||||
code = code.replace(
|
||||
`$${form.name}$`,
|
||||
data.value[form.name] || form.value
|
||||
);
|
||||
});
|
||||
emit("done", htmlEncode(code));
|
||||
return;
|
||||
}
|
||||
props.schema.handler && props.schema.handler(data.value);
|
||||
emit("done", null);
|
||||
};
|
||||
|
||||
// 修改FormKit ID来实现Schema重载
|
||||
watch(props, (val, old) => {
|
||||
if (props.open) {
|
||||
if (old.schema.id === val.schema.id) {
|
||||
loadKey.value = `${val.schema.id}-${idCount++}`;
|
||||
} else {
|
||||
idCount = 0;
|
||||
loadKey.value = props.schema.id;
|
||||
}
|
||||
console.log("This Load Key: " + loadKey.value);
|
||||
props.schema.formKit.forEach((form: { [key: string]: string }) => {
|
||||
data.value[form.name] = form.value;
|
||||
});
|
||||
}
|
||||
});
|
||||
|
||||
const htmlEncode = (str: string) => {
|
||||
let s = "";
|
||||
if (str.length === 0) {
|
||||
return "";
|
||||
}
|
||||
s = str.replace(/</g, "<");
|
||||
s = s.replace(/>/g, ">");
|
||||
s = s.replace(/"/g, """);
|
||||
return s;
|
||||
};
|
||||
</script>
|
||||
|
||||
<template>
|
||||
<VModal
|
||||
:visible="props.open"
|
||||
:layer-closable="false"
|
||||
:title="schema.name"
|
||||
@close="emit('close')"
|
||||
>
|
||||
<FormKit v-model="data" type="form">
|
||||
<FormKitSchema :key="loadKey" :schema="schema.formKit" :data="data" />
|
||||
</FormKit>
|
||||
<template #footer>
|
||||
<v-space align="center" direction="row" spacing="xs">
|
||||
<v-button type="primary" @click="generateCode">
|
||||
{{ t("confirm") }}
|
||||
</v-button>
|
||||
<v-button type="default" @click="emit('close')">
|
||||
{{ t("close") }}
|
||||
</v-button>
|
||||
</v-space>
|
||||
</template>
|
||||
</VModal>
|
||||
</template>
|
||||
|
||||
<style scoped></style>
|
|
@ -1,59 +0,0 @@
|
|||
<script setup lang="ts">
|
||||
import { VModal, VButton, VSpace } from "@halo-dev/components";
|
||||
import { ref } from "vue";
|
||||
import { t } from "@/utils/i18n-utils";
|
||||
|
||||
const props = defineProps<{
|
||||
open: boolean;
|
||||
}>();
|
||||
const emit = defineEmits<{
|
||||
(event: "done", value: string): void;
|
||||
(event: "close"): void;
|
||||
}>();
|
||||
|
||||
const type = ref("default");
|
||||
const content = ref("");
|
||||
|
||||
const generateCode = () => {
|
||||
emit(
|
||||
"done",
|
||||
"\n\n```halo\n" + `tips:${type.value}\n${content.value}\n` + "```\n\n"
|
||||
);
|
||||
};
|
||||
</script>
|
||||
|
||||
<template>
|
||||
<VModal
|
||||
:visible="props.open"
|
||||
:layer-closable="false"
|
||||
:title="t('insert_tips')"
|
||||
@close="emit('close')"
|
||||
>
|
||||
<v-space align="start" direction="column" spacing="xs" style="width: 100%">
|
||||
<label for="type" class="vditor-mde-label">
|
||||
<span>{{ t("type") }}</span>
|
||||
<select id="type" v-model="type" class="vditor-mde-select">
|
||||
<option value="default">{{ t("default") }}</option>
|
||||
<option value="danger">{{ t("danger") }}</option>
|
||||
<option value="warn">{{ t("warning") }}</option>
|
||||
<option value="info">{{ t("info") }}</option>
|
||||
<option value="success">{{ t("success") }}</option>
|
||||
</select>
|
||||
</label>
|
||||
<label for="content" class="vditor-mde-label">
|
||||
<span>{{ t("content") }}</span>
|
||||
<textarea id="content" v-model="content" class="vditor-mde-textarea" />
|
||||
</label>
|
||||
</v-space>
|
||||
<template #footer>
|
||||
<v-space align="center" direction="row" spacing="xs">
|
||||
<v-button type="primary" @click="generateCode">
|
||||
{{ t("confirm") }}
|
||||
</v-button>
|
||||
<v-button type="default" @click="emit('close')">
|
||||
{{ t("close") }}
|
||||
</v-button>
|
||||
</v-space>
|
||||
</template>
|
||||
</VModal>
|
||||
</template>
|
|
@ -0,0 +1,42 @@
|
|||
import type { Schema } from "@/type/editor";
|
||||
import { t } from "@/utils/i18n-utils";
|
||||
|
||||
const schema: Schema = {
|
||||
type: "template",
|
||||
id: "drive",
|
||||
icon: "",
|
||||
name: t("insert_drive"),
|
||||
formKit: [
|
||||
{
|
||||
$formkit: "select",
|
||||
name: "platform",
|
||||
label: t("platform"),
|
||||
value: "baidu",
|
||||
options: {
|
||||
baidu: t("baidu_net_disk"),
|
||||
ali: t("ali_drive"),
|
||||
},
|
||||
},
|
||||
{
|
||||
$formkit: "text",
|
||||
name: "title",
|
||||
label: t("title"),
|
||||
value: "",
|
||||
},
|
||||
{
|
||||
$formkit: "text",
|
||||
name: "link",
|
||||
label: t("link"),
|
||||
value: "",
|
||||
},
|
||||
{
|
||||
$formkit: "text",
|
||||
name: "password",
|
||||
label: t("password"),
|
||||
},
|
||||
],
|
||||
template:
|
||||
"```halo\ndrive:$platform$\nname:$title$\nlink:$link$\npassword:$password$\n```",
|
||||
};
|
||||
|
||||
export default schema;
|
|
@ -0,0 +1,44 @@
|
|||
import type { Schema } from "@/type/editor";
|
||||
import { t } from "@/utils/i18n-utils";
|
||||
|
||||
const schema: Schema = {
|
||||
type: "template",
|
||||
id: "git",
|
||||
icon: "",
|
||||
name: t("insert_git"),
|
||||
formKit: [
|
||||
{
|
||||
$formkit: "select",
|
||||
id: "platform",
|
||||
name: "platform",
|
||||
label: t("platform"),
|
||||
value: "github",
|
||||
options: {
|
||||
github: "GitHub",
|
||||
gitee: "Gitee",
|
||||
},
|
||||
},
|
||||
{
|
||||
$formkit: "text",
|
||||
name: "url",
|
||||
label: "URL",
|
||||
value: "",
|
||||
if: "false",
|
||||
},
|
||||
{
|
||||
$formkit: "text",
|
||||
name: "owner",
|
||||
label: t("owner"),
|
||||
value: "",
|
||||
},
|
||||
{
|
||||
$formkit: "text",
|
||||
name: "repo",
|
||||
label: t("repo"),
|
||||
value: "",
|
||||
},
|
||||
],
|
||||
template: "```halo\ngit:[$url$@$platform$/$owner$/$repo$]\n```",
|
||||
};
|
||||
|
||||
export default schema;
|
|
@ -0,0 +1,28 @@
|
|||
import type { Schema } from "@/type/editor";
|
||||
|
||||
const schema: Schema = {
|
||||
type: "template",
|
||||
id: "joe-progress",
|
||||
icon: "",
|
||||
name: "Joe Progress Bar",
|
||||
formKit: [
|
||||
{
|
||||
$formkit: "text",
|
||||
name: "percentage",
|
||||
label: "Percentage",
|
||||
help: "This is the percentage for progress bar.",
|
||||
value: "0%",
|
||||
},
|
||||
{
|
||||
$formkit: "color",
|
||||
name: "color",
|
||||
label: "Color",
|
||||
help: "This is the color for progress bar.",
|
||||
value: "#ffffff",
|
||||
},
|
||||
],
|
||||
template:
|
||||
'<joe-progress percentage="$percentage$" color="$color$"></joe-progress>',
|
||||
};
|
||||
|
||||
export default schema;
|
|
@ -0,0 +1,34 @@
|
|||
import type { Schema } from "@/type/editor";
|
||||
import {t} from "@/utils/i18n-utils";
|
||||
|
||||
const schema: Schema = {
|
||||
type: "template",
|
||||
id: "tips",
|
||||
icon: "",
|
||||
name: t("insert_tips"),
|
||||
formKit: [
|
||||
{
|
||||
$formkit: "select",
|
||||
name: "type",
|
||||
label: t("type"),
|
||||
help: "This is the percentage for progress bar.",
|
||||
value: "default",
|
||||
options: {
|
||||
default: t("default"),
|
||||
info: t("info"),
|
||||
success: t("success"),
|
||||
warn: t("warning"),
|
||||
danger: t("danger"),
|
||||
},
|
||||
},
|
||||
{
|
||||
$formkit: "textarea",
|
||||
name: "content",
|
||||
label: t("content"),
|
||||
value: "",
|
||||
},
|
||||
],
|
||||
template: "```halo\n" + `tips:$type$\n$content$\n` + "```",
|
||||
};
|
||||
|
||||
export default schema;
|
|
@ -1,12 +1,3 @@
|
|||
export declare type EditorConfig = {
|
||||
basic: {
|
||||
enable_render: boolean;
|
||||
defaultRenderMode: "ir" | "wysiwyg" | "sv" | undefined;
|
||||
typeWriterMode: boolean;
|
||||
codeBlockPreview: boolean;
|
||||
};
|
||||
};
|
||||
|
||||
export declare type Options = {
|
||||
defaultRenderMode: "ir" | "wysiwyg" | "sv" | undefined;
|
||||
typeWriterMode: boolean;
|
||||
|
@ -16,5 +7,40 @@ export declare type Options = {
|
|||
language: string;
|
||||
codeBlockPreview: boolean;
|
||||
uploadImage?: (files: File[]) => string | null | Promise;
|
||||
openModal: (name: string) => void;
|
||||
openModal: (schema: Schema) => void;
|
||||
quickInsertList: QuickInsert[];
|
||||
enableQuickInsert: boolean;
|
||||
};
|
||||
|
||||
export interface Schema {
|
||||
type: "template";
|
||||
id: string;
|
||||
icon?: string;
|
||||
name: string;
|
||||
formKit: Array;
|
||||
template?: string;
|
||||
// 解析后处理
|
||||
afterHandle?: (data: { [key: string]: string }, code: string) => string;
|
||||
// 覆盖解析
|
||||
handler?: (data: { [key: string]: string }) => string;
|
||||
}
|
||||
|
||||
export interface QuickInsert {
|
||||
// 展示名称
|
||||
name: string;
|
||||
// 鼠标移入时的提示文本
|
||||
tip: string;
|
||||
// 提供者
|
||||
provider: string;
|
||||
// 插入按钮的图标
|
||||
icon: string;
|
||||
// 配置结构
|
||||
schema: Schema[];
|
||||
inject?: Inject[];
|
||||
}
|
||||
|
||||
export interface Inject {
|
||||
id: string;
|
||||
type: "script" | "style";
|
||||
url?: string;
|
||||
}
|
||||
|
|
|
@ -0,0 +1,29 @@
|
|||
export declare type EditorConfig = {
|
||||
basic: {
|
||||
enable_render: boolean;
|
||||
defaultRenderMode: "ir" | "wysiwyg" | "sv" | undefined;
|
||||
typeWriterMode: boolean;
|
||||
codeBlockPreview: boolean;
|
||||
enableQuickInsert: boolean;
|
||||
quickInsertUrl: [];
|
||||
disableHTMLBlockPreview: boolean;
|
||||
};
|
||||
extension: {
|
||||
allowImageType: string;
|
||||
};
|
||||
};
|
||||
|
||||
export const defaultEditorConfig: EditorConfig = {
|
||||
basic: {
|
||||
enable_render: true,
|
||||
defaultRenderMode: "ir",
|
||||
typeWriterMode: true,
|
||||
codeBlockPreview: true,
|
||||
enableQuickInsert: false,
|
||||
quickInsertUrl: [],
|
||||
disableHTMLBlockPreview: false,
|
||||
},
|
||||
extension: {
|
||||
allowImageType: "png,jpg,jpeg,bmp,gif,webp,svg",
|
||||
}
|
||||
}
|
|
@ -0,0 +1,10 @@
|
|||
export function getCursor() {
|
||||
return window.getSelection()?.getRangeAt(0);
|
||||
}
|
||||
|
||||
export function setCursor(range: Range | undefined) {
|
||||
if (!range) return;
|
||||
const selection = window.getSelection();
|
||||
selection?.removeAllRanges();
|
||||
selection?.addRange(range);
|
||||
}
|
|
@ -0,0 +1,27 @@
|
|||
export const addScript = (url: string, id: string): HTMLElement => {
|
||||
const headElement = document.getElementsByTagName("head")[0];
|
||||
const scriptElement = document.createElement("script");
|
||||
scriptElement.id = id;
|
||||
scriptElement.src = url;
|
||||
headElement.append(scriptElement);
|
||||
return scriptElement;
|
||||
};
|
||||
|
||||
export const addStyleSheet = (url: string, id: string): HTMLElement => {
|
||||
const headElement = document.getElementsByTagName("head")[0];
|
||||
const linkElement = document.createElement("link");
|
||||
linkElement.id = id;
|
||||
linkElement.rel = "stylesheet";
|
||||
linkElement.href = url;
|
||||
headElement.append(linkElement);
|
||||
return linkElement;
|
||||
};
|
||||
|
||||
export const addStyle = (style: string, id: string): HTMLElement => {
|
||||
const headElement = document.getElementsByTagName("head")[0];
|
||||
const styleElement = document.createElement("style");
|
||||
styleElement.id = id;
|
||||
styleElement.innerHTML = style;
|
||||
headElement.append(styleElement);
|
||||
return styleElement;
|
||||
};
|
|
@ -0,0 +1,23 @@
|
|||
import type { QuickInsert } from "@/type/editor";
|
||||
|
||||
/**
|
||||
* 获取所有快速插入的配置文件
|
||||
* @param quickInsertUrls 配置文件地址
|
||||
* @return 配置文件
|
||||
*/
|
||||
export const fetchAllQuickInsert = async (
|
||||
quickInsertUrls: { url: string }[]
|
||||
): Promise<QuickInsert[]> => {
|
||||
const quickInsertList: QuickInsert[] = [];
|
||||
// Get Default Path
|
||||
for (const qi of quickInsertUrls) {
|
||||
try {
|
||||
const response = await fetch(qi.url);
|
||||
const quickInsertJson: QuickInsert = await response.json();
|
||||
quickInsertList.push(quickInsertJson);
|
||||
} catch (e) {
|
||||
// ignore this
|
||||
}
|
||||
}
|
||||
return quickInsertList;
|
||||
};
|
|
@ -0,0 +1,16 @@
|
|||
import type { Inject } from "@/type/editor";
|
||||
import { addScript, addStyleSheet } from "@/utils/dom-utils";
|
||||
|
||||
export const quickInsertInject = (injectList: Inject[], id: string): void => {
|
||||
injectList.forEach((inject) => {
|
||||
const injectId = `${id}-${inject.id}`;
|
||||
switch (inject.type) {
|
||||
case "script":
|
||||
addScript(inject.url || "", injectId);
|
||||
break;
|
||||
case "style":
|
||||
addStyleSheet(inject.url || "", injectId);
|
||||
break;
|
||||
}
|
||||
});
|
||||
};
|
|
@ -1,12 +1,27 @@
|
|||
import type { Options } from "@/type/editor";
|
||||
import {mdiGrid, mdiImage} from "@/utils/icon";
|
||||
import type { Options, QuickInsert, Schema } from "@/type/editor";
|
||||
import { mdiGrid, mdiImage } from "@/utils/icon";
|
||||
import { t } from "@/utils/i18n-utils";
|
||||
import tips from "@/schema/tips";
|
||||
import git from "@/schema/git";
|
||||
import drive from "@/schema/drive";
|
||||
|
||||
export function getOptions(options: Options): IOptions {
|
||||
const cdn =
|
||||
`${window.location.protocol}//${window.location.host}` +
|
||||
`/plugins/vditor-mde/assets/static`;
|
||||
console.log(`Your CDN IS: ${cdn}`);
|
||||
// Get Toolbar
|
||||
const toolbar = getToolbar(
|
||||
options.showAttachment,
|
||||
options.openModal,
|
||||
getLanguage(options.language)
|
||||
);
|
||||
if (options.enableQuickInsert) {
|
||||
options.quickInsertList.forEach((insert: QuickInsert) => {
|
||||
toolbar.splice(-1, 0, buildQuickInsertToolbar(options.openModal, insert));
|
||||
});
|
||||
}
|
||||
// Build Options
|
||||
return {
|
||||
height: "100%",
|
||||
mode: options.defaultRenderMode,
|
||||
|
@ -22,11 +37,7 @@ export function getOptions(options: Options): IOptions {
|
|||
},
|
||||
after: options.after,
|
||||
input: options.input,
|
||||
toolbar: getToolbar(
|
||||
options.showAttachment,
|
||||
options.openModal,
|
||||
getLanguage(options.language)
|
||||
),
|
||||
toolbar: toolbar,
|
||||
counter: {
|
||||
enable: true,
|
||||
},
|
||||
|
@ -70,9 +81,9 @@ export function getLanguage(lang = "zh-CN"): keyof II18n {
|
|||
|
||||
function getToolbar(
|
||||
showAttachmentCb: () => void,
|
||||
openModal: (name: string) => void,
|
||||
openModal: (schema: Schema) => void,
|
||||
lang: keyof II18n
|
||||
): (string | IMenuItem)[] | undefined {
|
||||
): (string | IMenuItem)[] {
|
||||
return [
|
||||
"emoji",
|
||||
"headings",
|
||||
|
@ -118,17 +129,17 @@ function getToolbar(
|
|||
{
|
||||
name: "insert_tips",
|
||||
icon: t("insert_tips"),
|
||||
click: () => openModal("tips"),
|
||||
click: () => openModal(tips),
|
||||
},
|
||||
{
|
||||
name: "insert_git",
|
||||
icon: t("insert_git"),
|
||||
click: () => openModal("git"),
|
||||
click: () => openModal(git),
|
||||
},
|
||||
{
|
||||
name: "insert_drive",
|
||||
icon: t("insert_drive"),
|
||||
click: () => openModal("drive"),
|
||||
click: () => openModal(drive),
|
||||
},
|
||||
],
|
||||
},
|
||||
|
@ -138,3 +149,24 @@ function getToolbar(
|
|||
},
|
||||
];
|
||||
}
|
||||
|
||||
function buildQuickInsertToolbar(
|
||||
openModal: (schema: Schema) => void,
|
||||
quickInsertList: QuickInsert
|
||||
): IMenuItem {
|
||||
const children: IMenuItem[] = [];
|
||||
quickInsertList.schema.forEach((sch: Schema) => {
|
||||
children.push({
|
||||
icon: (sch.icon || "") + sch.name,
|
||||
name: sch.id,
|
||||
click: () => openModal(sch),
|
||||
});
|
||||
});
|
||||
return {
|
||||
name: quickInsertList.name,
|
||||
tip: quickInsertList.tip,
|
||||
icon: quickInsertList.icon,
|
||||
tipPosition: "n",
|
||||
toolbar: children,
|
||||
};
|
||||
}
|
||||
|
|
File diff suppressed because one or more lines are too long
|
@ -1,5 +1,7 @@
|
|||
# Halo-Vditor语法介绍
|
||||
|
||||
## ⚠️ 这个文档已经过时,您可以[这里](https://github.com/justice2001/vditor-halo-render#语法参考)查看最新文档
|
||||
|
||||
> 当前halo-render还在测试中,halo-render也可以接入任何markdown编辑器中,
|
||||
> 在维护过程中,我会尽量保证语法保持不变
|
||||
|
||||
|
|
|
@ -0,0 +1,128 @@
|
|||
# 快速插入 设计方案
|
||||
|
||||
> Issue https://github.com/justice2001/halo-plugin-vditor/issues/11
|
||||
|
||||
## 目标
|
||||
|
||||
- 应该为用户提供一个可以存储自定义插入按钮配置的设置项
|
||||
- 应当允许用户关闭自定义插入按钮的功能
|
||||
- 为开发者维护一个form表单来管理插入资源
|
||||
|
||||
## 设计
|
||||
|
||||
使用json格式来存储自定义插入按钮的配置,并使用FormKit来生成表单,用户填写提交表单后自动将表单数据替换至预设的文本内容中。
|
||||
|
||||
### 配置格式
|
||||
|
||||
配置文件主要存储了提供者、icon、提示文本等信息,以及插入配置,其基础格式如下:
|
||||
|
||||
```json
|
||||
{
|
||||
"name": "Joe Theme",
|
||||
"tip": "Joe自定义模块",
|
||||
"provider": "halo-theme-joe-3.0",
|
||||
"icon": "...",
|
||||
"schema": [],
|
||||
"inject": []
|
||||
}
|
||||
```
|
||||
|
||||
- `name`:快速插入的标识名称
|
||||
- `tip`:鼠标移入图标显示的文本
|
||||
- `provider`:标识该配置文件的提供者,暂无实际作用
|
||||
- `icon`:您可以在此处直接插入svg图标,用于在工具栏展示
|
||||
- `schema`:具体的插入结构,详细配置见下方Schema格式
|
||||
- `inject`: 注入附加的js或样式表文件,详细配置见下方Inject章节
|
||||
|
||||
### Schema格式
|
||||
|
||||
Schema存储了每个快速插入功能的插入处理规则,如生成表单、插入预设文本等功能。
|
||||
|
||||
```json
|
||||
{
|
||||
"type": "template",
|
||||
"id": "joe-progress",
|
||||
"icon": "",
|
||||
"name": "Joe Progress Bar",
|
||||
"formkit": [
|
||||
{
|
||||
"$formkit": "text",
|
||||
"name": "percentage",
|
||||
"label": "Percentage",
|
||||
"help": "This is the percentage for progress bar."
|
||||
},
|
||||
{
|
||||
"$formkit": "text",
|
||||
"name": "color",
|
||||
"label": "Color",
|
||||
"help": "This is the color for progress bar."
|
||||
}
|
||||
],
|
||||
"template": "<joe-progress percentage=\"$percentage$\" color=\"$color$\"></joe-progress>"
|
||||
}
|
||||
```
|
||||
|
||||
- `type`:该按钮的处理类型,暂时规划下面几种
|
||||
- `template`:根据formkit生成表单,并将值替换至预设模版中
|
||||
- `static`:(暂未实现)插入特定的文本内容
|
||||
- `id`:模块标识,在插件中暂无实际作用
|
||||
- `icon`:图标,会自动插入到name左侧
|
||||
- `name`:在toolbar下拉列表中展示的名称
|
||||
- `formkit`:template模式下需要,遵守FormKit Schema规范,其中value被定义为缺省值
|
||||
- `template`:模版文本,使用`$varible$`识别变量,会被formkit中同name的值替换
|
||||
|
||||
# Inject格式
|
||||
|
||||
Inject是提供给利用HTML自定义标签或自定义样式表方式进行渲染的主题/插件实现实时渲染的配置。
|
||||
|
||||
该数组内的所有资源将在Vditor初始化时进行加载。
|
||||
|
||||
例如`joe`主题利用了`customElements`来实现的。
|
||||
|
||||
```json
|
||||
[
|
||||
{
|
||||
"id": "inject-js",
|
||||
"type": "script",
|
||||
"url": "/plugins/vditor-mde/assets/static/inject-demo.js"
|
||||
},
|
||||
{
|
||||
"id": "inject-css",
|
||||
"type": "style",
|
||||
"url": "/plugins/vditor-mde/assets/static/inject-demo.css"
|
||||
}
|
||||
]
|
||||
```
|
||||
|
||||
- `id`: 注入表示,用于注入的标签加入id选择器,id选择器为: <config-id>-<inject-id>
|
||||
- `type`: 注入类型,当前支持注入脚本和样式文件
|
||||
- `script`: 注入脚本
|
||||
- `style`: 注入样式表
|
||||
- `url`: 脚本和样式文件的URL
|
||||
|
||||
### 配置文件示例
|
||||
|
||||
[quick-insert-demo.json](quick-insert-demo.json)
|
||||
|
||||
### 如何使用
|
||||
|
||||
将json文件放置在插件或主题的静态资源目录下,并将访问该文件的URL填入halo-vditor配置中即可。
|
||||
|
||||
### 表单方案
|
||||
|
||||
与Halo相同的使用FormKit Schema方案,便于Halo开发者使用,但不应支持特别复杂的语法,应当支持一些较为基础的语法为优。
|
||||
|
||||
Halo使用的是yml格式语法,而为了方便配置文件编辑,插件暂时指定json为配置格式。
|
||||
|
||||
### 存储方案
|
||||
|
||||
对配置存储方案有下列四种:
|
||||
|
||||
- 使用依赖于Halo原生的附件管理(未验证是否可行)
|
||||
- 提供配置输入框,让用户粘贴配置文件
|
||||
- 🌟 提供URL输入框,让用户自行根据主题或插件配置文档输入URL
|
||||
- 🌟 自动识别某个特定目录是否存储配置文件(适用主题端)
|
||||
|
||||
## 多语言适配
|
||||
|
||||
WIP(v1.5.x暂时不做规划)
|
|
@ -0,0 +1,51 @@
|
|||
{
|
||||
"name": "Joe Theme",
|
||||
"tip": "Joe自定义模块",
|
||||
"provider": "halo-theme-joe-3.0",
|
||||
"icon": "<svg xmlns=\"http://www.w3.org/2000/svg\" height=\"24\" viewBox=\"0 -960 960 960\" width=\"24\"><path d=\"M480-80q-26 0-47-12.5T400-126q-33 0-56.5-23.5T320-206v-142q-59-39-94.5-103T190-590q0-121 84.5-205.5T480-880q121 0 205.5 84.5T770-590q0 77-35.5 140T640-348v142q0 33-23.5 56.5T560-126q-12 21-33 33.5T480-80Zm-80-126h160v-36H400v36Zm0-76h160v-38H400v38Zm-8-118h58v-108l-88-88 42-42 76 76 76-76 42 42-88 88v108h58q54-26 88-76.5T690-590q0-88-61-149t-149-61q-88 0-149 61t-61 149q0 63 34 113.5t88 76.5Zm88-162Zm0-38Z\"/></svg>",
|
||||
"schema": [
|
||||
{
|
||||
"type": "template",
|
||||
"id": "joe-progress",
|
||||
"icon": "",
|
||||
"name": "进度条",
|
||||
"formKit": [
|
||||
{
|
||||
"$formkit": "number",
|
||||
"min": 0,
|
||||
"max": 100,
|
||||
"step": 1,
|
||||
"name": "percentage",
|
||||
"label": "进度",
|
||||
"help": "This is the percentage for progress bar.",
|
||||
"value": 50
|
||||
},
|
||||
{
|
||||
"$formkit": "color",
|
||||
"name": "color",
|
||||
"label": "颜色",
|
||||
"help": "This is the color for progress bar.",
|
||||
"value": "#fb6c28"
|
||||
}
|
||||
],
|
||||
"template": "<joe-progress percentage=\"$percentage$%\" color=\"$color$\"></joe-progress>"
|
||||
}
|
||||
],
|
||||
"inject": [
|
||||
{
|
||||
"id": "inject-js",
|
||||
"type": "script",
|
||||
"url": "/plugins/vditor-mde/assets/static/inject-demo.js"
|
||||
},
|
||||
{
|
||||
"id": "inject-css",
|
||||
"type": "style",
|
||||
"url": "/plugins/vditor-mde/assets/static/inject-demo.css"
|
||||
},
|
||||
{
|
||||
"id": "inject-font",
|
||||
"type": "style",
|
||||
"url": "/themes/theme-Joe3/assets/lib/font-awesome/css/font-awesome.min.css"
|
||||
}
|
||||
]
|
||||
}
|
|
@ -0,0 +1,11 @@
|
|||
cd src/main/resources/static
|
||||
if exist dist rmdir /s /q dist
|
||||
echo %cd%
|
||||
bitsadmin /transfer vditorDownloadJob https://git.mczhengyi.top/zhengyi/-/packages/npm/@zhengyi%%2Fvditor/3.9.10/files/262 %cd%\vditor.tgz
|
||||
7z x vditor.tgz
|
||||
7z x vditor.tar
|
||||
move /y %cd%\package\dist %cd%
|
||||
rmdir /s /q package
|
||||
del vditor.tar
|
||||
del vditor.tgz
|
||||
pause
|
|
@ -4,7 +4,7 @@ cd src/main/resources/static
|
|||
rm -rf dist
|
||||
pwd
|
||||
curl -o vditor.tgz \
|
||||
https://git.mczhengyi.top/zhengyi/-/packages/npm/@zhengyi%2Fvditor/3.9.9/files/258
|
||||
https://git.mczhengyi.top/zhengyi/-/packages/npm/@zhengyi%2Fvditor/3.9.10/files/262
|
||||
tar -xzvf vditor.tgz
|
||||
mv package/dist .
|
||||
rm -rf package
|
||||
|
|
|
@ -1 +1 @@
|
|||
version=1.4.0-SNAPSHOT
|
||||
version=1.5.0-SNAPSHOT
|
||||
|
|
|
@ -14,8 +14,9 @@ public class ScriptUtils {
|
|||
|
||||
public static String basicScript(RenderConfig renderConfig) {
|
||||
return """
|
||||
<link rel="stylesheet" type="text/css" href="/plugins/vditor-mde/assets/static/dist/index.css" id="vditor-style" />
|
||||
<script src="/plugins/vditor-mde/assets/static/dist/method.min.js"></script>
|
||||
<script src="/plugins/vditor-mde/assets/static/render.js" id="render-script"
|
||||
<script src="/plugins/vditor-mde/assets/static/render.js" id="vditor-render"
|
||||
data-dark="%s" data-mediaRender="%s"></script>
|
||||
""".formatted(renderConfig.getDarkMode(), renderConfig.getMediaRender());
|
||||
}
|
||||
|
|
|
@ -29,6 +29,36 @@ spec:
|
|||
label: 编辑器代码块渲染
|
||||
help: 关闭后代码块(包括图表)在所见即所得和即时渲染模式下将不会被渲染
|
||||
value: true
|
||||
- $formkit: checkbox
|
||||
id: enableQuickInsert
|
||||
name: enableQuickInsert
|
||||
label: 启用快速插入功能
|
||||
help: 开启此选项后,将会加入插件或主题提供的快速插入按钮
|
||||
value: false
|
||||
- $formkit: repeater
|
||||
if: "$enableQuickInsert"
|
||||
name: quickInsertUrl
|
||||
label: 快速插入链接
|
||||
help: 在下面的选项框中填入主题或插件给出的配置地址,即可使用
|
||||
value: [ ]
|
||||
children:
|
||||
- $formkit: text
|
||||
name: url
|
||||
label: URL
|
||||
value: ""
|
||||
- $formkit: checkbox
|
||||
name: disableHTMLBlockPreview
|
||||
label: 禁用HTML代码块隐藏
|
||||
help: 开启此选项后,HTML代码块将会一直显示
|
||||
value: false
|
||||
- group: extension
|
||||
label: 文件格式
|
||||
formSchema:
|
||||
- $formkit: text
|
||||
name: allowImageType
|
||||
label: 允许的图片格式
|
||||
help: 自定义允许上传的图片格式
|
||||
value: "png,jpg,jpeg,bmp,gif,webp,svg"
|
||||
- group: render
|
||||
label: 渲染
|
||||
formSchema:
|
||||
|
|
File diff suppressed because one or more lines are too long
|
@ -0,0 +1,44 @@
|
|||
customElements.define(
|
||||
"joe-cloud",
|
||||
class JoeCloud extends HTMLElement {
|
||||
constructor() {
|
||||
super();
|
||||
this.options = {
|
||||
type: this.getAttribute("type") || "default",
|
||||
title: this.getAttribute("title") || "默认标题",
|
||||
url: this.getAttribute("url"),
|
||||
password: this.getAttribute("password"),
|
||||
};
|
||||
const type = {
|
||||
default: "默认网盘",
|
||||
360: "360网盘",
|
||||
bd: "百度网盘",
|
||||
ty: "天翼网盘",
|
||||
ct: "城通网盘",
|
||||
wy: "微云网盘",
|
||||
github: "Github仓库",
|
||||
gitee: "Gitee仓库",
|
||||
lz: "蓝奏云网盘",
|
||||
ad: "阿里云盘",
|
||||
};
|
||||
this.innerHTML = `
|
||||
<span class="joe_cloud">
|
||||
<div class="joe_cloud__logo _${this.options.type}"></div>
|
||||
<div class="joe_cloud__describe">
|
||||
<div class="joe_cloud__describe-title">${this.options.title}</div>
|
||||
<div class="joe_cloud__describe-type">来源:${
|
||||
type[this.options.type] || "默认网盘"
|
||||
}${
|
||||
this.options.password ? " | 提取码:" + this.options.password : ""
|
||||
}</div>
|
||||
</div>
|
||||
<a class="joe_cloud__btn" href="${
|
||||
this.options.url
|
||||
}" target="_blank" rel="noopener noreferrer nofollow">
|
||||
<i class="fa fa-download"></i>
|
||||
</a>
|
||||
</span>
|
||||
`;
|
||||
}
|
||||
}
|
||||
);
|
|
@ -0,0 +1,70 @@
|
|||
{
|
||||
"name": "Joe Theme",
|
||||
"tip": "Joe自定义模块",
|
||||
"provider": "halo-theme-joe-3.0",
|
||||
"icon": "<svg xmlns=\"http://www.w3.org/2000/svg\" height=\"24\" viewBox=\"0 -960 960 960\" width=\"24\"><path d=\"M480-80q-26 0-47-12.5T400-126q-33 0-56.5-23.5T320-206v-142q-59-39-94.5-103T190-590q0-121 84.5-205.5T480-880q121 0 205.5 84.5T770-590q0 77-35.5 140T640-348v142q0 33-23.5 56.5T560-126q-12 21-33 33.5T480-80Zm-80-126h160v-36H400v36Zm0-76h160v-38H400v38Zm-8-118h58v-108l-88-88 42-42 76 76 76-76 42 42-88 88v108h58q54-26 88-76.5T690-590q0-88-61-149t-149-61q-88 0-149 61t-61 149q0 63 34 113.5t88 76.5Zm88-162Zm0-38Z\"/></svg>",
|
||||
"schema": [
|
||||
{
|
||||
"type": "template",
|
||||
"id": "joe-cloud",
|
||||
"icon": "",
|
||||
"name": "网盘资源",
|
||||
"formKit": [
|
||||
{
|
||||
"$formkit": "select",
|
||||
"name": "cloud-type",
|
||||
"label": "云盘类型",
|
||||
"help": "Choose the type of cloud service.",
|
||||
"value": "default",
|
||||
"options": [
|
||||
{"label": "默认网盘", "value": "default"},
|
||||
{"label": "百度网盘", "value": "bd"},
|
||||
{"label": "阿里网盘", "value": "ad"},
|
||||
{"label": "蓝奏云网盘", "value": "lz"},
|
||||
{"label": "微云网盘", "value": "wy"},
|
||||
{"label": "Github仓库", "value": "github"},
|
||||
{"label": "Gitee仓库", "value": "gitee"}
|
||||
]
|
||||
},
|
||||
{
|
||||
"$formkit": "text",
|
||||
"name": "cloud-title",
|
||||
"label": "网盘名称",
|
||||
"help": "留空则显示默认标题"
|
||||
},
|
||||
{
|
||||
"$formkit": "url",
|
||||
"name": "cloud-url",
|
||||
"label": "跳转链接",
|
||||
"help": "网盘链接地址",
|
||||
"value": ""
|
||||
},
|
||||
{
|
||||
"$formkit": "text",
|
||||
"name": "cloud-password",
|
||||
"label": "密码",
|
||||
"help": "网盘的访问密码,无则留空",
|
||||
"value": ""
|
||||
}
|
||||
],
|
||||
"template": "<joe-cloud type=\"$cloud-type$\" url=\"$cloud-url$\" password=\"$cloud-password$\" title=\"$cloud-title$\"></joe-cloud>"
|
||||
}
|
||||
],
|
||||
"inject": [
|
||||
{
|
||||
"id": "inject-js",
|
||||
"type": "script",
|
||||
"url": "/plugins/vditor-mde/assets/static/inject-demo.js"
|
||||
},
|
||||
{
|
||||
"id": "inject-css",
|
||||
"type": "style",
|
||||
"url": "/plugins/vditor-mde/assets/static/inject-demo.css"
|
||||
},
|
||||
{
|
||||
"id": "inject-font",
|
||||
"type": "style",
|
||||
"url": "/themes/theme-Joe3/assets/lib/font-awesome/css/font-awesome.min.css"
|
||||
}
|
||||
]
|
||||
}
|
|
@ -6,24 +6,22 @@ window.addEventListener("load", () => {
|
|||
let dark = initDarkMode()
|
||||
setTheme(dark?"dark":"light")
|
||||
|
||||
// Math Render
|
||||
document.querySelectorAll(".language-math").forEach(el => {
|
||||
Vditor.mathRender(coverThis(el), {
|
||||
cdn: CDN
|
||||
})
|
||||
})
|
||||
const root = document.getElementById("vditor-render").parentElement
|
||||
root.classList.add("vditor-reset")
|
||||
// Render
|
||||
render("language-mindmap", Vditor.mindmapRender, dark)
|
||||
render("language-mermaid", Vditor.mermaidRender, dark)
|
||||
render("language-echarts", Vditor.chartRender, dark)
|
||||
render("language-abc", Vditor.abcRender)
|
||||
render("language-graphviz", Vditor.graphvizRender)
|
||||
render("language-flowchart", Vditor.flowchartRender)
|
||||
render("language-halo", Vditor.haloRender)
|
||||
const renderTheme = dark?"dark":"classic"
|
||||
Vditor.mathRender(root, {cdn: CDN})
|
||||
Vditor.mindmapRender(root, CDN, renderTheme)
|
||||
Vditor.mermaidRender(root, CDN, renderTheme)
|
||||
Vditor.chartRender(root, CDN, renderTheme)
|
||||
Vditor.abcRender(root, CDN)
|
||||
Vditor.graphvizRender(root, CDN)
|
||||
Vditor.flowchartRender(root, CDN)
|
||||
Vditor.haloRender(root, CDN)
|
||||
// Render Media
|
||||
let mediaRenderOption = document.getElementById("render-script").dataset.mediarender
|
||||
let mediaRenderOption = document.getElementById("vditor-render").dataset.mediarender
|
||||
if (mediaRenderOption==="true") {
|
||||
let article = document.getElementById("render-script").parentElement;
|
||||
let article = document.getElementById("vditor-render").parentElement;
|
||||
Vditor.mediaRender(article)
|
||||
}
|
||||
})
|
||||
|
@ -34,7 +32,7 @@ window.addEventListener("load", () => {
|
|||
* @returns {boolean} 初始暗黑模式状态
|
||||
*/
|
||||
function initDarkMode() {
|
||||
let darkModeChange = document.getElementById("render-script").dataset.dark
|
||||
let darkModeChange = document.getElementById("vditor-render").dataset.dark
|
||||
let dark = false
|
||||
// 检测暗黑模式策略
|
||||
switch (darkModeChange) {
|
||||
|
@ -72,37 +70,4 @@ function initSystemDarkMode() {
|
|||
*/
|
||||
function setTheme(theme) {
|
||||
Vditor.setContentTheme(theme, THEME_PREFIX)
|
||||
}
|
||||
|
||||
/**
|
||||
* 通用渲染
|
||||
* @param selector 选择器
|
||||
* @param callback 渲染方法
|
||||
* @param dark 暗色模式,null为不配置
|
||||
*/
|
||||
function render(selector, callback, dark=null) {
|
||||
let mindmap = document.getElementsByClassName(selector)
|
||||
for (let i = 0; i < mindmap.length;i++) {
|
||||
const el = coverThis(mindmap[i])
|
||||
if (dark) {
|
||||
callback(el, CDN, dark?"dark":"classic")
|
||||
} else {
|
||||
callback(el, CDN)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 符合Vditor渲染器要求
|
||||
* 需要在外套一层div
|
||||
* @param el 元素
|
||||
* @returns {*}
|
||||
*/
|
||||
function coverThis(el) {
|
||||
let copy = el.cloneNode(true)
|
||||
el.innerHTML = ""
|
||||
el.className = "vditor-reset"
|
||||
el.dataset.code = ""
|
||||
el.append(copy)
|
||||
return el
|
||||
}
|
Loading…
Reference in New Issue