From a65c86e2eee8450b3218bb5adc6759239730678d Mon Sep 17 00:00:00 2001
From: Libin YANG
Date: Thu, 22 Aug 2024 19:22:25 +0800
Subject: [PATCH] refactor: update renderer (#346)
---
index.html | 15 +-
.../CodemirrorEditor/UploadImgDialog.vue | 4 +-
src/stores/index.js | 5 +-
src/utils/wx-renderer.js | 421 +++++++++---------
src/views/CodemirrorEditor.vue | 27 +-
5 files changed, 222 insertions(+), 250 deletions(-)
diff --git a/index.html b/index.html
index d0105fc..2876afd 100644
--- a/index.html
+++ b/index.html
@@ -3,23 +3,14 @@
-
-
+
+
微信 Markdown 编辑器 | Doocs 开源社区
-
+
{
// 更新编辑器
const editorRefresh = () => {
codeThemeChange()
-
- const renderer = wxRenderer.getRenderer(isCiteStatus.value)
+ const renderer = wxRenderer
+ renderer.reset()
+ renderer.setOptions({ status: isCiteStatus.value, legend: legend.value })
marked.setOptions({ renderer })
let outputTemp = marked.parse(editor.value.getValue(0))
diff --git a/src/utils/wx-renderer.js b/src/utils/wx-renderer.js
index 9a4b39d..16108e1 100644
--- a/src/utils/wx-renderer.js
+++ b/src/utils/wx-renderer.js
@@ -2,230 +2,211 @@ import { Renderer, marked } from 'marked'
import hljs from 'highlight.js'
import markedKatex from 'marked-katex-extension'
-marked.use(markedKatex({
- throwOnError: false,
- output: `html`,
- nonStandard: true,
-}))
+marked.use(
+ markedKatex({
+ throwOnError: false,
+ output: `html`,
+ nonStandard: true,
+ }),
+)
-class WxRenderer {
+class WxRenderer extends Renderer {
constructor(opts) {
+ super()
this.opts = opts
- let footnotes = []
- let footnoteIndex = 0
- let styleMapping = new Map()
-
- const merge = (base, extend) => Object.assign({}, base, extend)
-
- this.buildTheme = (themeTpl) => {
- const mapping = {}
- const base = merge(themeTpl.BASE, {
- 'font-family': this.opts.fonts,
- 'font-size': this.opts.size,
- })
- for (const ele in themeTpl.inline) {
- if (Object.prototype.hasOwnProperty.call(themeTpl.inline, ele)) {
- const style = themeTpl.inline[ele]
- mapping[ele] = merge(themeTpl.BASE, style)
- }
- }
-
- const base_block = merge(base, {})
- for (const ele in themeTpl.block) {
- if (Object.prototype.hasOwnProperty.call(themeTpl.block, ele)) {
- const style = themeTpl.block[ele]
- mapping[ele] = merge(base_block, style)
- }
- }
- return mapping
- }
-
- const getStyles = (tokenName, addition) => {
- const arr = []
- const dict = styleMapping[tokenName]
- if (!dict)
- return ``
- for (const key in dict) {
- arr.push(`${key}:${dict[key]}`)
- }
- return `style="${arr.join(`;`) + (addition || ``)}"`
- }
-
- const addFootnote = (title, link) => {
- footnotes.push([++footnoteIndex, title, link])
- return footnoteIndex
- }
-
- this.buildFootnotes = () => {
- const footnoteArray = footnotes.map((x) => {
- if (x[1] === x[2]) {
- return `[${x[0]}]
: ${x[1]}
`
- }
- return `[${x[0]}]
${x[1]}: ${x[2]}
`
- })
- if (!footnoteArray.length) {
- return ``
- }
- return `引用链接
${footnoteArray.join(`\n`)}
`
- }
-
- this.buildAddition = () => {
- return `
-
- `
- }
-
- this.setOptions = (newOpts) => {
- this.opts = merge(this.opts, newOpts)
- }
-
- this.hasFootnotes = () => footnotes.length !== 0
-
- this.getRenderer = (status) => {
- footnotes = []
- footnoteIndex = 0
-
- styleMapping = this.buildTheme(this.opts.theme)
- const renderer = new Renderer()
-
- renderer.heading = (text, level) => {
- switch (level) {
- case 1:
- return `${text}
`
- case 2:
- return `${text}
`
- case 3:
- return `${text}
`
- default:
- return `${text}
`
- }
- }
- renderer.paragraph = (text) => {
- if (text.includes(`
`
- }
-
- renderer.blockquote = (text) => {
- text = text.replace(//g, ``)
- return `
${text}
`
- }
- renderer.code = (text, lang = ``) => {
- if (lang.startsWith(`mermaid`)) {
- setTimeout(() => {
- window.mermaid?.run()
- }, 0)
- return `${text}
`
- }
- lang = lang.split(` `)[0]
- lang = hljs.getLanguage(lang) ? lang : `plaintext`
- text = hljs.highlight(text, { language: lang }).value
- text = text
- .replace(/\r\n/g, `
`)
- .replace(/\n/g, `
`)
- .replace(/(>[^<]+)|(^[^<]+)/g, (str) => {
- return str.replace(/\s/g, ` `)
- })
-
- return `${text}
`
- }
- renderer.codespan = (text, _) =>
- `${text}
`
- renderer.listitem = text =>
- `<%s/>${text}`
-
- renderer.list = (text, ordered, _) => {
- text = text.replace(/<\/*p .*?>/g, ``).replace(/<\/*p>/g, ``)
- const segments = text.split(`<%s/>`)
- if (!ordered) {
- text = segments.join(`• `)
- return ``
- }
- text = segments[0]
- for (let i = 1; i < segments.length; i++) {
- text = `${text + i}. ${segments[i]}`
- }
- return `${text}
`
- }
- renderer.image = (href, title, text) => {
- const createSubText = (s) => {
- if (!s) {
- return ``
- }
-
- return `${s}`
- }
- const transform = (title, alt) => {
- const legend = localStorage.getItem(`legend`)
- switch (legend) {
- case `alt`:
- return alt
- case `title`:
- return title
- case `alt-title`:
- return alt || title
- case `title-alt`:
- return title || alt
- default:
- return ``
- }
- }
- const subText = createSubText(transform(title, text))
- const figureStyles = getStyles(`figure`)
- const imgStyles = getStyles(`image`)
- return ``
- }
- renderer.link = (href, title, text) => {
- if (href.startsWith(`https://mp.weixin.qq.com`)) {
- return `${text}`
- }
- if (href === text) {
- return text
- }
- if (status) {
- const ref = addFootnote(title || text, href)
- return `${text}[${ref}]`
- }
- return `${text}`
- }
- renderer.strong = text =>
- `${text}`
- renderer.em = text =>
- `${text}`
- renderer.table = (header, body) =>
- ``
- renderer.tablecell = (text, _) =>
- `${text} | `
- renderer.hr = () => `
`
- return renderer
- }
+ this.footnotes = []
+ this.footnoteIndex = 0
+ this.styleMapping = this.buildTheme(opts.theme)
}
+
+ reset = () => {
+ this.footnotes = []
+ this.footnoteIndex = 0
+ }
+
+ merge = (base, extend) => ({ ...base, ...extend })
+
+ buildTheme = (themeTpl) => {
+ const base = this.merge(themeTpl.BASE, {
+ 'font-family': this.opts.fonts,
+ 'font-size': this.opts.size,
+ })
+
+ const mapping = {
+ ...Object.fromEntries(
+ Object.entries(themeTpl.inline).map(([ele, style]) => [
+ ele,
+ this.merge(base, style),
+ ]),
+ ),
+ ...Object.fromEntries(
+ Object.entries(themeTpl.block).map(([ele, style]) => [
+ ele,
+ this.merge(base, style),
+ ]),
+ ),
+ }
+
+ return mapping
+ }
+
+ getStyles = (tokenName, addition = ``) => {
+ const dict = this.styleMapping[tokenName]
+ if (!dict)
+ return ``
+ const styles = Object.entries(dict)
+ .map(([key, value]) => `${key}:${value}`)
+ .join(`;`)
+ return `style="${styles}${addition}"`
+ }
+
+ addFootnote = (title, link) => {
+ this.footnotes.push([++this.footnoteIndex, title, link])
+ return this.footnoteIndex
+ }
+
+ buildFootnotes = () => {
+ if (!this.footnotes.length)
+ return ``
+ const footnoteArray = this.footnotes
+ .map(([index, title, link]) =>
+ link === title
+ ? `[${index}]
: ${title}
`
+ : `[${index}]
${title}: ${link}
`,
+ )
+ .join(`\n`)
+ return `引用链接
${footnoteArray}
`
+ }
+
+ buildAddition = () => `
+
+ `
+
+ setOptions = (newOpts) => {
+ this.opts = this.merge(this.opts, newOpts)
+ this.styleMapping = this.buildTheme(this.opts.theme)
+ }
+
+ heading = (text, level) => {
+ const tag = `h${level}`
+ return `<${tag} ${this.getStyles(tag)}>${text}${tag}>`
+ }
+
+ paragraph = (text) => {
+ const isFigureImage = text.includes(`