diff --git a/README.md b/README.md index f22a526c1..76a708e35 100644 --- a/README.md +++ b/README.md @@ -54,7 +54,7 @@ Vditor 在这些方面做了努力,希望能为现代化的通用 Markdown 编 ## ✨ 特性 * 支持三种编辑模式:所见即所得(wysiwyg)、即时渲染(ir)、分屏预览(sv) -* 支持大纲、数学公式、脑图、图表、流程图、甘特图、时序图、五线谱、[多媒体](https://ld246.com/article/1589813914768)、语音阅读、标题锚点、代码高亮及复制、graphviz 渲染 +* 支持大纲、数学公式、脑图、图表、流程图、甘特图、时序图、五线谱、[多媒体](https://ld246.com/article/1589813914768)、语音阅读、标题锚点、代码高亮及复制、graphviz 渲染、[plantuml](https://plantuml.com)UML图 * 内置安全过滤、导出、图片懒加载、任务列表、多平台预览、多主题切换、复制到微信公众号/知乎功能 * 实现 CommonMark 和 GFM 规范,可对 Markdown 进行格式化和语法树查看,并支持[10+项](https://ld246.com/article/1549638745630#options-preview-markdown)配置 * 工具栏包含 36+ 项操作,除支持扩展外还可对每一项中的[快捷键](https://ld246.com/article/1582778815353)、提示、提示位置、图标、点击事件、类名、子工具栏进行自定义 diff --git a/demo/index.js b/demo/index.js index a18721646..f6f8d248e 100644 --- a/demo/index.js +++ b/demo/index.js @@ -50,8 +50,8 @@ if (window.innerWidth < 768) { } window.vditor = new Vditor('vditor', { - _lutePath: `http://192.168.0.107:9090/lute.min.js?${new Date().getTime()}`, - // _lutePath: 'src/js/lute/lute.min.js', + //_lutePath: `http://192.168.0.107:9090/lute.min.js?${new Date().getTime()}`, + _lutePath: 'src/js/lute/lute.min.js', toolbar, mode: 'wysiwyg', height: window.innerHeight + 100, diff --git a/demo/markdown/zh_CN.md b/demo/markdown/zh_CN.md index cf4d4f7b0..62b86a5e7 100644 --- a/demo/markdown/zh_CN.md +++ b/demo/markdown/zh_CN.md @@ -223,6 +223,21 @@ $$ - 快捷键 ``` +### plantuml + +```plantuml +@startuml component +actor client +node app +database db + +db -> app +app -> client +@enduml +``` + +更多图形参考[https://plantuml.com/zh/](https://plantuml.com/zh/) + ### 流程图 ```mermaid diff --git a/src/assets/scss/_reset.scss b/src/assets/scss/_reset.scss index 3e2eee069..01cb63d24 100644 --- a/src/assets/scss/_reset.scss +++ b/src/assets/scss/_reset.scss @@ -214,6 +214,7 @@ .language-math, .language-echarts, .language-mindmap, + .language-plantuml, .language-mermaid, .language-abc, .language-flowchart, @@ -227,6 +228,7 @@ } .language-echarts, + .language-plantuml, .language-mindmap { overflow: hidden; height: 420px; diff --git a/src/method.ts b/src/method.ts index cc1ec0606..7d730eee2 100644 --- a/src/method.ts +++ b/src/method.ts @@ -9,6 +9,7 @@ import {mathRender} from "./ts/markdown/mathRender"; import {mediaRender} from "./ts/markdown/mediaRender"; import {mermaidRender} from "./ts/markdown/mermaidRender"; import {mindmapRender} from "./ts/markdown/mindmapRender"; +import {plantumlRender} from "./ts/markdown/plantumlRender"; import {outlineRender} from "./ts/markdown/outlineRender"; import {md2html, previewRender} from "./ts/markdown/previewRender"; import {speechRender} from "./ts/markdown/speechRender"; @@ -37,6 +38,8 @@ class Vditor { public static abcRender = abcRender; /** 脑图渲染 */ public static mindmapRender = mindmapRender; + /** plantuml渲染 */ + public static plantumlRender = plantumlRender; /** 大纲渲染 */ public static outlineRender = outlineRender; /** 为[特定链接](https://github.com/Vanessa219/vditor/issues/7)分别渲染为视频、音频、嵌入的 iframe */ diff --git a/src/ts/constants.ts b/src/ts/constants.ts index 617299930..5a60ad2d4 100644 --- a/src/ts/constants.ts +++ b/src/ts/constants.ts @@ -16,7 +16,7 @@ export abstract class Constants { "monokailight", "murphy", "native", "paraiso-dark", "paraiso-light", "pastie", "perldoc", "pygments", "rainbow_dash", "rrt", "solarized-dark", "solarized-dark256", "solarized-light", "swapoff", "tango", "trac", "vim", "vs", "xcode", "ant-design"]; - public static readonly CODE_LANGUAGES: string[] = ["mermaid", "echarts", "mindmap", "abc", "graphviz", "flowchart", "apache", + public static readonly CODE_LANGUAGES: string[] = ["mermaid", "echarts", "mindmap", "plantuml", "abc", "graphviz", "flowchart", "apache", "bash", "cs", "cpp", "css", "coffeescript", "diff", "xml", "http", "ini", "json", "java", "javascript", "js", "makefile", "markdown", "nginx", "objectivec", "php", "perl", "properties", "python", "ruby", "sql", "shell", "dart", "erb", "go", "gradle", "julia", "kotlin", "less", "lua", "matlab", "rust", "scss", "typescript", "ts", diff --git a/src/ts/markdown/codeRender.ts b/src/ts/markdown/codeRender.ts index 3dd0ea2de..f45a93c62 100644 --- a/src/ts/markdown/codeRender.ts +++ b/src/ts/markdown/codeRender.ts @@ -9,6 +9,7 @@ export const codeRender = (element: HTMLElement, lang: keyof II18n = "zh_CN") => } if (e.classList.contains("language-mermaid") || e.classList.contains("language-flowchart") || e.classList.contains("language-echarts") || e.classList.contains("language-mindmap") || + e.classList.contains("language-plantuml") || e.classList.contains("language-abc") || e.classList.contains("language-graphviz") || e.classList.contains("language-math") ) { return; diff --git a/src/ts/markdown/highlightRender.ts b/src/ts/markdown/highlightRender.ts index 605c348a9..b14402baa 100644 --- a/src/ts/markdown/highlightRender.ts +++ b/src/ts/markdown/highlightRender.ts @@ -38,6 +38,7 @@ export const highlightRender = (hljsOption?: IHljs, element: HTMLElement | Docum if (block.classList.contains("language-mermaid") || block.classList.contains("language-flowchat") || block.classList.contains("language-echarts") || block.classList.contains("language-mindmap") || + block.classList.contains("language-plantuml") || block.classList.contains("language-abc") || block.classList.contains("language-graphviz") || block.classList.contains("language-math")) { return; diff --git a/src/ts/markdown/plantumlRender.ts b/src/ts/markdown/plantumlRender.ts new file mode 100644 index 000000000..b3f928f6b --- /dev/null +++ b/src/ts/markdown/plantumlRender.ts @@ -0,0 +1,42 @@ +import {Constants} from "../constants"; +import {addScript} from "../util/addScript"; + +declare const plantumlEncoder: { + encode(options: string): string, +}; + +export const plantumlRender = (element: HTMLElement, cdn = Constants.CDN, theme: string) => { + const plantumlElements = element.querySelectorAll(".language-plantuml"); + if (plantumlElements.length === 0) { + return; + } + addScript(`https://cdn.jsdelivr.net/gh/jmnote/plantuml-encoder@1.2.4/dist/plantuml-encoder.min.js`, "vditorPlantumlEncoderScript").then(() => { + plantumlElements.forEach((e: HTMLDivElement) => { + if (e.parentElement.classList.contains("vditor-wysiwyg__pre") || + e.parentElement.classList.contains("vditor-ir__marker--pre")) { + return; + } + const text = e.innerText.trim(); + if (!text) { + return; + } + try { + if (e.getAttribute("data-processed") === "true") { + return; + } + + const encoded = plantumlEncoder.encode(text) + const imageElement = document.createElement("IMG"); + imageElement.setAttribute("loading", "lazy") + imageElement.setAttribute("src", 'http://www.plantuml.com/plantuml/svg/~1' + encoded) + e.parentNode.insertBefore(imageElement, e); + e.style.display = 'none'; + + e.setAttribute("data-processed", "true"); + } catch (error) { + e.className = "vditor-reset--error"; + e.innerHTML = `plantuml render error:
${error}`; + } + }); + }); +}; diff --git a/src/ts/markdown/previewRender.ts b/src/ts/markdown/previewRender.ts index 59c5001ec..fe560a125 100644 --- a/src/ts/markdown/previewRender.ts +++ b/src/ts/markdown/previewRender.ts @@ -15,6 +15,7 @@ import {mathRender} from "./mathRender"; import {mediaRender} from "./mediaRender"; import {mermaidRender} from "./mermaidRender"; import {mindmapRender} from "./mindmapRender"; +import {plantumlRender} from "./plantumlRender"; import {setLute} from "./setLute"; import {speechRender} from "./speechRender"; @@ -94,6 +95,7 @@ export const previewRender = async (previewElement: HTMLDivElement, markdown: st graphvizRender(previewElement, mergedOptions.cdn); chartRender(previewElement, mergedOptions.cdn, mergedOptions.mode); mindmapRender(previewElement, mergedOptions.cdn, mergedOptions.mode); + plantumlRender(previewElement, mergedOptions.cdn, mergedOptions.mode); abcRender(previewElement, mergedOptions.cdn); mediaRender(previewElement); if (mergedOptions.speech.enable) { diff --git a/src/ts/preview/index.ts b/src/ts/preview/index.ts index d6176e8e4..a1695a3cf 100644 --- a/src/ts/preview/index.ts +++ b/src/ts/preview/index.ts @@ -10,6 +10,7 @@ import {mathRender} from "../markdown/mathRender"; import {mediaRender} from "../markdown/mediaRender"; import {mermaidRender} from "../markdown/mermaidRender"; import {mindmapRender} from "../markdown/mindmapRender"; +import {plantumlRender} from "../markdown/plantumlRender"; import {getEventName} from "../util/compatibility"; import {hasClosestByClassName, hasClosestByMatchTag} from "../util/hasClosest"; import {hasClosestByTag} from "../util/hasClosestByHeadings"; @@ -210,6 +211,7 @@ export class Preview { graphvizRender(vditor.preview.element.lastElementChild as HTMLElement, vditor.options.cdn); chartRender(vditor.preview.element.lastElementChild as HTMLElement, vditor.options.cdn, vditor.options.theme); mindmapRender(vditor.preview.element.lastElementChild as HTMLElement, vditor.options.cdn, vditor.options.theme); + plantumlRender(vditor.preview.element.lastElementChild as HTMLElement, vditor.options.cdn, vditor.options.theme); abcRender(vditor.preview.element.lastElementChild as HTMLElement, vditor.options.cdn); mediaRender(vditor.preview.element.lastElementChild as HTMLElement); // toc render diff --git a/src/ts/undo/index.ts b/src/ts/undo/index.ts index cb259a83c..41e952590 100644 --- a/src/ts/undo/index.ts +++ b/src/ts/undo/index.ts @@ -227,6 +227,7 @@ class Undo { cloneElement.querySelectorAll(`.vditor-${vditor.currentMode}__preview[data-render='1']`) .forEach((item: HTMLElement) => { if (item.firstElementChild.classList.contains("language-echarts") || + item.firstElementChild.classList.contains("language-plantuml") || item.firstElementChild.classList.contains("language-mindmap")) { item.firstElementChild.removeAttribute("_echarts_instance_"); item.firstElementChild.removeAttribute("data-processed"); diff --git a/src/ts/util/processCode.ts b/src/ts/util/processCode.ts index eb006e652..9d7dc4568 100644 --- a/src/ts/util/processCode.ts +++ b/src/ts/util/processCode.ts @@ -7,6 +7,7 @@ import {highlightRender} from "../markdown/highlightRender"; import {mathRender} from "../markdown/mathRender"; import {mermaidRender} from "../markdown/mermaidRender"; import {mindmapRender} from "../markdown/mindmapRender"; +import {plantumlRender} from "../markdown/plantumlRender"; export const processPasteCode = (html: string, text: string, type = "sv") => { const tempElement = document.createElement("div"); @@ -74,6 +75,8 @@ export const processCodeRender = (previewPanel: HTMLElement, vditor: IVditor) => chartRender(previewPanel, vditor.options.cdn, vditor.options.theme); } else if (language === "mindmap") { mindmapRender(previewPanel, vditor.options.cdn, vditor.options.theme); + } else if (language === "plantuml") { + plantumlRender(previewPanel, vditor.options.cdn, vditor.options.theme); } else if (language === "graphviz") { graphvizRender(previewPanel, vditor.options.cdn); } else if (language === "math") {