feat: support Mermaid and Katex (#250)

---------

Co-authored-by: hoollyzhang <hoollyzhang@tencent.com>
This commit is contained in:
brzhang Group 2023-11-04 11:40:39 +08:00 committed by GitHub
parent 806c4c086d
commit 192c7878f9
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
9 changed files with 1290 additions and 34 deletions

1235
package-lock.json generated

File diff suppressed because it is too large Load Diff

View File

@ -23,6 +23,7 @@
"form-data": "4.0.0", "form-data": "4.0.0",
"highlight.js": "^11.6.0", "highlight.js": "^11.6.0",
"juice": "^8.0.0", "juice": "^8.0.0",
"katex": "^0.16.9",
"marked": "^4.0.18", "marked": "^4.0.18",
"minio": "7.0.33", "minio": "7.0.33",
"node-fetch": "^3.2.10", "node-fetch": "^3.2.10",

View File

@ -29,6 +29,9 @@
href="https://cdn-doocs.oss-cn-shenzhen.aliyuncs.com/gh/wechatsync/article-syncjs@latest/dist/styles.css" href="https://cdn-doocs.oss-cn-shenzhen.aliyuncs.com/gh/wechatsync/article-syncjs@latest/dist/styles.css"
/> />
<script src="https://cdn-doocs.oss-cn-shenzhen.aliyuncs.com/npm/prettify/r298/prettify.min.js"></script> <script src="https://cdn-doocs.oss-cn-shenzhen.aliyuncs.com/npm/prettify/r298/prettify.min.js"></script>
<!-- KaTeX CSS -->
<link rel="stylesheet" href="https://cdn.jsdelivr.net/npm/katex@0.16.9/dist/katex.min.css" integrity="sha384-n8MVd4RsNIU0tAv4ct0nTaAbDJwPJzDEaqSD1odI+WdtXRGWt2kTvGFasHpSy3SV" crossorigin="anonymous">
</head> </head>
<body> <body>
@ -39,5 +42,11 @@
<!-- built files will be auto injected --> <!-- built files will be auto injected -->
</body> </body>
<script src="https://cdn-doocs.oss-cn-shenzhen.aliyuncs.com/gh/wechatsync/article-syncjs@latest/dist/main.js"></script> <script src="https://cdn-doocs.oss-cn-shenzhen.aliyuncs.com/gh/wechatsync/article-syncjs@latest/dist/main.js"></script>
<!-- 支持一下 mermaid -->
<script type="module">
import mermaid from 'https://cdn.jsdelivr.net/npm/mermaid@10/dist/mermaid.esm.min.mjs';
mermaid.initialize({ startOnLoad: true });
window.mermaid = mermaid;
</script>
</html> </html>

View File

@ -1,5 +1,5 @@
/* /*
按Ctrl+F可格式化 按Ctrl/Command+F可格式化
*/ */
/* 一级标题样式 */ /* 一级标题样式 */
h1 { h1 {

View File

@ -54,6 +54,7 @@ export default {
value: `rgba(250, 81, 81, 1)`, value: `rgba(250, 81, 81, 1)`,
desc: `热情活泼`, desc: `热情活泼`,
}, },
// { label: `微信绿`, value: `rgb(26, 173, 25,1)`, desc: `经典微信绿` },
], ],
codeThemeOption: [ codeThemeOption: [
{ {

View File

@ -1,6 +1,6 @@
import { Renderer } from "marked"; import { Renderer } from "marked";
import hljs from 'highlight.js'; import hljs from "highlight.js";
import katex from "katex";
class WxRenderer { class WxRenderer {
constructor(opts) { constructor(opts) {
this.opts = opts; this.opts = opts;
@ -121,24 +121,37 @@ class WxRenderer {
return `<blockquote ${getStyles("blockquote")}>${text}</blockquote>`; return `<blockquote ${getStyles("blockquote")}>${text}</blockquote>`;
}; };
renderer.code = (text, lang) => { renderer.code = (text, lang) => {
lang = hljs.getLanguage(lang) ? lang : 'plaintext'; if (lang === "katex") {
const html = katex.renderToString(text);
return `${html}`;
}
if (lang.startsWith("mermaid")) {
setTimeout(() => {
window.mermaid?.run();
}, 0);
return `<center><pre class="mermaid">${text}</pre></center>`;
}
lang = hljs.getLanguage(lang) ? lang : "plaintext";
text = hljs.highlight(text, { language: lang }).value; text = hljs.highlight(text, { language: lang }).value;
text = text.replace(/\r\n/g,"<br/>") text = text
.replace(/\r\n/g, "<br/>")
.replace(/\n/g, "<br/>") .replace(/\n/g, "<br/>")
.replace(/(>[^<]+)|(^[^<]+)/g, function (str) { .replace(/(>[^<]+)|(^[^<]+)/g, function (str) {
return str.replace(/\s/g, '&nbsp;') return str.replace(/\s/g, "&nbsp;");
}); });
return `<pre class="hljs code__pre" ${getStyles("code_pre")}><code class="prettyprint language-${lang}" ${getStyles("code")}>${text}</code></pre>` return `<pre class="hljs code__pre" ${getStyles(
"code_pre"
)}><code class="prettyprint language-${lang}" ${getStyles(
"code"
)}>${text}</code></pre>`;
}; };
renderer.codespan = (text, lang) => renderer.codespan = (text, lang) =>
`<code ${getStyles("codespan")}>${text}</code>`; `<code ${getStyles("codespan")}>${text}</code>`;
renderer.listitem = (text) => renderer.listitem = (text) =>
`<li ${getStyles( `<li ${getStyles("listitem")}><span><%s/></span>${text}</li>`;
"listitem"
)}><span><%s/></span>${text}</li>`;
renderer.list = (text, ordered, start) => { renderer.list = (text, ordered, start) => {
text = text.replace(/<\/*p .*?>/g, "").replace(/<\/*p>/g, ""); text = text.replace(/<\/*p .*?>/g, "").replace(/<\/*p>/g, "");

View File

@ -203,27 +203,27 @@ export default {
formatItems: [ formatItems: [
{ {
label: `加粗`, label: `加粗`,
kbd: `Ctrl + B`, kbd: `Ctrl/Command + B`,
emitArgs: [`addFormat`, `**`], emitArgs: [`addFormat`, `**`],
}, },
{ {
label: `斜体`, label: `斜体`,
kbd: `Ctrl + I`, kbd: `Ctrl/Command + I`,
emitArgs: [`addFormat`, `*`], emitArgs: [`addFormat`, `*`],
}, },
{ {
label: `删除线`, label: `删除线`,
kbd: `Alt + Shift + U`, kbd: `Ctrl/Command + D`,
emitArgs: [`addFormat`, `~~`], emitArgs: [`addFormat`, `~~`],
}, },
{ {
label: `超链接`, label: `超链接`,
kbd: `Alt + Shift + K`, kbd: `Ctrl/Command + K`,
emitArgs: [`addFormat`, `[`, `]()`], emitArgs: [`addFormat`, `[`, `]()`],
}, },
{ {
label: `格式化`, label: `格式化`,
kbd: `Alt + Shift + L`, kbd: `Ctrl/Command + F`,
emitArgs: [`formatContent`], emitArgs: [`formatContent`],
}, },
], ],

View File

@ -8,6 +8,10 @@ import DEFAULT_CONTENT from '@/assets/example/markdown.md'
import DEFAULT_CSS_CONTENT from '@/assets/example/theme-css.txt' import DEFAULT_CSS_CONTENT from '@/assets/example/theme-css.txt'
import { setColor, formatDoc, formatCss } from '@/assets/scripts/util' import { setColor, formatDoc, formatCss } from '@/assets/scripts/util'
const defaultKeyMap = CodeMirror.keyMap[`default`]
const modPrefix =
defaultKeyMap === CodeMirror.keyMap[`macDefault`] ? `Cmd` : `Ctrl`
export const useStore = defineStore(`store`, { export const useStore = defineStore(`store`, {
state: () => ({ state: () => ({
wxRenderer: null, wxRenderer: null,
@ -88,9 +92,6 @@ export const useStore = defineStore(`store`, {
editorDom.value = editorDom.value =
localStorage.getItem(`__editor_content`) || formatDoc(DEFAULT_CONTENT) localStorage.getItem(`__editor_content`) || formatDoc(DEFAULT_CONTENT)
} }
const defaultKeyMap = CodeMirror.keyMap[`default`]
const modPrefix =
defaultKeyMap === CodeMirror.keyMap[`macDefault`] ? `Cmd` : `Ctrl`
this.editor = CodeMirror.fromTextArea(editorDom, { this.editor = CodeMirror.fromTextArea(editorDom, {
mode: `text/x-markdown`, mode: `text/x-markdown`,
theme: `xq-light`, theme: `xq-light`,
@ -104,7 +105,7 @@ export const useStore = defineStore(`store`, {
localStorage.setItem(`__editor_content`, doc) localStorage.setItem(`__editor_content`, doc)
editor.setValue(doc) editor.setValue(doc)
}, },
[[`${modPrefix}-B`]]: function bold(editor) { [`${modPrefix}-B`]: function bold(editor) {
const selected = editor.getSelection() const selected = editor.getSelection()
editor.replaceSelection(`**${selected}**`) editor.replaceSelection(`**${selected}**`)
}, },
@ -116,6 +117,10 @@ export const useStore = defineStore(`store`, {
const selected = editor.getSelection() const selected = editor.getSelection()
editor.replaceSelection(`*${selected}*`) editor.replaceSelection(`*${selected}*`)
}, },
[`${modPrefix}-K`]: function italic(editor) {
const selected = editor.getSelection()
editor.replaceSelection(`[${selected}]()`)
},
[`${modPrefix}-L`]: function code(editor) { [`${modPrefix}-L`]: function code(editor) {
const selected = editor.getSelection() const selected = editor.getSelection()
editor.replaceSelection(`\`${selected}\``) editor.replaceSelection(`\`${selected}\``)
@ -138,12 +143,12 @@ export const useStore = defineStore(`store`, {
matchBrackets: true, matchBrackets: true,
autofocus: true, autofocus: true,
extraKeys: { extraKeys: {
'Ctrl-F': function autoFormat(editor) { [`${modPrefix}-F`]: function autoFormat(editor) {
const doc = formatCss(editor.getValue(0)) const doc = formatCss(editor.getValue(0))
localStorage.setItem(`__css_content`, doc) localStorage.setItem(`__css_content`, doc)
editor.setValue(doc) editor.setValue(doc)
}, },
'Ctrl-S': function save(editor) {}, [`${modPrefix}-S`]: function save(editor) {},
}, },
}) })
}, },

View File

@ -1,11 +1,5 @@
<template> <template>
<div <div class="container" :class="{ container_night: nightMode }">
class="container"
:class="{ container_night: nightMode }"
@keydown.alt.shift.k="addFormat('[', ']()')"
@keydown.alt.shift.u="addFormat('~~')"
@keydown.ctrl.alt.l="formatContent()"
>
<el-container> <el-container>
<el-header class="editor__header"> <el-header class="editor__header">
<editor-header <editor-header