Compare commits
53 Commits
Author | SHA1 | Date |
---|---|---|
zhengyi | 2600445d15 | |
zhengyi59 | 868aec124c | |
zhengyi | 1b482b5d9b | |
zhengyi | 8cab82fa52 | |
zhengyi | b8000377ac | |
justice2001 | ffaf19cf76 | |
Ryan Wang | fe1ba30230 | |
Ryan Wang | eb58dc8028 | |
zhengyi | 7757091b80 | |
zhengyi | d830c03d8b | |
zhengyi | 8c6cfc73d4 | |
zhengyi | c60381920c | |
zhengyi | f927bfb3d5 | |
zhengyi | 6bcf8f89f0 | |
zhengyi | f08ec8074f | |
zhengyi | bab4075109 | |
zhengyi | 7581ccb6fc | |
zhengyi | 6fd0974199 | |
zhengyi | dfe7a274e9 | |
zhengyi | 0287ce2b33 | |
zhengyi | 8ae973f2c4 | |
zhengyi | c76808258a | |
zhengyi59 | 30ce08e2c8 | |
zhengyi | f8611f1a0c | |
zhengyi | 14ca6bf173 | |
zhengyi | 56143952c4 | |
zhengyi | 6d1b9eab6c | |
zhengyi | 464ca2769c | |
zhengyi | 285ae2c28a | |
zhengyi | 5760e1a9d4 | |
zhengyi | 4eea179a24 | |
zhengyi | 16442b7068 | |
zhengyi | 7cbdbd70ac | |
zhengyi | aa28599a75 | |
zhengyi | b416739947 | |
justice2001 | f9007864b6 | |
zhengyi | 968b7d0a7f | |
zhengyi | 527ce8ceab | |
Ryan Wang | 7eb9546241 | |
zhengyi59 | ed5f59cb5c | |
zhengyi | d48f8aa144 | |
zhengyi | b074329dc5 | |
zhengyi | 8cf103932e | |
zhengyi | d9f364ad49 | |
zhengyi | abf2a73d70 | |
zhengyi | c41bfea624 | |
zhengyi | 23cdb944a6 | |
zhengyi | 8fc93ab574 | |
zhengyi | dcc0758313 | |
zhengyi | 00eb633cf2 | |
zhengyi | cb6b6cc224 | |
zhengyi | 4724bc04e9 | |
zhengyi | 49ab197ffb |
|
@ -64,7 +64,7 @@ jobs:
|
|||
name: plugin-starter
|
||||
path: |
|
||||
build/libs/*.jar
|
||||
retention-days: 1
|
||||
retention-days: 90
|
||||
|
||||
github-release:
|
||||
runs-on: debian-12
|
||||
|
|
|
@ -0,0 +1,126 @@
|
|||
# 更新记录
|
||||
|
||||
### v1.7.0
|
||||
|
||||
- ⬆️ 调整 Halo 最低兼容版本为 `2.14.0`
|
||||
- ✨ 添加`使用第一个h1作为标题`的功能 #33
|
||||
- 🐛 修复代码块颜色异常的问题 #38
|
||||
|
||||
### v1.6.2
|
||||
|
||||
- ⬆️ 升级Vditor版本至3.10.2
|
||||
- ✨ 添加页面加载CDN设置,添加自定义CDN支持 `#30`
|
||||
|
||||
#### 关于CDN配置
|
||||
|
||||
由于Vditor本体加载CDN的方式,无法支持类似`cdnjs`的url格式。当前内置4种CDN: `内置`,`jsDelivr`, `unpkg`, `zstatic`
|
||||
|
||||
**如何确认cdn是否可用:**
|
||||
|
||||
您可以访问`<你的cdn>/dist/index.min.js`,能加载出来则说明可用,同时注意cdn是否包含该版本资源,版本升级信息会在更新日志中体现。
|
||||
|
||||
### v1.6.1
|
||||
|
||||
- 🐛 修复单页文章加载Vditor资源时 version 未成功替换问题 `#27`
|
||||
- 🐛 添加遗漏的 plantuml 渲染器 `#28`
|
||||
- 🐛 修复多媒体链接无法渲染的问题
|
||||
- 🐛 修复多媒体渲染的大小问题
|
||||
|
||||
### v1.6.0
|
||||
|
||||
- ⬆️ 修改Vditor为官方版本(Vditor 3.9.9)
|
||||
- ✨ 添加 Vditor Debugger 设置
|
||||
- 🐛 修复行内公式渲染的问题 #22
|
||||
- 🐛 修复操作栏修改属性不生效的问题 #20 \[vditor]
|
||||
- 🐛 修复在 PJAX 主题下渲染器无法正常处理的问题 #24
|
||||
- 🐛 修复 render 样式与其他样式冲突的问题
|
||||
- 🐛 修复`插入图片`在 Safari 中不可用的问题
|
||||
|
||||
### v1.5.1
|
||||
|
||||
- ✨ 添加图集的快捷插入按钮
|
||||
- 🐛 修复脑图、图标、echart暗色模式渲染错误的问题
|
||||
- 🐛 解决更新插件前台资源缓存的问题
|
||||
- 🐛 解决暗色模式渲染异常的问题
|
||||
- 🐛 解决主题代码块等样式覆盖的问题
|
||||
|
||||
### v1.5.0
|
||||
|
||||
- ✨ 使用新的快速插入表单,界面更美观
|
||||
- ✨ 支持渲染其他插件/主题的自定义组件(需要开发者支持)
|
||||
- ✨ 支持加入快速插入链接(需要开发者支持)
|
||||
- ✨ 允许用户禁用HTML代码块隐藏的特性
|
||||
- ✨ 支持用户自定义上传图片的格式
|
||||
- ✨ vditor-halo-render添加组图组件(gallery)
|
||||
- 🐛 修复图片拖拽插入错位的问题
|
||||
- 🐛 修复echarts,mindmap无法渲染的问题
|
||||
- 🐛 修复所见即所得模式下超链接颜色丢失的问题
|
||||
- 🚀 优化前端渲染逻辑,去除无用的dom查询
|
||||
|
||||
### v1.4.0
|
||||
|
||||
- ✨ 支持编辑器加载状态展示
|
||||
- ✨ 支持拖拽上传图片
|
||||
- ✨ 添加快速插入自定义块的按钮
|
||||
- ✨ 添加仅在Markdown文章注入解析器的选项
|
||||
- ⬆️ 更新halo-vditor至3.9.9版本(对应vditor 3.9.8)
|
||||
- ⬆️ 更新vditor-halo-render到1.1.0版本
|
||||
- ✨ 重新设计tip、git模块
|
||||
- ✨ 添加资源下载模块
|
||||
- ✨ 为git添加fork、topic信息
|
||||
- ✨ 为git添加缓存
|
||||
- ✨ 添加对Gitee的支持
|
||||
- 🐛 修复invalid type界面边框与其他模块不对齐的问题
|
||||
|
||||
### v1.3.2
|
||||
|
||||
- 🐛 编辑器编写代码块出现闪屏问题
|
||||
|
||||
### v1.3.1
|
||||
|
||||
- 🐛 修复Vditor编辑器切换到其他编辑器导致的样式冲突问题
|
||||
- 🐛 修复编辑器在Halo中出现页面滚动条的问题
|
||||
- 🐛 修复前台数学公式无法渲染的问题
|
||||
|
||||
### v1.3.0
|
||||
|
||||
- ✨ 支持更多tips渲染
|
||||
- ✨ 支持github仓库信息渲染
|
||||
- ✨ 升级vditor为分支halo-vditor版本 https://github.com/justice2001/halo-vditor
|
||||
- ⬆️ 升级halo-vditor到3.9.8版本(对应vditor3.9.7)
|
||||
- 🐛 修复页面偶尔出现加载shi问题,使用本地资源
|
||||
|
||||
### v1.2.0
|
||||
|
||||
- 🚸 将设置拆分为编辑器和渲染两个部分
|
||||
- ✨ 为编辑器添加多国语言支持(简中、繁中、英语)
|
||||
- ✨ 添加启用编辑器代码块的选项
|
||||
- ✨ 添加跟随Joe 3.0主题切换暗色模式的选项
|
||||
- 🐛 修复单页未对内容进行渲染的问题
|
||||
- 🐛 修复所见即所得模式下部分内容被小工具栏遮挡的问题
|
||||
|
||||
### v1.1.0
|
||||
|
||||
- ⬆️ 更新 Halo 最低兼容版本至 2.8.0
|
||||
- ✨ 添加默认编辑模式设置
|
||||
- ✨ 添加打字机模式设置
|
||||
- ✨ 添加插入图片功能(快捷键:⇧⌘P)
|
||||
- ✨ 添加自动暗色模式的支持
|
||||
- 🐛 修复了编辑器中有序列表左侧序号消失的问题
|
||||
- 🐛 修复全屏编辑器左侧被Halo导航栏遮挡的问题
|
||||
- 🧪 实验性特性: 支持多媒体渲染
|
||||
|
||||
### v1.0.1
|
||||
|
||||
- ⚡ 修改为仅在文章中加入vditor render,优化部分页面加载速度
|
||||
- ⚡ 修改vditor库从本地加载,优化页面加载速度
|
||||
- 🐛 修复由于id重复可能存在的冲突问题
|
||||
- ⚰️ 删除了部分无用代码
|
||||
|
||||
### v1.0.0
|
||||
|
||||
- ✨ 集成强大的Markdown编辑器Vditor进Halo中
|
||||
- ✨ 提供所见即所得(wysiwyg)、即时渲染(ir)、分屏渲染(sv)三种渲染模式
|
||||
- ✨ 支持大纲、数学公式、脑图、图表、流程图、甘特图、时序图、五线谱、多媒体、语音阅读、标题锚点、代码高亮及复制、graphviz 渲染、plantumlUML图
|
||||
- ✨ 支持字符计数
|
||||
- ✨ 支持在前台注入渲染脚本(需在设置中开启)
|
17
README.md
17
README.md
|
@ -1,14 +1,9 @@
|
|||
# halo-plugin-vditor
|
||||
|
||||
---
|
||||
|
||||
## NEXT VERSION
|
||||
|
||||
- ✨ 使用新的快速插入表单,界面更美观
|
||||
- ✨ 支持渲染其他插件/主题的自定义组件(需要开发者支持)
|
||||
- ✨ 允许用户禁用HTML代码块隐藏的特性
|
||||
|
||||
---
|
||||
![](https://img.shields.io/badge/状态-稳定-red.svg)
|
||||
![](https://img.shields.io/badge/启动时间-2023/11/23-green.svg)
|
||||
![](https://img.shields.io/badge/优先级-HIGH-blue.svg)
|
||||
![GitHub Release](https://img.shields.io/github/v/release/justice2001/halo-plugin-vditor?color=yellow&label=版本)
|
||||
|
||||
本插件将Vditor整合进Halo,支持所见即所得编辑模式。 Support English (*Only Editor)!
|
||||
|
||||
|
@ -68,7 +63,9 @@
|
|||
|
||||
## 🙏 鸣谢
|
||||
|
||||
>特别感谢[Vditor](https://github.com/Vanessa219/vditor),本插件大部分内容均为此编辑器提供
|
||||
> 特别感谢来自 [Halo](https://github.com/halo-dev) 的投喂
|
||||
|
||||
> 特别感谢[Vditor](https://github.com/Vanessa219/vditor),本插件大部分内容均为此编辑器提供
|
||||
|
||||
* [Vditor](https://github.com/Vanessa219/vditor) 一款浏览器端的 Markdown 编辑器,支持所见即所得、即时渲染(类似 Typora)和分屏预览模式。
|
||||
* [Lute](https://github.com/88250/lute):🎼 一款结构化的 Markdown 引擎,支持 Go 和 JavaScript
|
||||
|
|
|
@ -41,13 +41,9 @@ build {
|
|||
}
|
||||
|
||||
halo {
|
||||
version = '2.11.3'
|
||||
version = '2.14'
|
||||
superAdminUsername = 'admin'
|
||||
superAdminPassword = 'admin'
|
||||
externalUrl = 'http://localhost:8090'
|
||||
docker {
|
||||
// windows 默认为 npipe:////./pipe/docker_engine
|
||||
url = 'npipe:////./pipe/docker_engine'
|
||||
apiVersion = '1.42'
|
||||
}
|
||||
debug = true
|
||||
}
|
|
@ -15,11 +15,12 @@
|
|||
"@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",
|
||||
"vditor": "3.10.2",
|
||||
"canvas-confetti": "^1.6.0",
|
||||
"vue": "^3.3.4"
|
||||
},
|
||||
"devDependencies": {
|
||||
"@halo-dev/ui-plugin-bundler-kit": "^2.12.0",
|
||||
"@iconify/json": "^2.2.76",
|
||||
"@rushstack/eslint-patch": "^1.3.1",
|
||||
"@types/canvas-confetti": "^1.6.0",
|
||||
|
@ -42,5 +43,6 @@
|
|||
"vite": "^3.2.7",
|
||||
"vitest": "^0.24.5",
|
||||
"vue-tsc": "^1.6.5"
|
||||
}
|
||||
},
|
||||
"packageManager": "pnpm@8.0.0"
|
||||
}
|
||||
|
|
|
@ -17,17 +17,20 @@ dependencies:
|
|||
'@halo-dev/console-shared':
|
||||
specifier: ^2.11.0
|
||||
version: 2.11.0(vue-router@4.2.5)(vue@3.3.12)
|
||||
'@zhengyi/vditor':
|
||||
specifier: 3.9.10
|
||||
version: 3.9.10
|
||||
canvas-confetti:
|
||||
specifier: ^1.6.0
|
||||
version: 1.9.2
|
||||
vditor:
|
||||
specifier: 3.10.2
|
||||
version: 3.10.2
|
||||
vue:
|
||||
specifier: ^3.3.4
|
||||
version: 3.3.12(typescript@4.7.4)
|
||||
|
||||
devDependencies:
|
||||
'@halo-dev/ui-plugin-bundler-kit':
|
||||
specifier: ^2.12.0
|
||||
version: 2.12.0(vite@3.2.7)
|
||||
'@iconify/json':
|
||||
specifier: ^2.2.76
|
||||
version: 2.2.159
|
||||
|
@ -595,6 +598,15 @@ packages:
|
|||
vue-router: 4.2.5(vue@3.3.12)
|
||||
dev: false
|
||||
|
||||
/@halo-dev/ui-plugin-bundler-kit@2.12.0(vite@3.2.7):
|
||||
resolution: {integrity: sha512-3558qzH5RN9pB2j0ZonuIxX3cw8lh870cWpPPHjkDxTIjKt+aO5tjKhcqKlFL853jdx9nHIIS+nMDCeqjejpxw==}
|
||||
engines: {node: ^18.0.0 || >=20.0.0}
|
||||
peerDependencies:
|
||||
vite: ^4.0.0 || ^5.0.0
|
||||
dependencies:
|
||||
vite: 3.2.7(@types/node@16.18.68)(sass@1.69.5)
|
||||
dev: true
|
||||
|
||||
/@humanwhocodes/config-array@0.11.13:
|
||||
resolution: {integrity: sha512-JSBDMiDKSzQVngfRjOdFXgFfklaXI4K9nLF49Auh21lmBWRLIK3+xTErTWD4KU54pb6coM6ESE7Awz/FNU3zgQ==}
|
||||
engines: {node: '>=10.10.0'}
|
||||
|
@ -1117,17 +1129,6 @@ packages:
|
|||
'@types/node': 16.18.68
|
||||
dev: true
|
||||
|
||||
/@zhengyi/halo-render@1.1.0:
|
||||
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.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
|
||||
dev: false
|
||||
|
||||
/abab@2.0.6:
|
||||
resolution: {integrity: sha512-j2afSsaIENvHZN2B8GOpF566vZ5WVk5opAiMTvWgaQT8DkbOqsTfvNAvHoRGU2zzP8cPoqys+xHTRDWW8L+/BA==}
|
||||
deprecated: Use your platform's native atob() and btoa() methods instead
|
||||
|
@ -3717,6 +3718,12 @@ packages:
|
|||
spdx-expression-parse: 3.0.1
|
||||
dev: true
|
||||
|
||||
/vditor@3.10.2:
|
||||
resolution: {integrity: sha512-NtnsqKoU7cFUb7HPLct+fSjRnuhSlfSp0bpCDm0GcH2emngoZb6m1cEyg8yhu1WkHt8ugD9i1OMrA1tnMEW6fA==}
|
||||
dependencies:
|
||||
diff-match-patch: 1.0.5
|
||||
dev: false
|
||||
|
||||
/vite@3.2.7(@types/node@16.18.68)(sass@1.69.5):
|
||||
resolution: {integrity: sha512-29pdXjk49xAP0QBr0xXqu2s5jiQIXNvE/xwd0vUizYT2Hzqe4BksNNoWllFVXJf4eLZ+UlVQmXfB4lWrc+t18g==}
|
||||
engines: {node: ^14.18.0 || >=16.0.0}
|
||||
|
|
|
@ -0,0 +1,21 @@
|
|||
<?xml version="1.0" encoding="utf-8"?><!-- Uploaded to: SVG Repo, www.svgrepo.com, Generator: SVG Repo Mixer Tools -->
|
||||
<svg
|
||||
width="24"
|
||||
height="24"
|
||||
viewBox="0 0 24 24"
|
||||
fill="none"
|
||||
style="fill: #ffffff;"
|
||||
xmlns="http://www.w3.org/2000/svg"
|
||||
>
|
||||
<path
|
||||
d="M10 11C10 10.4477 10.4477 10 11 10H13C13.5523 10 14 10.4477 14 11C14 11.5523 13.5523 12 13 12H11C10.4477 12 10 11.5523 10 11Z"
|
||||
/>
|
||||
<path
|
||||
d="M11 14C10.4477 14 10 14.4477 10 15C10 15.5523 10.4477 16 11 16H13C13.5523 16 14 15.5523 14 15C14 14.4477 13.5523 14 13 14H11Z"
|
||||
/>
|
||||
<path
|
||||
fill-rule="evenodd"
|
||||
clip-rule="evenodd"
|
||||
d="M9.09447 4.74918C8.41606 4.03243 8 3.0648 8 2H10C10 3.10457 10.8954 4 12 4C13.1046 4 14 3.10457 14 2H16C16 3.0648 15.5839 4.03243 14.9055 4.74918C16.1782 5.45491 17.1673 6.6099 17.6586 8H19C19.5523 8 20 8.44772 20 9C20 9.55229 19.5523 10 19 10H18V12H19C19.5523 12 20 12.4477 20 13C20 13.5523 19.5523 14 19 14H18V16H19C19.5523 16 20 16.4477 20 17C20 17.5523 19.5523 18 19 18H17.6586C16.8349 20.3304 14.6124 22 12 22C9.38756 22 7.16508 20.3304 6.34141 18H5C4.44772 18 4 17.5523 4 17C4 16.4477 4.44772 16 5 16H6V14H5C4.44772 14 4 13.5523 4 13C4 12.4477 4.44772 12 5 12H6V10H5C4.44772 10 4 9.55229 4 9C4 8.44772 4.44772 8 5 8H6.34141C6.83274 6.6099 7.82181 5.45491 9.09447 4.74918ZM8 16V10C8 7.79086 9.79086 6 12 6C14.2091 6 16 7.79086 16 10V16C16 18.2091 14.2091 20 12 20C9.79086 20 8 18.2091 8 16Z"
|
||||
/>
|
||||
</svg>
|
After Width: | Height: | Size: 1.4 KiB |
|
@ -24,6 +24,13 @@ const lang: I18nLang = {
|
|||
link: "Link",
|
||||
password: "Password",
|
||||
quick_insert: "Quick Insert",
|
||||
insert_gallery: "Insert Gallery",
|
||||
grid: "Grid",
|
||||
linear: "Linear",
|
||||
image_group: "Image Group",
|
||||
image: "Image",
|
||||
image_alt: "Alt",
|
||||
image_alt_ph: "Use File Name If You Dont Place This",
|
||||
};
|
||||
|
||||
export default lang;
|
||||
|
|
|
@ -24,6 +24,13 @@ const lang: I18nLang = {
|
|||
link: "链接",
|
||||
password: "密码",
|
||||
quick_insert: "快速插入",
|
||||
insert_gallery: "插入图集",
|
||||
grid: "九宫格",
|
||||
linear: "线性",
|
||||
image_group: "图片组",
|
||||
image: "图片",
|
||||
image_alt: "图片介绍",
|
||||
image_alt_ph: "不填写此项则默认使用图像文件名",
|
||||
};
|
||||
|
||||
export default lang;
|
||||
|
|
|
@ -24,6 +24,13 @@ const lang: I18nLang = {
|
|||
link: "鏈接",
|
||||
password: "密碼",
|
||||
quick_insert: "快速插入",
|
||||
insert_gallery: "插入圖集",
|
||||
grid: "九宮格",
|
||||
linear: "線性",
|
||||
image_group: "圖片組",
|
||||
image: "圖片",
|
||||
image_alt: "圖片介紹",
|
||||
image_alt_ph: "不填寫此項則默認使用影像檔名",
|
||||
};
|
||||
|
||||
export default lang;
|
||||
|
|
|
@ -1,7 +1,7 @@
|
|||
import {definePlugin, type EditorProvider} from "@halo-dev/console-shared";
|
||||
import Vditor from "./views/Vditor.vue";
|
||||
import logo from "./assets/vditor.png"
|
||||
import {markRaw} from "vue";
|
||||
import { definePlugin, type EditorProvider } from "@halo-dev/console-shared";
|
||||
import logo from "./assets/vditor.png";
|
||||
import { markRaw } from "vue";
|
||||
import VditorMde from "./views/VditorMde.vue";
|
||||
|
||||
export default definePlugin({
|
||||
components: {},
|
||||
|
@ -11,10 +11,9 @@ export default definePlugin({
|
|||
{
|
||||
name: "vditor-mde",
|
||||
displayName: "Vditor Markdown",
|
||||
// @ts-ignore
|
||||
component: markRaw(Vditor),
|
||||
component: markRaw(VditorMde),
|
||||
rawType: "markdown",
|
||||
logo: logo
|
||||
logo: logo,
|
||||
},
|
||||
];
|
||||
},
|
||||
|
|
|
@ -0,0 +1,122 @@
|
|||
<template>
|
||||
<div id="vditor-debug-panel">
|
||||
<div id="vditor-debug-op" :class="{ active: debugOpOpen }">
|
||||
<div id="vditor-debug-op-bar" @click="debugOpOpen = !debugOpOpen">
|
||||
<img src="../assets/debug.svg" alt="DEBUG"/>
|
||||
</div>
|
||||
<VButton type="primary" size="sm" @click="getRaw">Get Raw</VButton>
|
||||
<VButton type="primary" size="sm" @click="getHTML">Get HTML</VButton>
|
||||
<VButton type="primary" size="sm" @click="getRenderList">
|
||||
Get Vditor Options
|
||||
</VButton>
|
||||
<VButton type="primary" size="sm" @click="getPluginConfig">
|
||||
Get Plugin Config
|
||||
</VButton>
|
||||
<VButton type="primary" size="sm" @click="getCursor">
|
||||
Get Last Cursor
|
||||
</VButton>
|
||||
</div>
|
||||
<div v-if="false" id="vditor-debug-data">
|
||||
<div>Vditor DEBUG</div>
|
||||
</div>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<script setup lang="ts">
|
||||
import type { EditorConfig } from "@/utils/config-utils";
|
||||
import { renderHTML } from "@/utils/vditor-utils";
|
||||
import { VButton } from "@halo-dev/components";
|
||||
import type Vditor from "vditor";
|
||||
import { defineProps, ref } from "vue";
|
||||
|
||||
const debugOpOpen = ref<boolean>(false);
|
||||
|
||||
const props = defineProps<{
|
||||
vditor: Vditor | null;
|
||||
config: EditorConfig | undefined;
|
||||
cursor: Range | undefined;
|
||||
}>();
|
||||
|
||||
const getRaw = () => {
|
||||
if (!props.vditor) return;
|
||||
console.log("RAW: ", props.vditor?.getValue());
|
||||
};
|
||||
|
||||
const getHTML = () => {
|
||||
if (!props.vditor) return;
|
||||
console.log("HTML", renderHTML(props.vditor));
|
||||
};
|
||||
|
||||
const getRenderList = () => {
|
||||
if (!props.vditor) return;
|
||||
console.log("RENDER LIST", props.vditor.vditor.options);
|
||||
};
|
||||
|
||||
const getPluginConfig = () => {
|
||||
if (props.config) {
|
||||
console.log("CONFIG: ", props.config);
|
||||
}
|
||||
};
|
||||
|
||||
const getCursor = () => {
|
||||
if (props.cursor) {
|
||||
console.log("LAST CURSOR", props.cursor);
|
||||
}
|
||||
};
|
||||
</script>
|
||||
|
||||
<style scoped>
|
||||
#vditor-debug-panel {
|
||||
z-index: 9999;
|
||||
}
|
||||
|
||||
#vditor-debug-op {
|
||||
position: fixed;
|
||||
padding: 10px;
|
||||
z-index: 9999;
|
||||
background-color: #999;
|
||||
border-radius: 5px 0 0 5px;
|
||||
right: 0;
|
||||
top: 50%;
|
||||
transform: translateY(-50%) translateX(100%);
|
||||
transition: transform 0.3s;
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
gap: 5px;
|
||||
}
|
||||
|
||||
#vditor-debug-op-bar {
|
||||
content: "";
|
||||
position: absolute;
|
||||
width: 26px;
|
||||
height: 35px;
|
||||
background-color: #999;
|
||||
left: -26px;
|
||||
border-radius: 5px 0 0 5px;
|
||||
top: 50%;
|
||||
transform: translateY(-50%);
|
||||
text-align: center;
|
||||
display: flex;
|
||||
align-items: center;
|
||||
justify-content: center;
|
||||
cursor: pointer;
|
||||
}
|
||||
|
||||
#vditor-debug-op-bar img {
|
||||
width: 16px;
|
||||
}
|
||||
|
||||
#vditor-debug-op.active {
|
||||
transform: translateY(-50%) translateX(0);
|
||||
}
|
||||
|
||||
#vditor-debug-data {
|
||||
position: fixed;
|
||||
left: 10px;
|
||||
top: 10px;
|
||||
background-color: #000000cc;
|
||||
color: #ffffff;
|
||||
z-index: 9999;
|
||||
padding: 10px;
|
||||
}
|
||||
</style>
|
|
@ -31,8 +31,11 @@ const generateCode = () => {
|
|||
emit("done", htmlEncode(code));
|
||||
return;
|
||||
}
|
||||
props.schema.handler && props.schema.handler(data.value);
|
||||
emit("done", null);
|
||||
|
||||
emit(
|
||||
"done",
|
||||
(props.schema.handler && props.schema.handler(data.value)) || null
|
||||
);
|
||||
};
|
||||
|
||||
// 修改FormKit ID来实现Schema重载
|
||||
|
|
|
@ -0,0 +1,83 @@
|
|||
import type { Schema, SchemaData } from "@/type/editor";
|
||||
import { t } from "@/utils/i18n-utils";
|
||||
|
||||
const schema: Schema = {
|
||||
type: "template",
|
||||
id: "gallery",
|
||||
icon: "",
|
||||
name: t("insert_gallery"),
|
||||
formKit: [
|
||||
{
|
||||
$formkit: "select",
|
||||
name: "type",
|
||||
label: t("type"),
|
||||
value: "grid",
|
||||
options: {
|
||||
grid: t("grid"),
|
||||
linear: t("linear"),
|
||||
},
|
||||
},
|
||||
{
|
||||
$formkit: "text",
|
||||
name: "title",
|
||||
label: t("title"),
|
||||
value: "",
|
||||
},
|
||||
{
|
||||
$formkit: "repeater",
|
||||
name: "attachments",
|
||||
label: t("image_group"),
|
||||
min: 1,
|
||||
value: [{}],
|
||||
children: [
|
||||
{
|
||||
$formkit: "attachment",
|
||||
name: "attachment",
|
||||
label: t("image"),
|
||||
accepts: ["image/*"],
|
||||
value: "",
|
||||
},
|
||||
{
|
||||
$formkit: "text",
|
||||
name: "attach_title",
|
||||
label: t("image_alt"),
|
||||
value: "",
|
||||
placeholder: t("image_alt_ph"),
|
||||
},
|
||||
],
|
||||
},
|
||||
],
|
||||
handler: (data: SchemaData) => {
|
||||
// title type list[]
|
||||
const galleryData = data as GallerySchemaData;
|
||||
let html = `\`\`\`halo\ngallery:${galleryData.type}`;
|
||||
if (galleryData.title) {
|
||||
html += `[${galleryData.title}]`;
|
||||
}
|
||||
html += "\n";
|
||||
galleryData.attachments?.forEach((att) => {
|
||||
let title = "";
|
||||
if (att.attach_title) {
|
||||
title = att.attach_title;
|
||||
} else {
|
||||
const start = att.attachment.lastIndexOf("/");
|
||||
const end = att.attachment.lastIndexOf(".");
|
||||
title = att.attachment.slice(start, end);
|
||||
}
|
||||
html += `![${title}](${att.attachment})\n`;
|
||||
});
|
||||
html += "```";
|
||||
return html;
|
||||
},
|
||||
};
|
||||
|
||||
export interface GallerySchemaData extends SchemaData {
|
||||
title: string;
|
||||
type: "grid" | "linear";
|
||||
attachments: Array<{
|
||||
attachment: string;
|
||||
attach_title?: string;
|
||||
}>;
|
||||
}
|
||||
|
||||
export default schema;
|
|
@ -1,5 +1,5 @@
|
|||
import type { Schema } from "@/type/editor";
|
||||
import {t} from "@/utils/i18n-utils";
|
||||
import { t } from "@/utils/i18n-utils";
|
||||
|
||||
const schema: Schema = {
|
||||
type: "template",
|
||||
|
|
|
@ -1,3 +1,5 @@
|
|||
import type { EditorConfig } from "@/utils/config-utils";
|
||||
|
||||
export declare type Options = {
|
||||
defaultRenderMode: "ir" | "wysiwyg" | "sv" | undefined;
|
||||
typeWriterMode: boolean;
|
||||
|
@ -10,6 +12,7 @@ export declare type Options = {
|
|||
openModal: (schema: Schema) => void;
|
||||
quickInsertList: QuickInsert[];
|
||||
enableQuickInsert: boolean;
|
||||
config: EditorConfig;
|
||||
};
|
||||
|
||||
export interface Schema {
|
||||
|
@ -22,7 +25,11 @@ export interface Schema {
|
|||
// 解析后处理
|
||||
afterHandle?: (data: { [key: string]: string }, code: string) => string;
|
||||
// 覆盖解析
|
||||
handler?: (data: { [key: string]: string }) => string;
|
||||
handler?: (data: SchemaData) => string;
|
||||
}
|
||||
|
||||
export interface SchemaData {
|
||||
_id?: string;
|
||||
}
|
||||
|
||||
export interface QuickInsert {
|
||||
|
|
|
@ -7,10 +7,14 @@ export declare type EditorConfig = {
|
|||
enableQuickInsert: boolean;
|
||||
quickInsertUrl: [];
|
||||
disableHTMLBlockPreview: boolean;
|
||||
firstH1AsTitle: boolean;
|
||||
};
|
||||
extension: {
|
||||
allowImageType: string;
|
||||
};
|
||||
developer: {
|
||||
debugger: boolean;
|
||||
};
|
||||
};
|
||||
|
||||
export const defaultEditorConfig: EditorConfig = {
|
||||
|
@ -22,8 +26,12 @@ export const defaultEditorConfig: EditorConfig = {
|
|||
enableQuickInsert: false,
|
||||
quickInsertUrl: [],
|
||||
disableHTMLBlockPreview: false,
|
||||
firstH1AsTitle: false,
|
||||
},
|
||||
extension: {
|
||||
allowImageType: "png,jpg,jpeg,bmp,gif,webp,svg",
|
||||
}
|
||||
}
|
||||
},
|
||||
developer: {
|
||||
debugger: false,
|
||||
},
|
||||
};
|
||||
|
|
|
@ -1,5 +1,5 @@
|
|||
export function getCursor() {
|
||||
return window.getSelection()?.getRangeAt(0);
|
||||
return window.getSelection()?.getRangeAt(0).cloneRange();
|
||||
}
|
||||
|
||||
export function setCursor(range: Range | undefined) {
|
||||
|
|
|
@ -2,7 +2,7 @@ import zhCN from "@/i18n/zh-CN";
|
|||
import type { I18nLang } from "@/type/i18n";
|
||||
import zhTW from "@/i18n/zh-TW";
|
||||
import enUS from "@/i18n/en-US";
|
||||
import {getLanguage} from "@/utils/vditor-utils";
|
||||
import { getLanguage } from "@/utils/vditor-utils";
|
||||
|
||||
const langDict: { [key: string]: I18nLang } = {
|
||||
zh_CN: zhCN,
|
||||
|
|
|
@ -4,6 +4,14 @@ import { t } from "@/utils/i18n-utils";
|
|||
import tips from "@/schema/tips";
|
||||
import git from "@/schema/git";
|
||||
import drive from "@/schema/drive";
|
||||
import gallery from "@/schema/gallery";
|
||||
import { addScript, addStyleSheet } from "@/utils/dom-utils";
|
||||
import type Vditor from "vditor";
|
||||
import type {EditorConfig} from "@/utils/config-utils";
|
||||
|
||||
declare const HaloJs: {
|
||||
renderHalo: (content: string, cdn: string) => string;
|
||||
};
|
||||
|
||||
export function getOptions(options: Options): IOptions {
|
||||
const cdn =
|
||||
|
@ -50,6 +58,9 @@ export function getOptions(options: Options): IOptions {
|
|||
current: "light",
|
||||
path: `${cdn}/dist/css/content-theme`,
|
||||
},
|
||||
math: {
|
||||
inlineDigit: true,
|
||||
},
|
||||
},
|
||||
outline: {
|
||||
enable: true,
|
||||
|
@ -61,6 +72,8 @@ export function getOptions(options: Options): IOptions {
|
|||
upload: {
|
||||
handler: options.uploadImage,
|
||||
},
|
||||
debugger: options.config.developer.debugger,
|
||||
customRenders: getCustomRenders(options),
|
||||
};
|
||||
}
|
||||
|
||||
|
@ -141,6 +154,11 @@ function getToolbar(
|
|||
icon: t("insert_drive"),
|
||||
click: () => openModal(drive),
|
||||
},
|
||||
{
|
||||
name: "insert_gallery",
|
||||
icon: t("insert_gallery"),
|
||||
click: () => openModal(gallery),
|
||||
},
|
||||
],
|
||||
},
|
||||
{
|
||||
|
@ -170,3 +188,70 @@ function buildQuickInsertToolbar(
|
|||
toolbar: children,
|
||||
};
|
||||
}
|
||||
|
||||
/**
|
||||
* 获取自定义渲染器
|
||||
* @param options
|
||||
*/
|
||||
function getCustomRenders(options: Options):
|
||||
| {
|
||||
language: string;
|
||||
render: (element: HTMLElement, vditor: IVditor) => void;
|
||||
}[]
|
||||
| undefined {
|
||||
if (options.config.developer.debugger) {
|
||||
console.log("QUICK INSERT: ", options.quickInsertList);
|
||||
}
|
||||
const renders: {
|
||||
language: string;
|
||||
render: (element: HTMLElement, vditor: IVditor) => void;
|
||||
}[] = [];
|
||||
// 启用内置渲染器
|
||||
addScript(
|
||||
"/plugins/vditor-mde/assets/static/halo-renders/index.js",
|
||||
"halo-render"
|
||||
);
|
||||
addStyleSheet(
|
||||
"/plugins/vditor-mde/assets/static/halo-renders/index.css",
|
||||
"halo-render-css"
|
||||
);
|
||||
renders.push({
|
||||
language: "halo",
|
||||
render: (element: HTMLElement) => {
|
||||
element.querySelectorAll(".language-halo").forEach((el) => {
|
||||
el.outerHTML = HaloJs.renderHalo(
|
||||
el.textContent || "",
|
||||
"/plugins/vditor-mde/assets/static/halo-renders"
|
||||
);
|
||||
});
|
||||
},
|
||||
});
|
||||
console.log("Renders: ", renders);
|
||||
return renders;
|
||||
}
|
||||
|
||||
/**
|
||||
* 进行自定义渲染器的后处理
|
||||
* TODO: 该部分建议加入Vditor
|
||||
* @param vditor vditor
|
||||
* @param config Editor Config
|
||||
* @returns html
|
||||
*/
|
||||
export function renderHTML(vditor: Vditor, config: EditorConfig): string {
|
||||
let value = vditor.getHTML();
|
||||
const customRenders = vditor.vditor.options.customRenders;
|
||||
// FIXME 此部分逻辑有大问题!
|
||||
customRenders?.forEach((render) => {
|
||||
const reg = new RegExp(
|
||||
`<pre><code class="language-${render.language}">(.*?)</code></pre>`,
|
||||
"gs"
|
||||
);
|
||||
value = value.replace(reg, '<div class="language-halo">$1</div>');
|
||||
});
|
||||
// Remove H1 Title When start with "h1"
|
||||
if (config.basic.firstH1AsTitle && value.startsWith("<h1")) {
|
||||
value = value.replace(/<h1(?:\s+[^>]*)?>(.*?)<\/h1>/, "");
|
||||
console.log(value);
|
||||
}
|
||||
return value;
|
||||
}
|
||||
|
|
File diff suppressed because one or more lines are too long
|
@ -0,0 +1,352 @@
|
|||
<script setup lang="ts">
|
||||
import Vditor from "vditor";
|
||||
import { onMounted, onUnmounted, ref, watch } from "vue";
|
||||
import "vditor/dist/index.css";
|
||||
import type { Schema } from "@/type/editor";
|
||||
import { getOptions, renderHTML } from "@/utils/vditor-utils";
|
||||
import type { AttachmentLike } from "@halo-dev/console-shared";
|
||||
import type { Attachment } from "@halo-dev/api-client";
|
||||
import { VLoading } from "@halo-dev/components";
|
||||
import TemplateModal from "@/model/TemplateModal.vue";
|
||||
import joeProgress from "@/schema/joe-progress";
|
||||
import { fetchAllQuickInsert } from "@/utils/fetch-utils";
|
||||
import { quickInsertInject } from "@/utils/quick-insert-utils";
|
||||
import { addStyle } from "@/utils/dom-utils";
|
||||
import { getCursor, setCursor } from "@/utils/cursor-utils";
|
||||
import { defaultEditorConfig, type EditorConfig } from "@/utils/config-utils";
|
||||
import DebugPanel from "@/model/DebugPanel.vue";
|
||||
|
||||
const props = withDefaults(
|
||||
defineProps<{
|
||||
title?: string;
|
||||
raw?: string;
|
||||
content: string;
|
||||
uploadImage?: (file: File) => Promise<Attachment>;
|
||||
}>(),
|
||||
{
|
||||
title: "",
|
||||
raw: "",
|
||||
content: "",
|
||||
uploadImage: undefined,
|
||||
}
|
||||
);
|
||||
|
||||
const vditor = ref();
|
||||
const vditorRef = ref();
|
||||
const vditorLoaded = ref(false);
|
||||
const attachmentSelectorModalShow = ref(false);
|
||||
const editorConfig = ref<EditorConfig>();
|
||||
// 特殊插入框, 当前支持none/tips/git
|
||||
// 自定义插入
|
||||
const customInsertOpen = ref(false);
|
||||
const customInsertSchema = ref<Schema>(joeProgress);
|
||||
let lastSelectionRange: Range | undefined = undefined;
|
||||
// Image Upload
|
||||
let imageUploadCursor: Range | undefined;
|
||||
const imageUploadLock = false;
|
||||
let allowImageUpload: string[] = [];
|
||||
// Debug
|
||||
const debugMode = ref<boolean>(false);
|
||||
// 防止内部标题更新触发updateContent
|
||||
const internalTitle = ref<string>("");
|
||||
|
||||
const emit = defineEmits<{
|
||||
(event: "update:raw", value: string): void;
|
||||
(event: "update:content", value: string): void;
|
||||
(event: "update", value: string): void;
|
||||
(event: "update:title", value: string): void;
|
||||
}>();
|
||||
|
||||
// Watch Title Change
|
||||
watch(
|
||||
() => props.title,
|
||||
(val) => {
|
||||
// When option disabled or nothing to update
|
||||
if (
|
||||
!editorConfig.value?.basic.firstH1AsTitle ||
|
||||
internalTitle.value === val
|
||||
) {
|
||||
return;
|
||||
}
|
||||
// Get title
|
||||
const vdiVal = vditor.value.getValue();
|
||||
if (vdiVal.startsWith("# ")) {
|
||||
internalTitle.value = val;
|
||||
vditor.value.setValue(vdiVal.replace(/# .*?\n/, `# ${val}\n`));
|
||||
}
|
||||
}
|
||||
);
|
||||
|
||||
// Update content
|
||||
const debounceOnUpdate = () => {
|
||||
// 解析标题
|
||||
let value = vditor.value.getValue();
|
||||
if (editorConfig.value?.basic.firstH1AsTitle && value.startsWith("# ")) {
|
||||
// First Line is Title
|
||||
const firstLine = value.match(/^.*/)[0];
|
||||
console.log(`title is ${firstLine.substring(2)}`);
|
||||
internalTitle.value = firstLine.substring(2);
|
||||
emit("update:title", internalTitle.value);
|
||||
// 删除第一行并清除空行
|
||||
value = value.substring(firstLine.length + 2);
|
||||
}
|
||||
// update content
|
||||
emit("update:raw", value);
|
||||
emit(
|
||||
"update:content",
|
||||
renderHTML(vditor.value, editorConfig.value || defaultEditorConfig) || ""
|
||||
);
|
||||
emit("update", value);
|
||||
};
|
||||
|
||||
// 选取附件后处理
|
||||
const attachmentSelect = (attachments: AttachmentLike[]) => {
|
||||
setCursor(lastSelectionRange);
|
||||
// Reference https://github.com/guqing/willow-mde/blob/4b8e697132f8a8f4b08dd0f92cf10d070cb26793/console/src/components/toolbar/Toolbar.vue#L104
|
||||
attachments.forEach((attachment) => {
|
||||
if (typeof attachment === "string") {
|
||||
vditor.value.insertValue(`![](${attachment})`);
|
||||
} else if ("url" in attachment) {
|
||||
vditor.value.insertValue(`![${attachment.type}](${attachment.url})`);
|
||||
} else if ("spec" in attachment) {
|
||||
const { displayName } = attachment.spec;
|
||||
const { permalink } = attachment.status || {};
|
||||
vditor.value.insertValue(`![${displayName}](${permalink})`);
|
||||
}
|
||||
});
|
||||
};
|
||||
|
||||
onUnmounted(async () => {
|
||||
document
|
||||
.querySelectorAll("script[id^='vditor']")
|
||||
.forEach((el) => el.remove());
|
||||
document.querySelectorAll("link[id^='vditor']").forEach((el) => el.remove());
|
||||
vditorLoaded.value = false;
|
||||
});
|
||||
|
||||
onMounted(async () => {
|
||||
// 实验性功能: 获取当前语言
|
||||
const lang = localStorage.getItem("locale") || "zh-CN";
|
||||
try {
|
||||
const response = await fetch(
|
||||
"/apis/api.vditor.mczhengyi.top/editor-options"
|
||||
);
|
||||
editorConfig.value = await response.json();
|
||||
} catch (e) {
|
||||
// ignore this
|
||||
editorConfig.value = defaultEditorConfig;
|
||||
}
|
||||
if (!editorConfig.value) return;
|
||||
// Assign allowImage
|
||||
allowImageUpload = allowImageUpload.concat(
|
||||
editorConfig.value.extension.allowImageType.split(",").map((i) => i.trim())
|
||||
);
|
||||
console.log("ALLOW", allowImageUpload);
|
||||
// 禁用HTML代码块隐藏
|
||||
if (editorConfig.value.basic.disableHTMLBlockPreview)
|
||||
addStyle(
|
||||
"[data-type=html-block] pre {display: block!important;}\n" +
|
||||
".vditor-ir__node[data-type=html-block] .vditor-ir__marker {height: auto; width: auto; display: inline;}",
|
||||
"vditor-mde-hide-html"
|
||||
);
|
||||
// Debug Mode
|
||||
debugMode.value = editorConfig.value.developer.debugger;
|
||||
// Quick Insert Process
|
||||
const qil = await fetchAllQuickInsert(
|
||||
editorConfig.value.basic.quickInsertUrl
|
||||
);
|
||||
qil.forEach((q) => {
|
||||
quickInsertInject(q.inject || [], q.provider);
|
||||
});
|
||||
// Create Vditor
|
||||
vditor.value = new Vditor(
|
||||
vditorRef.value,
|
||||
getOptions({
|
||||
defaultRenderMode: editorConfig.value.basic.defaultRenderMode,
|
||||
typeWriterMode: editorConfig.value.basic.typeWriterMode,
|
||||
after: () => {
|
||||
let content = "";
|
||||
if (props.raw) {
|
||||
content = props.raw;
|
||||
}
|
||||
if (editorConfig.value?.basic.firstH1AsTitle) {
|
||||
internalTitle.value = props.title;
|
||||
content = `# ${props.title}\n\n` + content;
|
||||
}
|
||||
vditor.value.setValue(content);
|
||||
vditorLoaded.value = true;
|
||||
},
|
||||
input: debounceOnUpdate,
|
||||
showAttachment: () => {
|
||||
lastSelectionRange = getCursor();
|
||||
attachmentSelectorModalShow.value = true;
|
||||
},
|
||||
language: lang,
|
||||
codeBlockPreview: editorConfig.value.basic.codeBlockPreview,
|
||||
uploadImage: (files: File[]) => {
|
||||
console.log("UPLOAD IMAGE");
|
||||
if (imageUploadLock) {
|
||||
vditor.value.tip("当前已经存在正在上传的文件,请等待上传完成", 2000);
|
||||
return;
|
||||
}
|
||||
// Save cursor
|
||||
imageUploadCursor = getCursor();
|
||||
// Check extension name
|
||||
const extendName = files[0].name
|
||||
.slice(files[0].name.lastIndexOf(".") + 1)
|
||||
.toLowerCase();
|
||||
if (allowImageUpload.indexOf(extendName) === -1) {
|
||||
vditor.value.tip("不允许上传该类型图片!", 2000);
|
||||
return null;
|
||||
}
|
||||
// Upload
|
||||
if (props.uploadImage) {
|
||||
vditor.value.tip("正在上传图片...", 2000);
|
||||
props.uploadImage(files[0]).then((res: Attachment) => {
|
||||
if (!res.status) return;
|
||||
vditor.value.disabled();
|
||||
// Move cursor
|
||||
const tmpCursor = getCursor();
|
||||
setCursor(imageUploadCursor);
|
||||
imageUploadCursor = undefined;
|
||||
// Insert
|
||||
vditor.value.insertValue(
|
||||
`\n\n![${res.spec.displayName}](${res.status.permalink})\n\n`
|
||||
);
|
||||
// Restore cursor
|
||||
setCursor(tmpCursor);
|
||||
vditor.value.enable();
|
||||
});
|
||||
}
|
||||
return null;
|
||||
},
|
||||
openModal: (schema: Schema) => {
|
||||
lastSelectionRange = getCursor();
|
||||
customInsertSchema.value = schema;
|
||||
customInsertOpen.value = true;
|
||||
},
|
||||
enableQuickInsert: editorConfig.value.basic.enableQuickInsert,
|
||||
quickInsertList: qil,
|
||||
config: editorConfig.value,
|
||||
})
|
||||
);
|
||||
});
|
||||
|
||||
const update = (val: string | null) => {
|
||||
setCursor(lastSelectionRange);
|
||||
if (!val) {
|
||||
vditor.value.tip("未知错误,插入失败", 3000);
|
||||
} else {
|
||||
vditor.value.focus();
|
||||
vditor.value.insertValue(`\n\n${val}\n\n`);
|
||||
}
|
||||
customInsertOpen.value = false;
|
||||
};
|
||||
</script>
|
||||
|
||||
<template>
|
||||
<div
|
||||
id="plugin-vditor-mde"
|
||||
:class="{ h1AsTitle: editorConfig?.basic.firstH1AsTitle }"
|
||||
>
|
||||
<VLoading v-if="!vditorLoaded" style="height: 100%" />
|
||||
<DebugPanel
|
||||
v-if="debugMode"
|
||||
:config="editorConfig"
|
||||
:vditor="vditor"
|
||||
:cursor="lastSelectionRange"
|
||||
/>
|
||||
<div id="vditor" ref="vditorRef"></div>
|
||||
<TemplateModal
|
||||
:open="customInsertOpen"
|
||||
:schema="customInsertSchema"
|
||||
@close="customInsertOpen = false"
|
||||
@done="update"
|
||||
/>
|
||||
|
||||
<AttachmentSelectorModal
|
||||
v-model:visible="attachmentSelectorModalShow"
|
||||
:accepts="['image/*']"
|
||||
:max="1"
|
||||
@select="attachmentSelect"
|
||||
/>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<style>
|
||||
#plugin-vditor-mde ol {
|
||||
list-style: decimal;
|
||||
}
|
||||
|
||||
/** Fix Link color in wysiwyg mode */
|
||||
|
||||
#plugin-vditor-mde a {
|
||||
color: #3478cd;
|
||||
}
|
||||
|
||||
/** Fix content was covered by vditor panel in wysiwyg mode */
|
||||
#plugin-vditor-mde button,
|
||||
#plugin-vditor-mde input {
|
||||
line-height: normal;
|
||||
}
|
||||
|
||||
.insert-modals label {
|
||||
width: 100%;
|
||||
display: flex;
|
||||
}
|
||||
|
||||
.insert-modals label span {
|
||||
width: 60px;
|
||||
text-align: right;
|
||||
}
|
||||
|
||||
.insert-modals select {
|
||||
border: 1px solid #cccccc;
|
||||
border-radius: 3px;
|
||||
padding-top: 8px;
|
||||
padding-bottom: 8px;
|
||||
margin-left: 10px;
|
||||
flex: 1;
|
||||
}
|
||||
|
||||
.insert-modals textarea {
|
||||
border: 1px solid #cccccc;
|
||||
border-radius: 3px;
|
||||
margin-left: 10px;
|
||||
flex: 1;
|
||||
}
|
||||
|
||||
.insert-modals input[type="text"] {
|
||||
border: 1px solid #cccccc;
|
||||
border-radius: 3px;
|
||||
margin-left: 10px;
|
||||
flex: 1;
|
||||
padding: 8px 10px;
|
||||
}
|
||||
|
||||
|
||||
/* title */
|
||||
#plugin-vditor-mde.h1AsTitle .vditor-ir h1:first-child::before,
|
||||
#plugin-vditor-mde.h1AsTitle .vditor-wysiwyg h1:first-child::before {
|
||||
content: "T";
|
||||
}
|
||||
|
||||
#plugin-vditor-mde.h1AsTitle .vditor-ir h1:first-child,
|
||||
#plugin-vditor-mde.h1AsTitle .vditor-wysiwyg h1:first-child {
|
||||
border-bottom: 2px solid #eaecef;
|
||||
color: #333333;
|
||||
text-align: left;
|
||||
text-decoration: none;
|
||||
font-size: 2rem;
|
||||
background: none;
|
||||
}
|
||||
|
||||
/**
|
||||
Fix compatible issues with docsme.
|
||||
https://github.com/justice2001/halo-plugin-vditor/issues/38
|
||||
*/
|
||||
#plugin-vditor-mde code[class*=language-],
|
||||
#plugin-vditor-mde pre[class*=language-] {
|
||||
color: #000000;
|
||||
}
|
||||
</style>
|
|
@ -4,58 +4,19 @@ import { defineConfig } from "vite";
|
|||
import Vue from "@vitejs/plugin-vue";
|
||||
import VueJsx from "@vitejs/plugin-vue-jsx";
|
||||
import Icons from "unplugin-icons/vite";
|
||||
|
||||
const pluginEntryName = "vditor-mde";
|
||||
import { HaloUIPluginBundlerKit } from "@halo-dev/ui-plugin-bundler-kit";
|
||||
|
||||
// https://vitejs.dev/config/
|
||||
export default ({ mode }: { mode: string }) => {
|
||||
const isProduction = mode === "production";
|
||||
const outDir = isProduction
|
||||
? "../src/main/resources/console"
|
||||
: "../build/resources/main/console";
|
||||
|
||||
return defineConfig({
|
||||
plugins: [Vue(),VueJsx(), Icons({ compiler: "vue3" })],
|
||||
export default defineConfig({
|
||||
plugins: [
|
||||
Vue(),
|
||||
VueJsx(),
|
||||
Icons({ compiler: "vue3" }),
|
||||
HaloUIPluginBundlerKit(),
|
||||
],
|
||||
resolve: {
|
||||
alias: {
|
||||
"@": fileURLToPath(new URL("./src", import.meta.url)),
|
||||
},
|
||||
},
|
||||
define: {
|
||||
"process.env": process.env,
|
||||
},
|
||||
build: {
|
||||
outDir,
|
||||
emptyOutDir: true,
|
||||
lib: {
|
||||
entry: "src/index.ts",
|
||||
name: pluginEntryName,
|
||||
formats: ["iife"],
|
||||
fileName: () => "main.js",
|
||||
},
|
||||
rollupOptions: {
|
||||
external: [
|
||||
"vue",
|
||||
"vue-router",
|
||||
"@vueuse/core",
|
||||
"@vueuse/components",
|
||||
"@vueuse/router",
|
||||
"@halo-dev/shared",
|
||||
"@halo-dev/components",
|
||||
],
|
||||
output: {
|
||||
globals: {
|
||||
vue: "Vue",
|
||||
"vue-router": "VueRouter",
|
||||
"@vueuse/core": "VueUse",
|
||||
"@vueuse/components": "VueUse",
|
||||
"@vueuse/router": "VueUse",
|
||||
"@halo-dev/console-shared": "HaloConsoleShared",
|
||||
"@halo-dev/components": "HaloComponents",
|
||||
},
|
||||
extend: true,
|
||||
},
|
||||
},
|
||||
},
|
||||
});
|
||||
};
|
||||
});
|
||||
|
|
|
@ -0,0 +1,47 @@
|
|||
```halo
|
||||
tips:info
|
||||
test1
|
||||
```
|
||||
|
||||
```halo**
|
||||
tips:warn
|
||||
22222
|
||||
```
|
||||
|
||||
```halo
|
||||
tips:default
|
||||
DEFAULT TIPS
|
||||
```
|
||||
|
||||
```halo
|
||||
tips:success
|
||||
SUCCESS TIPS
|
||||
```
|
||||
|
||||
```halo
|
||||
tips:danger
|
||||
DANGER TIPS
|
||||
```
|
||||
|
||||
```halo
|
||||
git:[@github/justice2001/halo-plugin-vditor]
|
||||
```
|
||||
|
||||
```halo
|
||||
gallery:grid[Grid]
|
||||
![/截屏2024-01-17%2015.53.25](/upload/截屏2024-01-17%2015.53.25.png)
|
||||
![123](/upload/截屏2024-01-17%2014.11.28.png)
|
||||
```
|
||||
|
||||
```halo
|
||||
gallery:linear
|
||||
![/截屏2024-01-17%2015.53.25](/upload/截屏2024-01-17%2015.53.25.png)
|
||||
![123](/upload/截屏2024-01-17%2014.11.28.png)
|
||||
```
|
||||
|
||||
|
||||
![截屏2024-01-17 15.53.25.png](/upload/截屏2024-01-17%2015.53.25.png)
|
||||
|
||||
![截屏2024-01-17 15.53.25.png](/upload/截屏2024-01-17%2015.53.25.png)
|
||||
|
||||
![截屏2024-01-17 14.11.28.png](/upload/截屏2024-01-17%2014.11.28.png)
|
|
@ -0,0 +1 @@
|
|||
[WIP]
|
|
@ -0,0 +1,51 @@
|
|||
# 测试文档[WIP]
|
||||
|
||||
为了保证插件更新过程的正常运行,需要进行下面的测试用例,测试包含下面几个大类:
|
||||
|
||||
1. 基础功能,包含插件的基础设置功能,运行状态
|
||||
2. 插件配置,包含所有插件的配置功能
|
||||
3. 拓展功能,包含插件的拓展项测试
|
||||
4. 文档测试,检查插件在前台、编辑器中的显示状态
|
||||
|
||||
## 基础功能
|
||||
|
||||
- 编辑器是否正常加载
|
||||
- 编辑器能否正常使用
|
||||
- 前台是否能够正常渲染
|
||||
|
||||
## 插件配置
|
||||
|
||||
- 编辑器配置
|
||||
- 默认渲染模式是否正常工作
|
||||
- 打字机模式是否正常开启
|
||||
- 编辑器代码块渲染是否能够正常关闭
|
||||
- 启用快速插入功能是否正常启用(使用见下方用例)
|
||||
- 是否能够禁用代码块隐藏
|
||||
- 文件格式
|
||||
- 能否正常限制上传的格式
|
||||
- 渲染
|
||||
- `内置渲染器` 能否正常禁用渲染器
|
||||
- `暗色模式` 切换主题,检查能否正常适配主题暗色模式
|
||||
- `渲染媒体标签` 检查渲染媒体标签是否正常运作
|
||||
- `仅在Markdown模式下渲染` 是否能够禁用在其他编辑器下渲染
|
||||
- 开发者配置
|
||||
- `启用Debugger` Debugger能否正常工作
|
||||
|
||||
## 拓展功能
|
||||
|
||||
WIP
|
||||
|
||||
## 文档测试
|
||||
|
||||
借助文档测试,可以判断编辑器及渲染器能否正常渲染文章,这包含下列几种情况
|
||||
|
||||
- 编辑器能否正常工作
|
||||
- 能否在文章页面进行渲染
|
||||
- 能否在单页进行渲染
|
||||
- 能否兼容PJAX
|
||||
|
||||
文章列表如下
|
||||
|
||||
- [Vditor 官方用例](vditor.md)
|
||||
- [渲染器用例](render.md)
|
||||
- [Halo Render测试](halo-render.md)
|
|
@ -0,0 +1,396 @@
|
|||
Vditor 是一款**所见即所得**编辑器,支持 *Markdown*。
|
||||
|
||||
* 不熟悉 Markdown 可使用工具栏或快捷键进行排版
|
||||
* 熟悉 Markdown 可直接排版,也可切换为分屏预览
|
||||
|
||||
更多细节和用法请参考 [Vditor - 浏览器端的 Markdown 编辑器](https://ld246.com/article/1549638745630),同时也欢迎向我们提出建议或报告问题,谢谢 ❤️
|
||||
|
||||
## 教程
|
||||
|
||||
这是一篇讲解如何正确使用 **Markdown** 的排版示例,学会这个很有必要,能让你的文章有更佳清晰的排版。
|
||||
|
||||
> 引用文本:Markdown is a text formatting syntax inspired
|
||||
|
||||
## 语法指导
|
||||
|
||||
### 普通内容
|
||||
|
||||
这段内容展示了在内容里面一些排版格式,比如:
|
||||
|
||||
- **加粗** - `**加粗**`
|
||||
- *倾斜* - `*倾斜*`
|
||||
- ~~删除线~~ - `~~删除线~~`
|
||||
- `Code 标记` - `` `Code 标记` ``
|
||||
- [超级链接](https://ld246.com) - `[超级链接](https://ld246.com)`
|
||||
- [username@gmail.com](mailto:username@gmail.com) - `[username@gmail.com](mailto:username@gmail.com)`
|
||||
|
||||
### 提及用户
|
||||
|
||||
@Vanessa 通过 `@User` 可以在内容中提及用户,被提及的用户将会收到系统通知。
|
||||
|
||||
> NOTE:
|
||||
>
|
||||
> 1. @用户名之后需要有一个空格
|
||||
> 2. 新手没有艾特的功能权限
|
||||
|
||||
### 表情符号 Emoji
|
||||
|
||||
支持大部分标准的表情符号,可使用输入法直接输入,也可手动输入字符格式。通过输入 `:` 触发自动完成,可在个人设置中[设置常用表情](https://ld246.com/settings/function)。
|
||||
|
||||
#### 一些表情例子
|
||||
|
||||
😄 😆 😵 😭 😰 😅 😢 😤 😍 😌
|
||||
👍 👎 💯 👏 🔔 🎁 ❓ 💣 ❤️ ☕️ 🌀 🙇 💋 🙏 💢
|
||||
|
||||
### 大标题 - Heading 3
|
||||
|
||||
你可以选择使用 H1 至 H6,使用 ##(N) 打头。建议帖子或回帖中的顶级标题使用 Heading 3,不要使用 1 或 2,因为 1 是系统站点级,2 是帖子标题级。
|
||||
|
||||
> NOTE: 别忘了 # 后面需要有空格!
|
||||
|
||||
#### Heading 4
|
||||
|
||||
##### Heading 5
|
||||
|
||||
###### Heading 6
|
||||
|
||||
### 图片
|
||||
|
||||
```
|
||||
![alt 文本](http://image-path.png)
|
||||
![alt 文本](http://image-path.png "图片 Title 值")
|
||||
```
|
||||
|
||||
支持复制粘贴直接上传。
|
||||
|
||||
### 代码块
|
||||
|
||||
#### 普通
|
||||
|
||||
```
|
||||
*emphasize* **strong**
|
||||
_emphasize_ __strong__
|
||||
var a = 1
|
||||
```
|
||||
|
||||
#### 语法高亮支持
|
||||
|
||||
如果在 ``` 后面跟随语言名称,可以有语法高亮的效果哦,比如:
|
||||
|
||||
##### 演示 Go 代码高亮
|
||||
|
||||
```go
|
||||
package main
|
||||
|
||||
import "fmt"
|
||||
|
||||
func main() {
|
||||
fmt.Println("Hello, 世界")
|
||||
}
|
||||
```
|
||||
|
||||
##### 演示 Java 高亮
|
||||
|
||||
```java
|
||||
public class HelloWorld {
|
||||
|
||||
public static void main(String[] args) {
|
||||
System.out.println("Hello World!");
|
||||
}
|
||||
|
||||
}
|
||||
```
|
||||
|
||||
> Tip: 语言名称支持下面这些: `ruby`, `python`, `js`, `html`, `erb`, `css`, `coffee`, `bash`, `json`, `yml`, `xml` ...
|
||||
|
||||
### 有序、无序、任务列表
|
||||
|
||||
#### 无序列表
|
||||
|
||||
- Java
|
||||
- Spring
|
||||
- IoC
|
||||
- AOP
|
||||
- Go
|
||||
- gofmt
|
||||
- Wide
|
||||
- Node.js
|
||||
- Koa
|
||||
- Express
|
||||
|
||||
#### 有序列表
|
||||
|
||||
1. Node.js
|
||||
1. Express
|
||||
2. Koa
|
||||
3. Sails
|
||||
2. Go
|
||||
1. gofmt
|
||||
2. Wide
|
||||
3. Java
|
||||
1. Latke
|
||||
2. IDEA
|
||||
|
||||
#### 任务列表
|
||||
|
||||
- [x] 发布 Sym
|
||||
- [x] 发布 Solo
|
||||
- [ ] 预约牙医
|
||||
|
||||
### 表格
|
||||
|
||||
如果需要展示数据什么的,可以选择使用表格。
|
||||
|
||||
| header 1 | header 2 |
|
||||
| -------- | -------- |
|
||||
| cell 1 | cell 2 |
|
||||
| cell 3 | cell 4 |
|
||||
| cell 5 | cell 6 |
|
||||
|
||||
### 隐藏细节
|
||||
|
||||
<details>
|
||||
<summary>这里是摘要部分。</summary>
|
||||
这里是细节部分。
|
||||
</details>
|
||||
|
||||
### 段落
|
||||
|
||||
空行可以将内容进行分段,便于阅读。(这是第一段)
|
||||
|
||||
使用空行在 Markdown 排版中相当重要。(这是第二段)
|
||||
|
||||
### 链接引用
|
||||
|
||||
[链接文本][链接标识]
|
||||
|
||||
[链接标识]: https://b3log.org
|
||||
|
||||
```
|
||||
[链接文本][链接标识]
|
||||
|
||||
[链接标识]: https://b3log.org
|
||||
```
|
||||
|
||||
### 数学公式
|
||||
|
||||
多行公式块:
|
||||
|
||||
$$
|
||||
\frac{1}{
|
||||
\Bigl(\sqrt{\phi \sqrt{5}}-\phi\Bigr) e^{
|
||||
\frac25 \pi}} = 1+\frac{e^{-2\pi}} {1+\frac{e^{-4\pi}} {
|
||||
1+\frac{e^{-6\pi}}
|
||||
{1+\frac{e^{-8\pi}}{1+\cdots}}
|
||||
}
|
||||
}
|
||||
$$
|
||||
|
||||
行内公式:
|
||||
|
||||
公式 $a^2 + b^2 = \color{red}c^2$ 是行内。
|
||||
|
||||
### 脑图
|
||||
|
||||
```mindmap
|
||||
- 教程
|
||||
- 语法指导
|
||||
- 普通内容
|
||||
- 提及用户
|
||||
- 表情符号 Emoji
|
||||
- 一些表情例子
|
||||
- 大标题 - Heading 3
|
||||
- Heading 4
|
||||
- Heading 5
|
||||
- Heading 6
|
||||
- 图片
|
||||
- 代码块
|
||||
- 普通
|
||||
- 语法高亮支持
|
||||
- 演示 Go 代码高亮
|
||||
- 演示 Java 高亮
|
||||
- 有序、无序、任务列表
|
||||
- 无序列表
|
||||
- 有序列表
|
||||
- 任务列表
|
||||
- 表格
|
||||
- 隐藏细节
|
||||
- 段落
|
||||
- 链接引用
|
||||
- 数学公式
|
||||
- 脑图
|
||||
- 流程图
|
||||
- 时序图
|
||||
- 甘特图
|
||||
- 图表
|
||||
- 五线谱
|
||||
- Graphviz
|
||||
- 多媒体
|
||||
- 脚注
|
||||
- 快捷键
|
||||
```
|
||||
|
||||
### 流程图
|
||||
|
||||
```mermaid
|
||||
graph TB
|
||||
c1-->a2
|
||||
subgraph one
|
||||
a1-->a2
|
||||
end
|
||||
subgraph two
|
||||
b1-->b2
|
||||
end
|
||||
subgraph three
|
||||
c1-->c2
|
||||
end
|
||||
```
|
||||
|
||||
### 时序图
|
||||
|
||||
```mermaid
|
||||
sequenceDiagram
|
||||
Alice->>John: Hello John, how are you?
|
||||
loop Every minute
|
||||
John-->>Alice: Great!
|
||||
end
|
||||
```
|
||||
|
||||
### 甘特图
|
||||
|
||||
```mermaid
|
||||
gantt
|
||||
title A Gantt Diagram
|
||||
dateFormat YYYY-MM-DD
|
||||
section Section
|
||||
A task :a1, 2019-01-01, 30d
|
||||
Another task :after a1 , 20d
|
||||
section Another
|
||||
Task in sec :2019-01-12 , 12d
|
||||
another task : 24d
|
||||
```
|
||||
|
||||
### 图表
|
||||
|
||||
```echarts
|
||||
{
|
||||
"title": { "text": "最近 30 天" },
|
||||
"tooltip": { "trigger": "axis", "axisPointer": { "lineStyle": { "width": 0 } } },
|
||||
"legend": { "data": ["帖子", "用户", "回帖"] },
|
||||
"xAxis": [{
|
||||
"type": "category",
|
||||
"boundaryGap": false,
|
||||
"data": ["2019-05-08","2019-05-09","2019-05-10","2019-05-11","2019-05-12","2019-05-13","2019-05-14","2019-05-15","2019-05-16","2019-05-17","2019-05-18","2019-05-19","2019-05-20","2019-05-21","2019-05-22","2019-05-23","2019-05-24","2019-05-25","2019-05-26","2019-05-27","2019-05-28","2019-05-29","2019-05-30","2019-05-31","2019-06-01","2019-06-02","2019-06-03","2019-06-04","2019-06-05","2019-06-06","2019-06-07"],
|
||||
"axisTick": { "show": false },
|
||||
"axisLine": { "show": false }
|
||||
}],
|
||||
"yAxis": [{ "type": "value", "axisTick": { "show": false }, "axisLine": { "show": false }, "splitLine": { "lineStyle": { "color": "rgba(0, 0, 0, .38)", "type": "dashed" } } }],
|
||||
"series": [
|
||||
{
|
||||
"name": "帖子", "type": "line", "smooth": true, "itemStyle": { "color": "#d23f31" }, "areaStyle": { "normal": {} }, "z": 3,
|
||||
"data": ["18","14","22","9","7","18","10","12","13","16","6","9","15","15","12","15","8","14","9","10","29","22","14","22","9","10","15","9","9","15","0"]
|
||||
},
|
||||
{
|
||||
"name": "用户", "type": "line", "smooth": true, "itemStyle": { "color": "#f1e05a" }, "areaStyle": { "normal": {} }, "z": 2,
|
||||
"data": ["31","33","30","23","16","29","23","37","41","29","16","13","39","23","38","136","89","35","22","50","57","47","36","59","14","23","46","44","51","43","0"]
|
||||
},
|
||||
{
|
||||
"name": "回帖", "type": "line", "smooth": true, "itemStyle": { "color": "#4285f4" }, "areaStyle": { "normal": {} }, "z": 1,
|
||||
"data": ["35","42","73","15","43","58","55","35","46","87","36","15","44","76","130","73","50","20","21","54","48","73","60","89","26","27","70","63","55","37","0"]
|
||||
}
|
||||
]
|
||||
}
|
||||
```
|
||||
|
||||
### 五线谱
|
||||
|
||||
```abc
|
||||
X: 24
|
||||
T: Clouds Thicken
|
||||
C: Paul Rosen
|
||||
S: Copyright 2005, Paul Rosen
|
||||
M: 6/8
|
||||
L: 1/8
|
||||
Q: 3/8=116
|
||||
R: Creepy Jig
|
||||
K: Em
|
||||
|:"Em"EEE E2G|"C7"_B2A G2F|"Em"EEE E2G|\
|
||||
"C7"_B2A "B7"=B3|"Em"EEE E2G|
|
||||
"C7"_B2A G2F|"Em"GFE "D (Bm7)"F2D|\
|
||||
1"Em"E3-E3:|2"Em"E3-E2B|:"Em"e2e gfe|
|
||||
"G"g2ab3|"Em"gfeg2e|"D"fedB2A|"Em"e2e gfe|\
|
||||
"G"g2ab3|"Em"gfe"D"f2d|"Em"e3-e3:|
|
||||
```
|
||||
|
||||
### Graphviz
|
||||
|
||||
```graphviz
|
||||
digraph finite_state_machine {
|
||||
rankdir=LR;
|
||||
size="8,5"
|
||||
node [shape = doublecircle]; S;
|
||||
node [shape = point ]; qi
|
||||
|
||||
node [shape = circle];
|
||||
qi -> S;
|
||||
S -> q1 [ label = "a" ];
|
||||
S -> S [ label = "a" ];
|
||||
q1 -> S [ label = "a" ];
|
||||
q1 -> q2 [ label = "ddb" ];
|
||||
q2 -> q1 [ label = "b" ];
|
||||
q2 -> q2 [ label = "b" ];
|
||||
}
|
||||
```
|
||||
|
||||
### Flowchart
|
||||
|
||||
```flowchart
|
||||
st=>start: Start
|
||||
op=>operation: Your Operation
|
||||
cond=>condition: Yes or No?
|
||||
e=>end
|
||||
|
||||
st->op->cond
|
||||
cond(yes)->e
|
||||
cond(no)->op
|
||||
```
|
||||
|
||||
### 多媒体
|
||||
|
||||
支持 v.qq.com,youtube.com,youku.com,coub.com,facebook.com/video,dailymotion.com,.mp4,.m4v,.ogg,.ogv,.webm,.mp3,.wav 链接解析
|
||||
|
||||
https://v.qq.com/x/cover/zf2z0xpqcculhcz/y0016tj0qvh.html
|
||||
|
||||
### 脚注
|
||||
|
||||
这里是一个脚注引用[^1],这里是另一个脚注引用[^bignote]。
|
||||
|
||||
[^1]: 第一个脚注定义。
|
||||
|
||||
[^bignote]: 脚注定义可使用多段内容。
|
||||
|
||||
缩进对齐的段落包含在这个脚注定义内。
|
||||
|
||||
```
|
||||
可以使用代码块。
|
||||
```
|
||||
|
||||
还有其他行级排版语法,比如**加粗**和[链接](https://b3log.org)。
|
||||
|
||||
```
|
||||
这里是一个脚注引用[^1],这里是另一个脚注引用[^bignote]。
|
||||
[^1]: 第一个脚注定义。
|
||||
[^bignote]: 脚注定义可使用多段内容。
|
||||
|
||||
缩进对齐的段落包含在这个脚注定义内。
|
||||
|
||||
```
|
||||
可以使用代码块。
|
||||
```
|
||||
|
||||
还有其他行级排版语法,比如**加粗**和[链接](https://b3log.org)。
|
||||
```
|
||||
|
||||
## 快捷键
|
||||
|
||||
我们的编辑器支持很多快捷键,具体请参考 [键盘快捷键](https://ld246.com/article/1474030007391)(或者按 "`?` "😼)
|
|
@ -1,7 +1,7 @@
|
|||
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
|
||||
bitsadmin /transfer vditorDownloadJob https://registry.npmjs.org/vditor/-/vditor-3.10.2.tgz %cd%\vditor.tgz
|
||||
7z x vditor.tgz
|
||||
7z x vditor.tar
|
||||
move /y %cd%\package\dist %cd%
|
||||
|
|
|
@ -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.10/files/262
|
||||
https://registry.npmjs.org/vditor/-/vditor-3.10.2.tgz
|
||||
tar -xzvf vditor.tgz
|
||||
mv package/dist .
|
||||
rm -rf package
|
||||
|
|
|
@ -1 +1 @@
|
|||
version=1.5.0-SNAPSHOT
|
||||
version=1.6.3-SNAPSHOT
|
||||
|
|
|
@ -1,8 +1,8 @@
|
|||
package top.mczhengyi.vditor;
|
||||
|
||||
import org.pf4j.PluginWrapper;
|
||||
import org.springframework.stereotype.Component;
|
||||
import run.halo.app.plugin.BasePlugin;
|
||||
import run.halo.app.plugin.PluginContext;
|
||||
|
||||
/**
|
||||
* <p>Plugin main class to manage the lifecycle of the plugin.</p>
|
||||
|
@ -15,8 +15,8 @@ import run.halo.app.plugin.BasePlugin;
|
|||
@Component
|
||||
public class VditorMdePlugin extends BasePlugin {
|
||||
|
||||
public VditorMdePlugin(PluginWrapper wrapper) {
|
||||
super(wrapper);
|
||||
public VditorMdePlugin(PluginContext pluginContext) {
|
||||
super(pluginContext);
|
||||
}
|
||||
|
||||
@Override
|
||||
|
|
|
@ -4,8 +4,19 @@ import lombok.Data;
|
|||
|
||||
@Data
|
||||
public class RenderConfig {
|
||||
private final String VDITOR_VERSION = "3.10.2";
|
||||
|
||||
Boolean enableRender;
|
||||
String darkMode;
|
||||
Boolean mediaRender;
|
||||
Boolean onlyMarkdown;
|
||||
String cdn;
|
||||
String cdnUrl;
|
||||
|
||||
public String getCdn() {
|
||||
if ("self-hosted".equals(cdn)) {
|
||||
return cdnUrl.replace("${v}", VDITOR_VERSION);
|
||||
}
|
||||
return cdn.replace("${v}", VDITOR_VERSION);
|
||||
}
|
||||
}
|
||||
|
|
|
@ -3,6 +3,7 @@ package top.mczhengyi.vditor.extension;
|
|||
import com.google.common.base.Throwables;
|
||||
import lombok.AllArgsConstructor;
|
||||
import lombok.extern.slf4j.Slf4j;
|
||||
import org.pf4j.PluginWrapper;
|
||||
import org.springframework.stereotype.Component;
|
||||
import reactor.core.publisher.Mono;
|
||||
import run.halo.app.plugin.ReactiveSettingFetcher;
|
||||
|
@ -17,18 +18,23 @@ public class VditorPostContentHandler implements ReactivePostContentHandler {
|
|||
|
||||
private final ReactiveSettingFetcher reactiveSettingFetcher;
|
||||
|
||||
private final PluginWrapper pluginWrapper;
|
||||
|
||||
@Override
|
||||
public Mono<PostContentContext> handle(PostContentContext contentContext) {
|
||||
return reactiveSettingFetcher.fetch("render", RenderConfig.class)
|
||||
.map(renderConfig -> {
|
||||
if (renderConfig.getEnableRender()&&
|
||||
(!renderConfig.getOnlyMarkdown() || contentContext.getRawType().equals("markdown"))) {
|
||||
contentContext.setContent(ScriptUtils.renderScript(renderConfig) + "\n" + contentContext.getContent());
|
||||
var content = ScriptUtils.renderScript(renderConfig) + "\n" + contentContext.getContent();
|
||||
contentContext.setContent(ScriptUtils.setContentProperty(content, pluginWrapper));
|
||||
}
|
||||
return contentContext;
|
||||
})
|
||||
.onErrorResume(e -> {
|
||||
if (e instanceof Throwable) {
|
||||
log.error("VditorHeadProcessor process failed", Throwables.getRootCause(e));
|
||||
}
|
||||
return Mono.just(contentContext);
|
||||
});
|
||||
}
|
||||
|
|
|
@ -1,8 +1,10 @@
|
|||
package top.mczhengyi.vditor.extension;
|
||||
|
||||
import com.google.common.base.Throwables;
|
||||
|
||||
import lombok.AllArgsConstructor;
|
||||
import lombok.extern.slf4j.Slf4j;
|
||||
import org.pf4j.PluginWrapper;
|
||||
import org.springframework.stereotype.Component;
|
||||
import reactor.core.publisher.Mono;
|
||||
import run.halo.app.plugin.ReactiveSettingFetcher;
|
||||
|
@ -15,6 +17,8 @@ import top.mczhengyi.vditor.utils.ScriptUtils;
|
|||
@Slf4j
|
||||
public class VditorSinglePageContentHandler implements ReactiveSinglePageContentHandler {
|
||||
private final ReactiveSettingFetcher reactiveSettingFetcher;
|
||||
|
||||
private final PluginWrapper pluginWrapper;
|
||||
@Override
|
||||
public Mono<SinglePageContentContext> handle(SinglePageContentContext contentContext) {
|
||||
return reactiveSettingFetcher.fetch("render", RenderConfig.class)
|
||||
|
@ -22,12 +26,15 @@ public class VditorSinglePageContentHandler implements ReactiveSinglePageContent
|
|||
// 启用条件:开启渲染器,在启用仅Markdown渲染时当前页面为Markdown
|
||||
if (renderConfig.getEnableRender() &&
|
||||
(!renderConfig.getOnlyMarkdown() || contentContext.getRawType().equals("markdown"))) {
|
||||
contentContext.setContent(ScriptUtils.renderScript(renderConfig) + "\n" + contentContext.getContent());
|
||||
var content = ScriptUtils.renderScript(renderConfig) + "\n" + contentContext.getContent();
|
||||
contentContext.setContent(ScriptUtils.setContentProperty(content, pluginWrapper));
|
||||
}
|
||||
return contentContext;
|
||||
})
|
||||
.onErrorResume(e -> {
|
||||
if (e instanceof Throwable) {
|
||||
log.error("VditorHeadProcessor process failed", Throwables.getRootCause(e));
|
||||
}
|
||||
return Mono.empty();
|
||||
});
|
||||
}
|
||||
|
|
|
@ -0,0 +1,54 @@
|
|||
package top.mczhengyi.vditor.utils;
|
||||
|
||||
/**
|
||||
* Script Builder
|
||||
* 用于构造Script、内嵌Script、样式表
|
||||
* @author zhengyi59
|
||||
*/
|
||||
public class ScriptBuilder {
|
||||
private final StringBuilder script;
|
||||
|
||||
ScriptBuilder() {
|
||||
this.script = new StringBuilder();
|
||||
}
|
||||
|
||||
public ScriptBuilder script(String path, String id) {
|
||||
this.script.append("<script src=\"%s?version=${version}\" id=\"vditor-%s\"></script>"
|
||||
.formatted(getUrl(path), id));
|
||||
return this;
|
||||
}
|
||||
|
||||
public ScriptBuilder innerScript(String script) {
|
||||
this.script.append("<script>%s</script>".formatted(script));
|
||||
return this;
|
||||
}
|
||||
|
||||
public ScriptBuilder stylesheet(String path, String id) {
|
||||
this.script.append("<link rel=\"stylesheet\" type=\"text/css\" href=\"%s?version=${version}\" id=\"vditor-%s\" />"
|
||||
.formatted(getUrl(path), id));
|
||||
return this;
|
||||
}
|
||||
|
||||
public ScriptBuilder sign(String signId) {
|
||||
this.script.append("<div data-type=\"sign\" id=\"vditor-%s\"></div>".formatted(signId));
|
||||
return this;
|
||||
}
|
||||
|
||||
public ScriptBuilder variable(String varId, String varValue) {
|
||||
this.script.append("<div data-type=\"var\" id=\"vditor-var-%s\" value=\"%s\"></div>"
|
||||
.formatted(varId, varValue));
|
||||
return this;
|
||||
}
|
||||
|
||||
public String getScript() {
|
||||
return this.script.toString();
|
||||
}
|
||||
|
||||
private String getUrl(String url) {
|
||||
if (url.startsWith("http")) {
|
||||
return url;
|
||||
} else {
|
||||
return "/plugins/vditor-mde/assets/static/%s".formatted(url);
|
||||
}
|
||||
}
|
||||
}
|
|
@ -1,44 +1,36 @@
|
|||
package top.mczhengyi.vditor.utils;
|
||||
|
||||
import org.pf4j.PluginWrapper;
|
||||
import org.springframework.util.PropertyPlaceholderHelper;
|
||||
import top.mczhengyi.vditor.bean.RenderConfig;
|
||||
import java.util.Properties;
|
||||
|
||||
public class ScriptUtils {
|
||||
static final PropertyPlaceholderHelper
|
||||
PROPERTY_PLACEHOLDER_HELPER = new PropertyPlaceholderHelper("${", "}");
|
||||
|
||||
public static String renderScript(RenderConfig renderConfig) {
|
||||
StringBuilder script = new StringBuilder();
|
||||
script.append(basicScript(renderConfig));
|
||||
// 如果是跟随Joe 3.0则注入脚本
|
||||
if ("joe".equals(renderConfig.getDarkMode()))
|
||||
script.append(joeDarkMode());
|
||||
return script.toString();
|
||||
ScriptBuilder script = new ScriptBuilder();
|
||||
script.sign("article-sign")
|
||||
.variable("cdn", renderConfig.getCdn())
|
||||
.stylesheet("vditor-render.css", "style")
|
||||
.script("dist/method.min.js", "methods")
|
||||
.script("render-utils.js", "render-utils"); // 标记文章位置
|
||||
if (renderConfig.getMediaRender())
|
||||
script.script("external/media-render.js", "media");
|
||||
if (!renderConfig.getDarkMode().equals("disabled")) {
|
||||
script.script("dark-mode/dark-%s.js".formatted(renderConfig.getDarkMode()), "dark-mode");
|
||||
}
|
||||
script.script("halo-renders/index.js", "halo-render-js")
|
||||
.stylesheet("halo-renders/index.css", "halo-render-css")
|
||||
.script("external/halo-renders.js", "halo-render")
|
||||
.script("render.js", "render"); // 完成操作后渲染
|
||||
return script.getScript();
|
||||
}
|
||||
|
||||
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="vditor-render"
|
||||
data-dark="%s" data-mediaRender="%s"></script>
|
||||
""".formatted(renderConfig.getDarkMode(), renderConfig.getMediaRender());
|
||||
}
|
||||
|
||||
public static String joeDarkMode() {
|
||||
return """
|
||||
<script>
|
||||
window.addEventListener("load", () => {
|
||||
var html = document.getElementsByTagName("html")[0]
|
||||
if (!html) return
|
||||
setTheme(html.dataset.mode)
|
||||
var callback = (mutation) => {
|
||||
if (mutation[0].attributeName=="data-mode") {
|
||||
console.log("CHANGED")
|
||||
var mode = mutation[0].target.dataset.mode
|
||||
setTheme(mode)
|
||||
}
|
||||
}
|
||||
var observer = new MutationObserver(callback)
|
||||
observer.observe(html, {attributes:true})
|
||||
})
|
||||
</script>
|
||||
""";
|
||||
public static String setContentProperty(String script, PluginWrapper pluginWrapper) {
|
||||
final Properties properties = new Properties();
|
||||
properties.setProperty("version", pluginWrapper.getDescriptor().getVersion());
|
||||
return PROPERTY_PLACEHOLDER_HELPER.replacePlaceholders(script, properties);
|
||||
}
|
||||
}
|
||||
|
|
|
@ -51,6 +51,11 @@ spec:
|
|||
label: 禁用HTML代码块隐藏
|
||||
help: 开启此选项后,HTML代码块将会一直显示
|
||||
value: false
|
||||
- $formkit: checkbox
|
||||
name: firstH1AsTitle
|
||||
label: 🧪自动将第一行作为标题
|
||||
help: 开启此选项后,第一行H1将会作为文章标题,同时标题会用 T 标识
|
||||
value: false
|
||||
- group: extension
|
||||
label: 文件格式
|
||||
formSchema:
|
||||
|
@ -89,3 +94,34 @@ spec:
|
|||
label: "仅在Markdown模式下渲染"
|
||||
help: "启用该功能将仅在Markdown格式的文章下注入渲染脚本"
|
||||
value: true
|
||||
- $formkit: select
|
||||
id: cdn
|
||||
name: cdn
|
||||
label: "Vditor CDN"
|
||||
help: "前台渲染所使用的 CDN 配置"
|
||||
value: "/plugins/vditor-mde/assets/static"
|
||||
options:
|
||||
- label: "内建"
|
||||
value: "/plugins/vditor-mde/assets/static"
|
||||
- label: "jsDelivr(国外)"
|
||||
value: "https://cdn.jsdelivr.net/npm/vditor@${v}"
|
||||
- label: "unpkg(国外)"
|
||||
value: "https://unpkg.com/vditor@${v}"
|
||||
- label: "npmmirror(国内)"
|
||||
value: "https://registry.npmmirror.com/vditor/${v}/files"
|
||||
- label: "自建"
|
||||
value: "self-hosted"
|
||||
- $formkit: text
|
||||
if: "$cdn == 'self-hosted'"
|
||||
name: cdnUrl
|
||||
label: CDN URL
|
||||
placeholder: "在此处填入您的自建cdn(不支持 cdnjs 格式,使用${v}代替版本号)"
|
||||
value: ""
|
||||
- group: developer
|
||||
label: 开发者设置
|
||||
formSchema:
|
||||
- $formkit: checkbox
|
||||
name: debugger
|
||||
label: "启用Debugger"
|
||||
help: "启动Vditor编辑器的Debugger模式,一般用于开发者使用"
|
||||
value: false
|
|
@ -1,22 +1,23 @@
|
|||
apiVersion: plugin.halo.run/v1alpha1
|
||||
kind: Plugin
|
||||
metadata:
|
||||
# The name defines how the plugin is invoked,A unique name
|
||||
name: vditor-mde
|
||||
annotations:
|
||||
store.halo.run/app-id: app-uBcYw
|
||||
spec:
|
||||
enabled: true
|
||||
requires: ">=2.11.0"
|
||||
requires: ">=2.14.0"
|
||||
author:
|
||||
name: zhengyi59
|
||||
website: https://github.com/justice2001/veditor-plugin
|
||||
website: https://github.com/justice2001/halo-plugin-vditor
|
||||
logo: vditor.png
|
||||
# 'homepage' usually links to the GitHub repository of the plugin
|
||||
homepage: https://github.com/justice2001/veditor-plugin
|
||||
# 'displayName' explains what the plugin does in only a few words
|
||||
homepage: https://www.halo.run/store/apps/app-uBcYw
|
||||
repo: https://github.com/justice2001/halo-plugin-vditor
|
||||
issues: https://github.com/justice2001/halo-plugin-vditor/issues
|
||||
settingName: vditor-mde-settings
|
||||
configMapName: vditor-mde-configMap
|
||||
displayName: "Vditor编辑器"
|
||||
description: "适用于Halo的Vditor编辑器"
|
||||
displayName: "Vditor 编辑器"
|
||||
description: "适用于 Halo 的 Vditor 编辑器"
|
||||
license:
|
||||
- name: "GPL-3.0"
|
||||
url: "https://github.com/justice2001/veditor-plugin/blob/main/LICENSE"
|
||||
url: "https://github.com/justice2001/halo-plugin-vditor/blob/main/LICENSE"
|
||||
|
|
|
@ -0,0 +1,7 @@
|
|||
window.addEventListener("load", () => {
|
||||
console.log("Use Joe Dark Mode")
|
||||
let html = document.getElementsByTagName("html")[0]
|
||||
if (!html) return
|
||||
const mode = html.dataset.mode
|
||||
setDarkMode(mode === "dark")
|
||||
})
|
|
@ -0,0 +1,5 @@
|
|||
window.addEventListener("load", () => {
|
||||
console.log("Use System Dark Mode")
|
||||
let media = window.matchMedia('(prefers-color-scheme: dark)');
|
||||
setDarkMode(media.matches)
|
||||
})
|
|
@ -0,0 +1,8 @@
|
|||
vditorRender.addExternal((conf) => {
|
||||
document.querySelectorAll(".language-halo").forEach(el => {
|
||||
el.outerHTML = HaloJs.renderHalo(
|
||||
el.textContent,
|
||||
"/plugins/vditor-mde/assets/static/halo-renders"
|
||||
);
|
||||
})
|
||||
})
|
|
@ -0,0 +1,5 @@
|
|||
vditorRender.addExternal((conf) => {
|
||||
console.log("Run External Function: Media Render!")
|
||||
let root = document.getElementById("vditor-article-sign").parentElement;
|
||||
Vditor.mediaRender(root)
|
||||
})
|
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
After Width: | Height: | Size: 8.1 KiB |
|
@ -0,0 +1 @@
|
|||
<?xml version="1.0" standalone="no"?><!DOCTYPE svg PUBLIC "-//W3C//DTD SVG 1.1//EN" "http://www.w3.org/Graphics/SVG/1.1/DTD/svg11.dtd"><svg t="1703730875005" class="icon" viewBox="0 0 1024 1024" version="1.1" xmlns="http://www.w3.org/2000/svg" p-id="7787" xmlns:xlink="http://www.w3.org/1999/xlink" width="200" height="200"><path d="M157.520435 0h708.958984C953.08055-0.058514 1023.443968 69.822161 1023.999854 156.408663v711.182527C1023.443968 954.177692 953.065921 1024.058368 866.479419 1023.999854H157.520435C70.919304 1024.058368 0.555886 954.177692 0 867.59119V156.408663C0.555886 69.822161 70.933933-0.058514 157.520435 0z" fill="#FFFFFF" p-id="7788"></path><path d="M438.008623 167.146033a227.386482 227.386482 0 0 1 237.231509 86.147645 224.372997 224.372997 0 0 1 42.964108 115.858269c1.097143 15.828112 0.6144 31.743995-1.404342 47.469708l-0.906972 2.311314c-28.671996 0.146286-57.05142 6.026971-83.426731 17.261712a226.88911 226.88911 0 0 0-64.146276 42.569136c-5.412571 6.334171-11.746741 11.849141-17.568912 17.876112-20.085026 20.085026-40.857594 40.755194-61.14742 61.235191-5.909942 5.529599-11.029941 11.746741-17.466512 16.764341A211.924084 211.924084 0 0 0 317.147383 424.755139a171.563861 171.563861 0 0 0-41.545137-5.31017l-8.045713-0.512a192.248658 192.248658 0 0 1-1.901714-32.226739 217.951055 217.951055 0 0 1 24.590625-103.292328 227.181682 227.181682 0 0 1 147.777808-116.267869z m38.85348 103.906728a116.443412 116.443412 0 0 0-101.800214 112.552213 115.653469 115.653469 0 0 0 18.168683 65.53599 116.66284 116.66284 0 1 0 83.631531-178.088203z" fill="#2C61E4" p-id="7789"></path><path d="M715.892926 418.932969L716.799898 416.621655v2.311314a211.529113 211.529113 0 0 1 92.964558 401.568857 213.942827 213.942827 0 0 1-147.880208 14.453026 210.826941 210.826941 0 0 1-84.231302-44.470851 52.809135 52.809135 0 0 1-18.066283-40.755194 53.408907 53.408907 0 0 1 67.876562-50.190621 55.120449 55.120449 0 0 1 21.679539 11.746741 104.916099 104.916099 0 1 0 11.936913-169.574376 136.235866 136.235866 0 0 0-24.795425 20.479997L423.350797 774.436461a231.80431 231.80431 0 0 1-31.817139 28.11611 211.426713 211.426713 0 0 1-105.61827 38.85348 212.523855 212.523855 0 0 1-206.204314-112.054841 211.631513 211.631513 0 0 1 111.030842-295.935958 214.235398 214.235398 0 0 1 77.311989-14.467655l8.031084 0.512c12.317255 1.609143 23.639768 7.592227 31.919538 16.852112a53.511307 53.511307 0 0 1-7.738513 78.701703 54.506049 54.506049 0 0 1-29.213253 10.649599 106.013242 106.013242 0 0 0-60.22582 16.471769 104.813699 104.813699 0 0 0 94.661473 185.929116 108.426956 108.426956 0 0 0 42.071765-30.134853c41.954737-40.959994 83.017131-82.812331 124.781696-123.874725 6.436571-5.017599 11.55657-11.234741 17.466512-16.76434 20.085026-20.070397 40.769823-40.755194 61.14742-61.235192 5.822171-6.026971 12.141713-11.55657 17.554283-17.876111a226.88911 226.88911 0 0 1 63.956105-41.954737 215.537341 215.537341 0 0 1 83.426731-17.261712z" fill="#3377F9" p-id="7790"></path><path d="M275.572989 419.444969c14.028798 0.058514 27.984453 1.8432 41.559766 5.31017a211.924084 211.924084 0 0 1 155.20912 149.884322c-41.764565 41.062394-82.82696 82.92936-124.781696 123.889353a106.422842 106.422842 0 0 0 21.67954-41.354965 104.301699 104.301699 0 0 0-6.729142-71.592218 105.515871 105.515871 0 0 0-65.448219-55.617821 111.630613 111.630613 0 0 0-26.009597-4.315428 54.506049 54.506049 0 0 0 29.110853-10.63497 53.511307 53.511307 0 0 0 7.738513-78.701703 51.799764 51.799764 0 0 0-32.329138-16.86674z" fill="#D2402C" p-id="7791"></path></svg>
|
After Width: | Height: | Size: 3.5 KiB |
|
@ -0,0 +1,309 @@
|
|||
.halo-render {
|
||||
margin: 5px;
|
||||
}
|
||||
.halo-render .btn {
|
||||
background-color: #3478CD;
|
||||
color: #ffffff;
|
||||
padding: 5px 8px;
|
||||
text-align: center;
|
||||
border-radius: 3px;
|
||||
font-size: 14px;
|
||||
min-width: 80px;
|
||||
text-decoration: none;
|
||||
}
|
||||
.halo-render .error {
|
||||
text-align: center;
|
||||
color: #C31919;
|
||||
}
|
||||
.halo-render .invalid-type {
|
||||
color: red;
|
||||
font-size: 20px;
|
||||
text-align: center;
|
||||
padding: 10px;
|
||||
background-color: lightpink;
|
||||
border-radius: 3px;
|
||||
}
|
||||
.halo-render .iconfont {
|
||||
font-family: iconfont, sans-serif;
|
||||
font-size: 16px;
|
||||
}
|
||||
.halo-render img,
|
||||
.halo-render a {
|
||||
width: initial;
|
||||
height: initial;
|
||||
min-width: initial;
|
||||
min-height: initial;
|
||||
max-width: initial;
|
||||
max-height: initial;
|
||||
border-radius: initial;
|
||||
border: initial;
|
||||
margin: initial;
|
||||
padding: initial;
|
||||
}
|
||||
.halo-render-wrapper {
|
||||
margin: 20px 15px;
|
||||
}
|
||||
|
||||
.halo-render.tips {
|
||||
position: relative;
|
||||
padding: 15px 15px;
|
||||
border-left-style: solid;
|
||||
border-left-width: 6px;
|
||||
border-radius: 3px;
|
||||
color: #333333;
|
||||
border-left-color: #818181;
|
||||
background-color: #F4F4F4;
|
||||
}
|
||||
.halo-render.tips.halo-render.tips:after {
|
||||
font-family: "iconfont", serif !important;
|
||||
width: 20px;
|
||||
height: 20px;
|
||||
line-height: 20px;
|
||||
text-align: center;
|
||||
font-size: 14px;
|
||||
font-style: normal;
|
||||
-webkit-font-smoothing: antialiased;
|
||||
-moz-osx-font-smoothing: grayscale;
|
||||
border-radius: 50%;
|
||||
color: #F4F4F4;
|
||||
background-color: #818181;
|
||||
display: block;
|
||||
position: absolute;
|
||||
left: calc(-10px - 6px / 2);
|
||||
top: 4px;
|
||||
content: "\e77e";
|
||||
}
|
||||
.halo-render.tips.halo-render.tips-warn {
|
||||
background-color: #FFFAED;
|
||||
color: #FF8C00;
|
||||
border-left-color: #FF8C00;
|
||||
}
|
||||
.halo-render.tips.halo-render.tips-warn.halo-render.tips.halo-render.tips-warn:after {
|
||||
color: #FFFAED;
|
||||
background-color: #FF8C00;
|
||||
content: "\e671";
|
||||
}
|
||||
.halo-render.tips.halo-render.tips-danger {
|
||||
background-color: #FFEDED;
|
||||
color: #C31919;
|
||||
border-left-color: #C31919;
|
||||
}
|
||||
.halo-render.tips.halo-render.tips-danger.halo-render.tips.halo-render.tips-danger:after {
|
||||
background-color: #C31919;
|
||||
color: #FFEDED;
|
||||
content: "\e671";
|
||||
}
|
||||
.halo-render.tips.halo-render.tips-success {
|
||||
background-color: #F2FFED;
|
||||
color: #05B800;
|
||||
border-left-color: #05B800;
|
||||
}
|
||||
.halo-render.tips.halo-render.tips-success.halo-render.tips.halo-render.tips-success:after {
|
||||
background-color: #05B800;
|
||||
color: #F2FFED;
|
||||
}
|
||||
.halo-render.tips.halo-render.tips-info {
|
||||
background-color: #F1F7FF;
|
||||
color: #3478CD;
|
||||
border-left-color: #3478CD;
|
||||
}
|
||||
.halo-render.tips.halo-render.tips-info.halo-render.tips.halo-render.tips-info:after {
|
||||
color: #F1F7FF;
|
||||
background-color: #3478CD;
|
||||
}
|
||||
|
||||
.halo-render.git {
|
||||
display: flex;
|
||||
gap: 10px;
|
||||
border: 1px solid #ececec;
|
||||
padding: 10px;
|
||||
border-radius: 3px;
|
||||
}
|
||||
.halo-render.git .information {
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
gap: 10px;
|
||||
}
|
||||
.halo-render.git .information .info {
|
||||
font-size: 14px;
|
||||
display: flex;
|
||||
align-items: center;
|
||||
gap: 15px;
|
||||
}
|
||||
.halo-render.git .information .info .topics {
|
||||
position: relative;
|
||||
display: flex;
|
||||
gap: 5px;
|
||||
color: #ffffff;
|
||||
font-size: 12px;
|
||||
max-width: 30%;
|
||||
overflow: hidden;
|
||||
transition: max-width 0.2s;
|
||||
cursor: pointer;
|
||||
}
|
||||
.halo-render.git .information .info .topics:after {
|
||||
position: absolute;
|
||||
display: block;
|
||||
width: 20px;
|
||||
height: 30px;
|
||||
right: 0;
|
||||
background: linear-gradient(to right, transparent, #ffffff);
|
||||
content: '';
|
||||
transition: width 0.2s;
|
||||
}
|
||||
.halo-render.git .information .info .topics:hover:after {
|
||||
width: 0;
|
||||
}
|
||||
.halo-render.git .information .info .topics:hover {
|
||||
max-width: 70%;
|
||||
}
|
||||
.halo-render.git .information .info .topics .topic {
|
||||
background-color: #3478CD;
|
||||
padding: 3px 5px;
|
||||
border-radius: 3px;
|
||||
white-space: nowrap;
|
||||
}
|
||||
.halo-render.git .information .info .language {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
gap: 5px;
|
||||
}
|
||||
.halo-render.git .information .info .language .dot {
|
||||
width: 14px;
|
||||
height: 14px;
|
||||
border-radius: 50%;
|
||||
}
|
||||
.halo-render.git .information .info .count {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
gap: 10px;
|
||||
}
|
||||
.halo-render.git .information .info .count .count-item {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
gap: 5px;
|
||||
}
|
||||
.halo-render.git .information .info .count .icon {
|
||||
font-size: 18px;
|
||||
color: #818181;
|
||||
}
|
||||
.halo-render.git .information .description {
|
||||
font-size: 14px;
|
||||
}
|
||||
.halo-render.git .information .info {
|
||||
font-size: 14px;
|
||||
color: #999999;
|
||||
}
|
||||
.halo-render.git .error {
|
||||
color: darkred;
|
||||
text-align: center;
|
||||
}
|
||||
.halo-render.git .repo-name a {
|
||||
font-size: 18px;
|
||||
color: #3478CD;
|
||||
text-decoration: none;
|
||||
}
|
||||
.halo-render.git .icon {
|
||||
font-family: "iconfont", serif;
|
||||
font-size: 24px;
|
||||
color: #333333;
|
||||
}
|
||||
|
||||
.halo-render.drive {
|
||||
border: 1px solid #F4F4F4;
|
||||
border-radius: 3px;
|
||||
padding: 10px;
|
||||
display: flex;
|
||||
align-items: center;
|
||||
justify-content: space-between;
|
||||
user-select: none;
|
||||
}
|
||||
.halo-render.drive .drive-info {
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
gap: 10px;
|
||||
}
|
||||
.halo-render.drive .drive-info .platform {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
}
|
||||
.halo-render.drive .drive-info .platform .drive-icon {
|
||||
width: 25px;
|
||||
}
|
||||
.halo-render.drive .drive-info .platform span {
|
||||
font-size: 16px;
|
||||
margin-left: 3px;
|
||||
color: #3478CD;
|
||||
}
|
||||
.halo-render.drive .download-info {
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
align-items: center;
|
||||
}
|
||||
.halo-render.drive .download-info .drive-password {
|
||||
color: #818181;
|
||||
font-size: 12px;
|
||||
margin-top: 5px;
|
||||
}
|
||||
|
||||
.halo-render.gallery {
|
||||
border: 1px solid #F4F4F4;
|
||||
border-radius: 3px;
|
||||
padding: 10px 15px;
|
||||
}
|
||||
.halo-render.gallery .title {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
font-size: 18px;
|
||||
margin-bottom: 10px;
|
||||
}
|
||||
.halo-render.gallery .title .iconfont {
|
||||
font-size: 30px;
|
||||
margin-right: 5px;
|
||||
}
|
||||
.halo-render.gallery .image-list {
|
||||
display: flex;
|
||||
width: 100%;
|
||||
}
|
||||
.halo-render.gallery .image-list .vditor--gallery-container .img {
|
||||
border-radius: 3px !important;
|
||||
}
|
||||
.halo-render.gallery.halo-render.gallery.grid .image-list {
|
||||
flex-wrap: wrap;
|
||||
column-gap: 2%;
|
||||
row-gap: 10px;
|
||||
}
|
||||
.halo-render.gallery.halo-render.gallery.grid .image-list .vditor--gallery-container {
|
||||
width: 32%;
|
||||
height: 0;
|
||||
position: relative;
|
||||
padding-bottom: 32%;
|
||||
}
|
||||
.halo-render.gallery.halo-render.gallery.grid .image-list .vditor--gallery-container .img {
|
||||
position: absolute;
|
||||
width: 100%;
|
||||
height: 100%;
|
||||
object-fit: cover;
|
||||
}
|
||||
@media screen and (min-width: 1400px) {
|
||||
.halo-render.gallery.halo-render.gallery.grid .image-list .container {
|
||||
width: 23%;
|
||||
padding-bottom: 23%;
|
||||
}
|
||||
}
|
||||
.halo-render.gallery.halo-render.gallery.linear .image-list {
|
||||
display: flex;
|
||||
overflow-y: hidden;
|
||||
overflow-x: auto;
|
||||
width: 100%;
|
||||
gap: 10px;
|
||||
}
|
||||
.halo-render.gallery.halo-render.gallery.linear .image-list .vditor--gallery-container {
|
||||
height: 300px;
|
||||
}
|
||||
.halo-render.gallery.halo-render.gallery.linear .image-list .vditor--gallery-container .img {
|
||||
height: 99%;
|
||||
width: auto!important;
|
||||
max-width: unset!important;
|
||||
}
|
||||
|
File diff suppressed because one or more lines are too long
|
@ -0,0 +1,78 @@
|
|||
(function (win) {
|
||||
if (win.vditorRender) return;
|
||||
|
||||
const THEME_PREFIX="/plugins/vditor-mde/assets/static/themes"
|
||||
const CDN = "https://cdn.jsdelivr.net/npm/vditor@3.9.9"
|
||||
|
||||
/** 拓展处理 ({dark}) => void */
|
||||
let functionList = []
|
||||
let darkMode = false
|
||||
|
||||
/**
|
||||
* 处理渲染
|
||||
* @param func
|
||||
*/
|
||||
function addExternal(func) {
|
||||
functionList.push(func)
|
||||
}
|
||||
|
||||
/**
|
||||
* 设置暗色模式
|
||||
* @param {Boolean} dark
|
||||
*/
|
||||
function setDarkMode(dark = false) {
|
||||
darkMode = dark
|
||||
}
|
||||
|
||||
/**
|
||||
* 设置CDN
|
||||
* @param {string} cdn CDN
|
||||
*/
|
||||
function setCDN(cdn) {
|
||||
this.CDN = cdn;
|
||||
}
|
||||
|
||||
/**
|
||||
* 渲染
|
||||
* @param elId 搜寻位置
|
||||
*/
|
||||
function vditorRender(elId="vditor-article-sign") {
|
||||
Vditor.setContentTheme(darkMode?"dark":"light", THEME_PREFIX)
|
||||
let root = document.getElementById(elId)
|
||||
if (!root) {
|
||||
console.log("[Vditor Render] Can't Found Article Root Element!");
|
||||
return
|
||||
}
|
||||
root = root.parentElement
|
||||
root.classList.add("vditor-reset")
|
||||
console.log(`Using CDN: ${this.CDN}`)
|
||||
// Render
|
||||
const renderTheme = darkMode?"dark":"classic"
|
||||
Vditor.mathRender(root, {cdn: this.CDN})
|
||||
Vditor.mindmapRender(root, this.CDN, renderTheme)
|
||||
Vditor.mermaidRender(root, this.CDN, renderTheme)
|
||||
Vditor.chartRender(root, this.CDN, renderTheme)
|
||||
Vditor.abcRender(root, this.CDN)
|
||||
Vditor.graphvizRender(root, this.CDN)
|
||||
Vditor.flowchartRender(root, this.CDN)
|
||||
Vditor.plantumlRender(root, this.CDN)
|
||||
// Run External Plugin
|
||||
functionList.forEach(func => {
|
||||
func({
|
||||
darkMode
|
||||
})
|
||||
})
|
||||
}
|
||||
|
||||
win.vditorRender = {
|
||||
THEME_PREFIX,
|
||||
CDN,
|
||||
functionList,
|
||||
darkMode,
|
||||
addExternal,
|
||||
setDarkMode,
|
||||
vditorRender,
|
||||
render: vditorRender,
|
||||
setCDN
|
||||
}
|
||||
})(window)
|
|
@ -1,73 +1,23 @@
|
|||
const THEME_PREFIX="/plugins/vditor-mde/assets/static/dist/css/content-theme"
|
||||
const CDN = "/plugins/vditor-mde/assets/static"
|
||||
|
||||
window.addEventListener("load", () => {
|
||||
// 暗色模式初始化
|
||||
let dark = initDarkMode()
|
||||
setTheme(dark?"dark":"light")
|
||||
|
||||
const root = document.getElementById("vditor-render").parentElement
|
||||
root.classList.add("vditor-reset")
|
||||
// Render
|
||||
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("vditor-render").dataset.mediarender
|
||||
if (mediaRenderOption==="true") {
|
||||
let article = document.getElementById("vditor-render").parentElement;
|
||||
Vditor.mediaRender(article)
|
||||
if (!window.vditorPjax) {
|
||||
window.vditorPjax = true
|
||||
let cdnVar = document.getElementById("vditor-var-cdn")
|
||||
let cdn = cdnVar ? cdnVar.getAttribute("value") : ""
|
||||
cdn && vditorRender.setCDN(cdn)
|
||||
// 当网页已经准备就绪则直接执行渲染任务
|
||||
if (document.readyState === "complete") {
|
||||
vditorRender.render()
|
||||
} else {
|
||||
window.addEventListener('load', () => vditorRender.render())
|
||||
}
|
||||
})
|
||||
|
||||
/**
|
||||
* 初始化暗色模式策略
|
||||
* 创建所需监听器
|
||||
* @returns {boolean} 初始暗黑模式状态
|
||||
*/
|
||||
function initDarkMode() {
|
||||
let darkModeChange = document.getElementById("vditor-render").dataset.dark
|
||||
let dark = false
|
||||
// 检测暗黑模式策略
|
||||
switch (darkModeChange) {
|
||||
// 禁用暗黑模式
|
||||
case "disabled": break
|
||||
// 跟随系统
|
||||
case "system":
|
||||
dark = initSystemDarkMode()
|
||||
}
|
||||
return dark
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* 系统模式暗黑模式策略
|
||||
* @returns {boolean}
|
||||
*/
|
||||
function initSystemDarkMode() {
|
||||
let media = window.matchMedia('(prefers-color-scheme: dark)');
|
||||
let callback = (e) => {
|
||||
let prefersDarkMode = e.matches;
|
||||
setTheme(prefersDarkMode?"dark":"light")
|
||||
};
|
||||
if (typeof media.addEventListener === 'function') {
|
||||
media.addEventListener('change', callback);
|
||||
} else if (typeof media.addListener === 'function') {
|
||||
media.addListener(callback);
|
||||
}
|
||||
return media.matches
|
||||
}
|
||||
|
||||
/**
|
||||
* 配置主题
|
||||
* @param theme 主题
|
||||
*/
|
||||
function setTheme(theme) {
|
||||
Vditor.setContentTheme(theme, THEME_PREFIX)
|
||||
// 兼容 PJAX
|
||||
$(document).on('pjax:complete', function() {
|
||||
vditorRender.render()
|
||||
console.log("[Vditor Render] PJAX END")
|
||||
})
|
||||
// 兼容 Jquery-Pjax
|
||||
$(document).on('pjax:end', function() {
|
||||
vditorRender.render()
|
||||
console.log("[Vditor Render] PJAX END")
|
||||
})
|
||||
console.log("[Vditor Render] PJAX Injected")
|
||||
}
|
|
@ -0,0 +1,27 @@
|
|||
/**
|
||||
This is a theme for halo-plugin-vditor
|
||||
Modified from vditor content_theme
|
||||
*/
|
||||
.vditor-reset .language-abc svg,
|
||||
.vditor-reset .language-abc path {
|
||||
fill: currentColor;
|
||||
color: #d1d5da;
|
||||
}
|
||||
|
||||
.language-graphviz polygon {
|
||||
fill: rgba(66, 133, 244, .36);
|
||||
}
|
||||
|
||||
.vditor-reset .iframe__video {
|
||||
display: block;
|
||||
margin: 0 auto !important;
|
||||
min-width: 80%;
|
||||
min-height: 36vh;
|
||||
border: none;
|
||||
}
|
||||
|
||||
.vditor-reset video {
|
||||
display: block;
|
||||
margin: 0 auto !important;
|
||||
min-width: 80%;
|
||||
}
|
|
@ -0,0 +1,18 @@
|
|||
/**
|
||||
This is a theme for halo-plugin-vditor
|
||||
Modified from vditor content_theme
|
||||
*/
|
||||
|
||||
.vditor-reset .iframe__video {
|
||||
display: block;
|
||||
margin: 0 auto !important;
|
||||
min-width: 80%;
|
||||
min-height: 36vh;
|
||||
border: none;
|
||||
}
|
||||
|
||||
.vditor-reset video {
|
||||
display: block;
|
||||
margin: 0 auto !important;
|
||||
min-width: 80%;
|
||||
}
|
|
@ -0,0 +1,39 @@
|
|||
.vditor-reset--anchor {
|
||||
padding-left: 20px;
|
||||
}
|
||||
.vditor-reset--error {
|
||||
color: #d23f31;
|
||||
font-size: 12px;
|
||||
display: block;
|
||||
line-height: 16px;
|
||||
}
|
||||
|
||||
.vditor-reset .language-math,
|
||||
.vditor-reset .language-echarts,
|
||||
.vditor-reset .language-mindmap,
|
||||
.vditor-reset .language-plantuml,
|
||||
.vditor-reset .language-mermaid,
|
||||
.vditor-reset .language-markmap,
|
||||
.vditor-reset .language-abc,
|
||||
.vditor-reset .language-flowchart,
|
||||
.vditor-reset .language-graphviz {
|
||||
margin-bottom: 16px;
|
||||
}
|
||||
.vditor-reset .language-math mjx-container:focus {
|
||||
outline: none;
|
||||
cursor: context-menu;
|
||||
}
|
||||
.vditor-reset .language-echarts,
|
||||
.vditor-reset .language-mindmap {
|
||||
overflow: hidden;
|
||||
height: 420px;
|
||||
}
|
||||
.vditor-reset .language-mermaid,
|
||||
.vditor-reset .language-markmap,
|
||||
.vditor-reset .language-flowchart,
|
||||
.vditor-reset .language-graphviz {
|
||||
text-align: center;
|
||||
}
|
||||
.vditor-reset .language-graphviz parsererror {
|
||||
overflow: auto;
|
||||
}
|
Loading…
Reference in New Issue