From 22254b6d3cce3f066011f77876c83fabbd00a102 Mon Sep 17 00:00:00 2001
From: yanglbme
Date: Thu, 22 Aug 2024 16:45:11 +0800
Subject: [PATCH 1/2] refactor: update renderer
---
index.html | 15 +-
.../CodemirrorEditor/UploadImgDialog.vue | 4 +-
src/stores/index.js | 5 +-
src/utils/wx-renderer.js | 370 ++++++++----------
src/views/CodemirrorEditor.vue | 27 +-
5 files changed, 183 insertions(+), 238 deletions(-)
diff --git a/index.html b/index.html
index d0105fc02..2876afd1c 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 })
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 9a4b39d5e..ec947ca19 100644
--- a/src/utils/wx-renderer.js
+++ b/src/utils/wx-renderer.js
@@ -2,230 +2,184 @@ 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,
-}))
-
-class WxRenderer {
+marked.use(
+ markedKatex({
+ throwOnError: false,
+ output: `html`,
+ nonStandard: true,
+ }),
+)
+
+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.footnotes = []
+ this.footnoteIndex = 0
+ this.styleMapping = this.buildTheme(opts.theme)
+ }
- 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)
- }
- }
+ reset = () => {
+ this.footnotes = []
+ this.footnoteIndex = 0
+ }
- 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
+ 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),
+ ]),
+ ),
}
- 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 || ``)}"`
- }
+ return mapping
+ }
- const addFootnote = (title, link) => {
- footnotes.push([++footnoteIndex, title, link])
- return footnoteIndex
- }
+ 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}"`
+ }
- 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 ``
+ 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 = () => `
+
+ `
- this.buildAddition = () => {
- return `
-
- `
- }
+ 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 =>
+ text.includes(``)
+ ? text
+ : `${text}
`
+
+ blockquote = (text) => {
+ text = text.replace(//g, ``)
+ return `
${text}
`
+ }
- this.setOptions = (newOpts) => {
- this.opts = merge(this.opts, newOpts)
+ code = (text, lang = ``) => {
+ if (lang.startsWith(`mermaid`)) {
+ setTimeout(() => {
+ window.mermaid?.run()
+ }, 0)
+ return `${text}
`
}
+ const langText = lang.split(` `)[0]
+ const language = hljs.getLanguage(langText) ? langText : `plaintext`
+ text = hljs.highlight(text, { language }).value
+ text = text
+ .replace(/\r\n/g, `
`)
+ .replace(/\n/g, `
`)
+ .replace(/(>[^<]+)|(^[^<]+)/g, (str) => {
+ return str.replace(/\s/g, ` `)
+ })
- 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(`${text}
`
- }
+ return `${text}
`
+ }
- 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 `
${subText}`
- }
- 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
+ image = (href, title, text) => {
+ const subText = `${
+ title || text
+ }`
+ return `
${subText}`
+ }
+
+ link = (href, title, text) => {
+ if (href.startsWith(`https://mp.weixin.qq.com`)) {
+ return `${text}`
+ }
+ if (href === text)
+ return text
+ if (this.opts.status) {
+ const ref = this.addFootnote(title || text, href)
+ return `${text}[${ref}]`
}
+ return `${text}`
}
+
+ strong = text => `${text}`
+
+ em = text => `${text}`
+
+ table = (header, body) => `
+ `
+
+ tablecell = text => `${text} | `
+
+ hr = () => `
`
}
export default WxRenderer
diff --git a/src/views/CodemirrorEditor.vue b/src/views/CodemirrorEditor.vue
index 4429c0140..8b596755b 100644
--- a/src/views/CodemirrorEditor.vue
+++ b/src/views/CodemirrorEditor.vue
@@ -170,7 +170,6 @@ function beforeUpload(file) {
// 图片上传结束
function uploaded(imageUrl) {
- console.log(`图片上传之后: `, imageUrl)
if (!imageUrl) {
ElMessage.error(`上传图片未知异常`)
return
@@ -182,27 +181,27 @@ function uploaded(imageUrl) {
// 将 Markdown 形式的 URL 插入编辑框光标所在位置
toRaw(store.editor).replaceSelection(`\n${markdownImage}\n`, cursor)
ElMessage.success(`图片上传成功`)
- // formatContent()
- // onEditorRefresh()
}
function uploadImage(file, cb) {
isImgLoading.value = true
+
toBase64(file)
- .then((base64Content) => {
- fileApi
- .fileUpload(base64Content, file)
- .then((url) => {
- console.log(url)
- cb ? cb(url) : uploaded(url)
- })
- .catch((err) => {
- ElMessage.error(err.message)
- })
+ .then(base64Content => fileApi.fileUpload(base64Content, file))
+ .then((url) => {
+ console.log(url)
+ if (cb) {
+ cb(url)
+ }
+ else {
+ uploaded(url)
+ }
})
.catch((err) => {
ElMessage.error(err.message)
})
- isImgLoading.value = false
+ .finally(() => {
+ isImgLoading.value = false
+ })
}
const changeTimer = ref(0)
From 83fabaf18b289a751ec797ea2d06207c7b1e49bb Mon Sep 17 00:00:00 2001
From: yanglbme
Date: Thu, 22 Aug 2024 19:15:19 +0800
Subject: [PATCH 2/2] fix: style
---
src/stores/index.js | 2 +-
src/utils/wx-renderer.js | 49 +++++++++++++++++++++++++++++++---------
2 files changed, 39 insertions(+), 12 deletions(-)
diff --git a/src/stores/index.js b/src/stores/index.js
index df7a3b197..a25744bc6 100644
--- a/src/stores/index.js
+++ b/src/stores/index.js
@@ -89,7 +89,7 @@ export const useStore = defineStore(`store`, () => {
codeThemeChange()
const renderer = wxRenderer
renderer.reset()
- renderer.setOptions({ status: isCiteStatus.value })
+ 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 ec947ca19..16108e18e 100644
--- a/src/utils/wx-renderer.js
+++ b/src/utils/wx-renderer.js
@@ -107,10 +107,11 @@ class WxRenderer extends Renderer {
return `<${tag} ${this.getStyles(tag)}>${text}${tag}>`
}
- paragraph = text =>
- text.includes(``)
- ? text
- : `${text}
`
+ paragraph = (text) => {
+ const isFigureImage = text.includes(`${text}`
+ }
blockquote = (text) => {
text = text.replace(//g, ``)
@@ -141,13 +142,37 @@ class WxRenderer extends Renderer {
)}>${text}`
}
+ codespan = text => `${text}
`
+
+ listitem = text => `
<%s/>${text}`
+
+ list = (text, ordered) => {
+ text = text.replace(/<\/*p.*?>/g, ``).replace(/<\/*p>/g, ``)
+
+ const segments = text.split(`<%s/>`)
+
+ if (!ordered) {
+ return ``
+ }
+
+ const orderedText = segments.map((segment, i) => (i > 0 ? `${i}. ` : ``) + segment).join(``)
+ return `${orderedText}
`
+ }
+
image = (href, title, text) => {
- const subText = `${
- title || text
- }`
- return `
${subText}`
+ const createSubText = s => s ? `${s}` : ``
+ const transform = {
+ 'alt': () => text,
+ 'title': () => title,
+ 'alt-title': () => text || title,
+ 'title-alt': () => title || text,
+ }[this.opts.legend] || (() => ``)
+
+ const subText = createSubText(transform())
+ const figureStyles = this.getStyles(`figure`)
+ const imgStyles = this.getStyles(`image`)
+
+ return `
${subText}`
}
link = (href, title, text) => {
@@ -160,7 +185,9 @@ class WxRenderer extends Renderer {
return text
if (this.opts.status) {
const ref = this.addFootnote(title || text, href)
- return `${text}[${ref}]`
+ return `${text}[${ref}]`
}
return `${text}`
}