feat: support GFM

This commit is contained in:
dribble-njr 2024-11-20 19:26:46 +08:00
parent e946a55f0f
commit 014e4b4aea
8 changed files with 317 additions and 37 deletions

View File

@ -35,6 +35,7 @@ Markdown 文档自动即时渲染为微信图文,让你不再为微信文章
- [x] 支持自定义 CSS 样式 - [x] 支持自定义 CSS 样式
- [x] 支持 Markdown 所有基础语法、代码块、LaTeX 公式 - [x] 支持 Markdown 所有基础语法、代码块、LaTeX 公式
- [x] 支持 [GFM 警告块](https://github.com/orgs/community/discussions/16925)
- [x] 支持浅色、深色两种编辑器外观 - [x] 支持浅色、深色两种编辑器外观
- [x] 支持 <kbd>Alt</kbd> + <kbd>Shift</kbd> + <kbd>F</kbd> 快速格式化文档 - [x] 支持 <kbd>Alt</kbd> + <kbd>Shift</kbd> + <kbd>F</kbd> 快速格式化文档
- [x] 支持色盘取色,快速替换文章整体色调 - [x] 支持色盘取色,快速替换文章整体色调

View File

@ -33,6 +33,27 @@ blockquote {
/* 引用段落样式 */ /* 引用段落样式 */
blockquote_p { blockquote_p {
} }
/* GFM 警告块 */
markdown-alert {
}
/* GFM 警告块标题 */
markdown-alert-title {
}
/* GFM note */
markdown-alert-title-note {
}
/* GFM tip */
markdown-alert-title-tip {
}
/* GFM important */
markdown-alert-title-important {
}
/* GFM warning */
markdown-alert-title-warning {
}
/* GFM caution */
markdown-alert-title-caution {
}
/* 段落样式 */ /* 段落样式 */
p { p {
} }

View File

@ -10,7 +10,7 @@ const defaultTheme: Theme = {
}, },
block: { block: {
// 一级标题 // 一级标题
h1: { 'h1': {
'display': `table`, 'display': `table`,
'padding': `0 1em`, 'padding': `0 1em`,
'border-bottom': `2px solid var(--md-primary-color)`, 'border-bottom': `2px solid var(--md-primary-color)`,
@ -22,7 +22,7 @@ const defaultTheme: Theme = {
}, },
// 二级标题 // 二级标题
h2: { 'h2': {
'display': `table`, 'display': `table`,
'padding': `0 0.2em`, 'padding': `0 0.2em`,
'margin': `4em auto 2em`, 'margin': `4em auto 2em`,
@ -34,7 +34,7 @@ const defaultTheme: Theme = {
}, },
// 三级标题 // 三级标题
h3: { 'h3': {
'padding-left': `8px`, 'padding-left': `8px`,
'border-left': `3px solid var(--md-primary-color)`, 'border-left': `3px solid var(--md-primary-color)`,
'margin': `2em 8px 0.75em 0`, 'margin': `2em 8px 0.75em 0`,
@ -45,7 +45,7 @@ const defaultTheme: Theme = {
}, },
// 四级标题 // 四级标题
h4: { 'h4': {
'margin': `2em 8px 0.5em`, 'margin': `2em 8px 0.5em`,
'color': `var(--md-primary-color)`, 'color': `var(--md-primary-color)`,
'font-size': `1em`, 'font-size': `1em`,
@ -53,7 +53,7 @@ const defaultTheme: Theme = {
}, },
// 五级标题 // 五级标题
h5: { 'h5': {
'margin': `1.5em 8px 0.5em`, 'margin': `1.5em 8px 0.5em`,
'color': `var(--md-primary-color)`, 'color': `var(--md-primary-color)`,
'font-size': `1em`, 'font-size': `1em`,
@ -61,14 +61,14 @@ const defaultTheme: Theme = {
}, },
// 六级标题 // 六级标题
h6: { 'h6': {
'margin': `1.5em 8px 0.5em`, 'margin': `1.5em 8px 0.5em`,
'font-size': `1em`, 'font-size': `1em`,
'color': `var(--md-primary-color)`, 'color': `var(--md-primary-color)`,
}, },
// 段落 // 段落
p: { 'p': {
'margin': `1.5em 8px`, 'margin': `1.5em 8px`,
'letter-spacing': `0.1em`, 'letter-spacing': `0.1em`,
'color': `var(--el-text-color-regular)`, 'color': `var(--el-text-color-regular)`,
@ -76,7 +76,7 @@ const defaultTheme: Theme = {
}, },
// 引用 // 引用
blockquote: { 'blockquote': {
'font-style': `normal`, 'font-style': `normal`,
'border-left': `none`, 'border-left': `none`,
'padding': `1em`, 'padding': `1em`,
@ -87,15 +87,52 @@ const defaultTheme: Theme = {
}, },
// 引用内容 // 引用内容
blockquote_p: { 'blockquote_p': {
'display': `block`, 'display': `block`,
'font-size': `1em`, 'font-size': `1em`,
'letter-spacing': `0.1em`, 'letter-spacing': `0.1em`,
'color': `rgb(80, 80, 80)`, 'color': `rgb(80, 80, 80)`,
}, },
// GFM 警告块
'markdown-alert': {
'font-style': `normal`,
'border-left': `none`,
'padding': `1em`,
'border-radius': `8px`,
'background': `#f7f7f7`,
'margin': `2em 8px`,
'--el-text-color-regular': `rgb(80, 80, 80) !important`,
},
// GFM 警告块标题
'markdown-alert-title': {
'display': `flex`,
'align-items': `center`,
},
'markdown-alert-title-note': {
color: `#478be6`,
},
'markdown-alert-title-tip': {
color: `#57ab5a`,
},
'markdown-alert-title-important': {
color: `#986ee2`,
},
'markdown-alert-title-warning': {
color: `#c69026`,
},
'markdown-alert-title-caution': {
color: `#e5534b`,
},
// 代码块 // 代码块
code_pre: { 'code_pre': {
'font-size': `14px`, 'font-size': `14px`,
'overflow-x': `auto`, 'overflow-x': `auto`,
'border-radius': `8px`, 'border-radius': `8px`,
@ -105,14 +142,14 @@ const defaultTheme: Theme = {
}, },
// 行内代码 // 行内代码
code: { 'code': {
'margin': 0, 'margin': 0,
'white-space': `nowrap`, 'white-space': `nowrap`,
'font-family': `Menlo, Operator Mono, Consolas, Monaco, monospace`, 'font-family': `Menlo, Operator Mono, Consolas, Monaco, monospace`,
}, },
// 图片 // 图片
image: { 'image': {
'display': `block`, 'display': `block`,
'width': `100% !important`, 'width': `100% !important`,
'margin': `0.1em auto 0.5em`, 'margin': `0.1em auto 0.5em`,
@ -120,32 +157,32 @@ const defaultTheme: Theme = {
}, },
// 有序列表 // 有序列表
ol: { 'ol': {
'padding-left': `1em`, 'padding-left': `1em`,
'margin-left': `0`, 'margin-left': `0`,
'color': `var(--el-text-color-regular)`, 'color': `var(--el-text-color-regular)`,
}, },
// 无序列表 // 无序列表
ul: { 'ul': {
'list-style': `circle`, 'list-style': `circle`,
'padding-left': `1em`, 'padding-left': `1em`,
'margin-left': `0`, 'margin-left': `0`,
'color': `var(--el-text-color-regular)`, 'color': `var(--el-text-color-regular)`,
}, },
footnotes: { 'footnotes': {
'margin': `0.5em 8px`, 'margin': `0.5em 8px`,
'font-size': `80%`, 'font-size': `80%`,
'color': `var(--el-text-color-regular)`, 'color': `var(--el-text-color-regular)`,
}, },
figure: { 'figure': {
margin: `1.5em 8px`, margin: `1.5em 8px`,
color: `var(--el-text-color-regular)`, color: `var(--el-text-color-regular)`,
}, },
hr: { 'hr': {
'border-style': `solid`, 'border-style': `solid`,
'border-width': `1px 0 0`, 'border-width': `1px 0 0`,
'border-color': `rgba(0,0,0,0.1)`, 'border-color': `rgba(0,0,0,0.1)`,
@ -230,43 +267,43 @@ const graceTheme = toMerged(defaultTheme, {
base: { base: {
}, },
block: { block: {
h1: { 'h1': {
'padding': `0.5em 1em`, 'padding': `0.5em 1em`,
'border-bottom': `2px solid var(--md-primary-color)`, 'border-bottom': `2px solid var(--md-primary-color)`,
'font-size': `1.4em`, 'font-size': `1.4em`,
'text-shadow': `2px 2px 4px rgba(0,0,0,0.1)`, 'text-shadow': `2px 2px 4px rgba(0,0,0,0.1)`,
}, },
h2: { 'h2': {
'padding': `0.3em 1em`, 'padding': `0.3em 1em`,
'border-radius': `8px`, 'border-radius': `8px`,
'font-size': `1.3em`, 'font-size': `1.3em`,
'box-shadow': `0 4px 6px rgba(0,0,0,0.1)`, 'box-shadow': `0 4px 6px rgba(0,0,0,0.1)`,
}, },
h3: { 'h3': {
'padding-left': `12px`, 'padding-left': `12px`,
'font-size': `1.2em`, 'font-size': `1.2em`,
'border-left': `4px solid var(--md-primary-color)`, 'border-left': `4px solid var(--md-primary-color)`,
'border-bottom': `1px dashed var(--md-primary-color)`, 'border-bottom': `1px dashed var(--md-primary-color)`,
}, },
h4: { 'h4': {
'font-size': `1.1em`, 'font-size': `1.1em`,
}, },
h5: { 'h5': {
'font-size': `1em`, 'font-size': `1em`,
}, },
h6: { 'h6': {
'font-size': `1em`, 'font-size': `1em`,
}, },
p: { 'p': {
}, },
blockquote: { 'blockquote': {
'font-style': `italic`, 'font-style': `italic`,
'padding': `1em 1em 1em 2em`, 'padding': `1em 1em 1em 2em`,
'border-left': `4px solid var(--md-primary-color)`, 'border-left': `4px solid var(--md-primary-color)`,
@ -276,41 +313,51 @@ const graceTheme = toMerged(defaultTheme, {
'box-shadow': `0 4px 6px rgba(0,0,0,0.05)`, 'box-shadow': `0 4px 6px rgba(0,0,0,0.05)`,
}, },
blockquote_p: { 'blockquote_p': {
}, },
code_pre: { 'markdown-alert': {
'font-style': `italic`,
'padding': `1em 1em 1em 2em`,
'border-left': `4px solid var(--md-primary-color)`,
'border-radius': `6px`,
'color': `rgba(0,0,0,0.6)`,
'background': `linear-gradient(to right, #f7f7f7, #ffffff)`,
'box-shadow': `0 4px 6px rgba(0,0,0,0.05)`,
},
'code_pre': {
'box-shadow': `inset 0 0 10px rgba(0,0,0,0.05)`, 'box-shadow': `inset 0 0 10px rgba(0,0,0,0.05)`,
}, },
code: { 'code': {
'white-space': `pre-wrap`, 'white-space': `pre-wrap`,
'font-family': `'Fira Code', Menlo, Operator Mono, Consolas, Monaco, monospace`, 'font-family': `'Fira Code', Menlo, Operator Mono, Consolas, Monaco, monospace`,
}, },
image: { 'image': {
'border-radius': `8px`, 'border-radius': `8px`,
'box-shadow': `0 4px 8px rgba(0,0,0,0.1)`, 'box-shadow': `0 4px 8px rgba(0,0,0,0.1)`,
}, },
ol: { 'ol': {
'padding-left': `1.5em`, 'padding-left': `1.5em`,
}, },
ul: { 'ul': {
'list-style': `none`, 'list-style': `none`,
'padding-left': `1.5em`, 'padding-left': `1.5em`,
}, },
footnotes: { 'footnotes': {
}, },
figure: { 'figure': {
}, },
hr: { 'hr': {
height: `1px`, height: `1px`,
border: `none`, border: `none`,
margin: `2em 0`, margin: `2em 0`,

View File

@ -2,6 +2,7 @@ import DEFAULT_CONTENT from '@/assets/example/markdown.md?raw'
import DEFAULT_CSS_CONTENT from '@/assets/example/theme-css.txt?raw' import DEFAULT_CSS_CONTENT from '@/assets/example/theme-css.txt?raw'
import { altKey, codeBlockThemeOptions, colorOptions, fontFamilyOptions, fontSizeOptions, legendOptions, shiftKey, themeMap, themeOptions } from '@/config' import { altKey, codeBlockThemeOptions, colorOptions, fontFamilyOptions, fontSizeOptions, legendOptions, shiftKey, themeMap, themeOptions } from '@/config'
import { addPrefix, css2json, customCssWithTemplate, customizeTheme, downloadMD, exportHTML, formatDoc } from '@/utils' import { addPrefix, css2json, customCssWithTemplate, customizeTheme, downloadMD, exportHTML, formatDoc } from '@/utils'
import markedAlert from '@/utils/MDAlert'
import { initRenderer } from '@/utils/renderer' import { initRenderer } from '@/utils/renderer'
import { useDark, useStorage, useToggle } from '@vueuse/core' import { useDark, useStorage, useToggle } from '@vueuse/core'
@ -144,6 +145,7 @@ export const useStore = defineStore(`store`, () => {
const editorRefresh = () => { const editorRefresh = () => {
codeThemeChange() codeThemeChange()
renderer.reset({ citeStatus: isCiteStatus.value, legend: legend.value, isUseIndent: isUseIndent.value }) renderer.reset({ citeStatus: isCiteStatus.value, legend: legend.value, isUseIndent: isUseIndent.value })
let outputTemp = marked.parse(editor.value!.getValue()) as string let outputTemp = marked.parse(editor.value!.getValue()) as string
// 去除第一行的 margin-top // 去除第一行的 margin-top
@ -184,6 +186,8 @@ export const useStore = defineStore(`store`, () => {
renderer.setOptions({ renderer.setOptions({
theme: newTheme, theme: newTheme,
}) })
marked.use(markedAlert({ theme: newTheme }))
editorRefresh() editorRefresh()
} }
// 初始化 CSS 编辑器 // 初始化 CSS 编辑器

View File

@ -1,5 +1,7 @@
import type { PropertiesHyphen } from 'csstype' import type { PropertiesHyphen } from 'csstype'
import type { Token } from 'marked'
export type Block = `h1` | `h2` | `h3` | `h4` | `h5` | `h6` | `p` | `blockquote` | `blockquote_p` | `code_pre` | `code` | `image` | `ol` | `ul` | `footnotes` | `figure` | `hr` export type Block = `h1` | `h2` | `h3` | `h4` | `h5` | `h6` | `p` | `blockquote` | `blockquote_p` | `code_pre` | `code` | `image` | `ol` | `ul` | `footnotes` | `figure` | `hr`
export type Inline = `listitem` | `codespan` | `link` | `wx_link` | `strong` | `table` | `thead` | `td` | `footnote` | `figcaption` | `em` export type Inline = `listitem` | `codespan` | `link` | `wx_link` | `strong` | `table` | `thead` | `td` | `footnote` | `figcaption` | `em`
@ -12,8 +14,8 @@ export type ExtendedProperties = PropertiesHyphen & CustomCSSProperties
export interface Theme { export interface Theme {
base: ExtendedProperties base: ExtendedProperties
block: Record<Block, PropertiesHyphen> block: Record<Block | string, ExtendedProperties>
inline: Record<Inline, PropertiesHyphen> inline: Record<Inline | string, ExtendedProperties>
} }
export interface IOpts { export interface IOpts {
@ -32,3 +34,39 @@ export interface IConfigOption<VT = string> {
value: VT value: VT
desc: string desc: string
} }
/**
* Options for the `markedAlert` extension.
*/
export interface AlertOptions {
className?: string
variants?: AlertVariantItem[]
theme?: Theme
}
/**
* Configuration for an alert type.
*/
export interface AlertVariantItem {
type: string
icon: string
title?: string
titleClassName?: string
}
/**
* Represents an alert token.
*/
export interface Alert {
type: `alert`
meta: {
className: string
variant: string
icon: string
title: string
titleClassName: string
}
raw: string
text: string
tokens: Token[]
}

155
src/utils/MDAlert.ts Normal file
View File

@ -0,0 +1,155 @@
import type { AlertOptions, AlertVariantItem } from '@/types'
import type { MarkedExtension, Tokens } from 'marked'
/**
* https://github.com/bent10/marked-extensions/tree/main/packages/alert
* To support theme, we need to modify the source code.
* A [marked](https://marked.js.org/) extension to support [GFM alerts](https://github.com/orgs/community/discussions/16925).
*/
export default function markedAlert(options: AlertOptions = {}): MarkedExtension {
const { className = `markdown-alert`, variants = [] } = options
const resolvedVariants = resolveVariants(variants)
return {
walkTokens(token) {
if (token.type !== `blockquote`)
return
const matchedVariant = resolvedVariants.find(({ type }) =>
new RegExp(createSyntaxPattern(type)).test(token.text),
)
if (matchedVariant) {
const {
type: variantType,
icon,
title = ucfirst(variantType),
titleClassName = `${className}-title`,
} = matchedVariant
const typeRegexp = new RegExp(createSyntaxPattern(variantType))
Object.assign(token, {
type: `alert`,
meta: {
className,
variant: variantType,
icon,
title,
titleClassName,
style: {
...options.theme?.block[className],
...options.theme?.block[`${className}-${variantType}`],
},
titleStyle: {
...options.theme?.block[titleClassName],
...options.theme?.block[`${titleClassName}-${variantType}`],
},
},
})
console.log({
...options.theme?.block[className],
...options.theme?.block[`${className}-${variantType}`],
}, `style`)
const firstLine = token.tokens?.[0] as Tokens.Paragraph
const firstLineText = firstLine.raw?.replace(typeRegexp, ``).trim()
if (firstLineText) {
const patternToken = firstLine.tokens[0] as Tokens.Text
Object.assign(patternToken, {
raw: patternToken.raw.replace(typeRegexp, ``),
text: patternToken.text.replace(typeRegexp, ``),
})
if (firstLine.tokens[1]?.type === `br`) {
firstLine.tokens.splice(1, 1)
}
}
else {
token.tokens?.shift()
}
}
},
extensions: [
{
name: `alert`,
level: `block`,
renderer({ meta, tokens = [] }) {
let tmpl = `<div class="${meta.className} ${meta.className}-${meta.variant}" style='${Object.entries(meta.style ?? {}).map(([key, value]) => `${key}: ${value}`).join(`; `)}'>\n`
tmpl += `<p class="${meta.titleClassName}" style='${Object.entries(meta.titleStyle ?? {}).map(([key, value]) => `${key}: ${value}`).join(`; `)}'>`
tmpl += meta.icon.replace(
`<svg`,
`<svg style="fill: ${meta.titleStyle?.color ?? `inherit`}"`,
)
tmpl += meta.title
tmpl += `</p>\n`
tmpl += this.parser.parse(tokens)
tmpl += `</div>\n`
return tmpl
},
},
],
}
}
/**
* The default configuration for alert variants.
*/
const defaultAlertVariant: AlertVariantItem[] = [
{
type: `note`,
icon: `<svg class="octicon octicon-info mr-2" viewBox="0 0 16 16" width="16" height="16" aria-hidden="true"><path d="M0 8a8 8 0 1 1 16 0A8 8 0 0 1 0 8Zm8-6.5a6.5 6.5 0 1 0 0 13 6.5 6.5 0 0 0 0-13ZM6.5 7.75A.75.75 0 0 1 7.25 7h1a.75.75 0 0 1 .75.75v2.75h.25a.75.75 0 0 1 0 1.5h-2a.75.75 0 0 1 0-1.5h.25v-2h-.25a.75.75 0 0 1-.75-.75ZM8 6a1 1 0 1 1 0-2 1 1 0 0 1 0 2Z"></path></svg>`,
},
{
type: `tip`,
icon: `<svg class="octicon octicon-light-bulb mr-2" viewBox="0 0 16 16" width="16" height="16" aria-hidden="true"><path d="M8 1.5c-2.363 0-4 1.69-4 3.75 0 .984.424 1.625.984 2.304l.214.253c.223.264.47.556.673.848.284.411.537.896.621 1.49a.75.75 0 0 1-1.484.211c-.04-.282-.163-.547-.37-.847a8.456 8.456 0 0 0-.542-.68c-.084-.1-.173-.205-.268-.32C3.201 7.75 2.5 6.766 2.5 5.25 2.5 2.31 4.863 0 8 0s5.5 2.31 5.5 5.25c0 1.516-.701 2.5-1.328 3.259-.095.115-.184.22-.268.319-.207.245-.383.453-.541.681-.208.3-.33.565-.37.847a.751.751 0 0 1-1.485-.212c.084-.593.337-1.078.621-1.489.203-.292.45-.584.673-.848.075-.088.147-.173.213-.253.561-.679.985-1.32.985-2.304 0-2.06-1.637-3.75-4-3.75ZM5.75 12h4.5a.75.75 0 0 1 0 1.5h-4.5a.75.75 0 0 1 0-1.5ZM6 15.25a.75.75 0 0 1 .75-.75h2.5a.75.75 0 0 1 0 1.5h-2.5a.75.75 0 0 1-.75-.75Z"></path></svg>`,
},
{
type: `important`,
icon: `<svg class="octicon octicon-report mr-2" viewBox="0 0 16 16" width="16" height="16" aria-hidden="true"><path d="M0 1.75C0 .784.784 0 1.75 0h12.5C15.216 0 16 .784 16 1.75v9.5A1.75 1.75 0 0 1 14.25 13H8.06l-2.573 2.573A1.458 1.458 0 0 1 3 14.543V13H1.75A1.75 1.75 0 0 1 0 11.25Zm1.75-.25a.25.25 0 0 0-.25.25v9.5c0 .138.112.25.25.25h2a.75.75 0 0 1 .75.75v2.19l2.72-2.72a.749.749 0 0 1 .53-.22h6.5a.25.25 0 0 0 .25-.25v-9.5a.25.25 0 0 0-.25-.25Zm7 2.25v2.5a.75.75 0 0 1-1.5 0v-2.5a.75.75 0 0 1 1.5 0ZM9 9a1 1 0 1 1-2 0 1 1 0 0 1 2 0Z"></path></svg>`,
},
{
type: `warning`,
icon: `<svg class="octicon octicon-alert mr-2" viewBox="0 0 16 16" width="16" height="16" aria-hidden="true"><path d="M6.457 1.047c.659-1.234 2.427-1.234 3.086 0l6.082 11.378A1.75 1.75 0 0 1 14.082 15H1.918a1.75 1.75 0 0 1-1.543-2.575Zm1.763.707a.25.25 0 0 0-.44 0L1.698 13.132a.25.25 0 0 0 .22.368h12.164a.25.25 0 0 0 .22-.368Zm.53 3.996v2.5a.75.75 0 0 1-1.5 0v-2.5a.75.75 0 0 1 1.5 0ZM9 11a1 1 0 1 1-2 0 1 1 0 0 1 2 0Z"></path></svg>`,
},
{
type: `caution`,
icon: `<svg class="octicon octicon-stop mr-2" viewBox="0 0 16 16" width="16" height="16" aria-hidden="true"><path d="M4.47.22A.749.749 0 0 1 5 0h6c.199 0 .389.079.53.22l4.25 4.25c.141.14.22.331.22.53v6a.749.749 0 0 1-.22.53l-4.25 4.25A.749.749 0 0 1 11 16H5a.749.749 0 0 1-.53-.22L.22 11.53A.749.749 0 0 1 0 11V5c0-.199.079-.389.22-.53Zm.84 1.28L1.5 5.31v5.38l3.81 3.81h5.38l3.81-3.81V5.31L10.69 1.5ZM8 4a.75.75 0 0 1 .75.75v3.5a.75.75 0 0 1-1.5 0v-3.5A.75.75 0 0 1 8 4Zm0 8a1 1 0 1 1 0-2 1 1 0 0 1 0 2Z"></path></svg>`,
},
]
/**
* Resolves the variants configuration, combining the provided variants with
* the default variants.
*/
export function resolveVariants(variants: AlertVariantItem[]) {
if (!variants.length)
return defaultAlertVariant
return Object.values(
[...defaultAlertVariant, ...variants].reduce(
(map, item) => {
map[item.type] = item
return map
},
{} as { [key: string]: AlertVariantItem },
),
)
}
/**
* Returns regex pattern to match alert syntax.
*/
export function createSyntaxPattern(type: string) {
return `^(?:\\[!${type.toUpperCase()}])\\s*?\n*`
}
/**
* Capitalizes the first letter of a string.
*/
export function ucfirst(str: string) {
return str.slice(0, 1).toUpperCase() + str.slice(1).toLowerCase()
}

View File

@ -6,6 +6,7 @@ import hljs from 'highlight.js'
import { marked } from 'marked' import { marked } from 'marked'
import mermaid from 'mermaid' import mermaid from 'mermaid'
import markedAlert from './MDAlert'
import { MDKatex } from './MDKatex' import { MDKatex } from './MDKatex'
marked.use(MDKatex({ nonStandard: true })) marked.use(MDKatex({ nonStandard: true }))
@ -103,6 +104,8 @@ export function initRenderer(opts: IOpts) {
let listIndex: number = 0 let listIndex: number = 0
let isOrdered: boolean = false let isOrdered: boolean = false
marked.use(markedAlert({ theme: opts.theme }))
function styles(tag: string, addition: string = ``): string { function styles(tag: string, addition: string = ``): string {
return getStyles(styleMapping, tag, addition) return getStyles(styleMapping, tag, addition)
} }

View File

@ -519,6 +519,17 @@ onMounted(() => {
border-spacing: 0; border-spacing: 0;
} }
:deep(.markdown-alert) {
display: block;
padding: 1em;
border-radius: 8px;
background: #f7f7f7;
}
:deep(.markdown-alert-title) {
font-weight: bold;
}
.codeMirror-wrapper, .codeMirror-wrapper,
.preview-wrapper { .preview-wrapper {
height: 100%; height: 100%;