feat: 支持解析 plantUML

This commit is contained in:
Frankie Huang 2025-05-02 16:35:35 +08:00
parent a8c8a72bd6
commit bb7f6e2772
3 changed files with 59 additions and 2 deletions

7
package-lock.json generated
View File

@ -14,6 +14,7 @@
"@tauri-apps/plugin-opener": "^2",
"@vavt/v3-extension": "^3.0.0",
"md-editor-v3": "^5.5.0",
"plantuml-encoder": "^1.4.0",
"scriptjs": "^2.5.9",
"view-ui-plus": "^1.3.19",
"vue": "^3.5.13"
@ -2091,6 +2092,12 @@
"integrity": "sha512-xceH2snhtb5M9liqDsmEw56le376mTZkEX/jEb/RxNFyegNul7eNslCXP9FDj/Lcu0X8KEyMceP2ntpaHrDEVA==",
"license": "ISC"
},
"node_modules/plantuml-encoder": {
"version": "1.4.0",
"resolved": "https://registry.npmjs.org/plantuml-encoder/-/plantuml-encoder-1.4.0.tgz",
"integrity": "sha512-sxMwpDw/ySY1WB2CE3+IdMuEcWibJ72DDOsXLkSmEaSzwEUaYBT6DWgOfBiHGCux4q433X6+OEFWjlVqp7gL6g==",
"license": "MIT"
},
"node_modules/popper.js": {
"version": "1.16.1",
"resolved": "https://registry.npmjs.org/popper.js/-/popper.js-1.16.1.tgz",

View File

@ -16,6 +16,7 @@
"@tauri-apps/plugin-opener": "^2",
"@vavt/v3-extension": "^3.0.0",
"md-editor-v3": "^5.5.0",
"plantuml-encoder": "^1.4.0",
"scriptjs": "^2.5.9",
"view-ui-plus": "^1.3.19",
"vue": "^3.5.13"

View File

@ -3,8 +3,8 @@
<template #left>
<MdEditor ref="editorRef" v-model="editorState.text" :id="editorState.id" :theme="editorState.theme"
:previewTheme="editorState.previewTheme" :style="{ height: editorState.height }"
:toolbars="editorState.toolbars" :toolbarsExclude="editorState.toolbarsExclude" @onSave="onSave"
@onHtmlChanged="getPreviewedHTML" @onUploadImg="onUploadImg">
:toolbars="editorState.toolbars" :toolbarsExclude="editorState.toolbarsExclude" :sanitize="sanitize"
@onSave="onSave" @onHtmlChanged="getPreviewedHTML" @onUploadImg="onUploadImg">
<template #defToolbars>
<NormalToolbar :title="leftSidebarState == 'open' ? '隐藏文件树' : '显示文件树'" @onClick="toggleLeftSideBar">
<template #trigger>
@ -55,6 +55,7 @@ import { ThemeSwitch, PreviewThemeSwitch, ExportPDF } from '@vavt/v3-extension';
import { lineNumbers } from '@codemirror/view';
import { ref, reactive, watch, nextTick, onMounted } from "vue";
import { Message } from 'view-ui-plus'
import { encode as plantumlEncoder } from 'plantuml-encoder';
// fetchScript JS
const fetchScript = (url) => new Promise((resolve) => scriptjs(url, () => resolve()));
@ -188,6 +189,54 @@ const toggleCatalog = () => {
}
}
const sanitize = (html) => {
// XSS 使 sanitize-html html
// !!! <h>
// html = sanitizeHtml(html);
// PlantUML
html = html.replace(/@startuml([\s\S]*?)@enduml/g, (_, umlCode) => {
const lines = umlCode.split('\n');
//
if (lines[0] == '<br>') {
lines.shift();
}
if (/<p\s+[^>]*>/i.test(lines[lines.length - 1])) {
lines.pop();
}
const removeLineBreak = /(.*?)(?=<br>|<\/p>)/i;
const extractedLines = lines.map(line => {
// <p data-line="2">text<br>
line = line.trim().replace(/<\w+\s+[^>]*>(.*?)<[^>]*>/i, '$1');
//
let match = line.match(removeLineBreak);
line = match ? match[1].trim() : line;
// HTML &gt; >
const decodedText = decodeHTMLEntities(line);
return decodedText;
});
extractedLines.unshift('@startuml');
extractedLines.push('@enduml');
const plantUMLCode = extractedLines.join('\n');
console.log(plantUMLCode);
// PlantUML
const encoded = plantumlEncoder(plantUMLCode);
return `<img class="plantuml-diagram"
src="https://www.plantuml.com/plantuml/svg/${encoded}"
alt="PlantUML Diagram">`;
});
return html;
};
const decodeHTMLEntities = (text) => {
const textArea = document.createElement('textarea');
textArea.innerHTML = text;
return textArea.value;
};
const onSave = (v, h) => {
// save v markdown code
props.save(v);