mirror of
https://github.com/doocs/md.git
synced 2024-11-24 11:00:33 +08:00
feat: support GFM alerts & render perf (#446)
All checks were successful
Build and Deploy / build-and-deploy (push) Has been skipped
All checks were successful
Build and Deploy / build-and-deploy (push) Has been skipped
This commit is contained in:
parent
e032c06ba3
commit
d8b14f5ce8
@ -35,6 +35,7 @@ Markdown 文档自动即时渲染为微信图文,让你不再为微信文章
|
||||
|
||||
- [x] 支持自定义 CSS 样式
|
||||
- [x] 支持 Markdown 所有基础语法、代码块、LaTeX 公式
|
||||
- [x] 支持 [GFM 警告块](https://github.com/orgs/community/discussions/16925)
|
||||
- [x] 支持浅色、深色两种编辑器外观
|
||||
- [x] 支持 <kbd>Alt</kbd> + <kbd>Shift</kbd> + <kbd>F</kbd> 快速格式化文档
|
||||
- [x] 支持色盘取色,快速替换文章整体色调
|
||||
|
@ -33,6 +33,54 @@ blockquote {
|
||||
/* 引用段落样式 */
|
||||
blockquote_p {
|
||||
}
|
||||
/* GFM note 样式 */
|
||||
blockquote_note {
|
||||
}
|
||||
/* GFM tip 样式 */
|
||||
blockquote_tip {
|
||||
}
|
||||
/* GFM important 样式 */
|
||||
blockquote_important {
|
||||
}
|
||||
/* GFM warning 样式 */
|
||||
blockquote_warning {
|
||||
}
|
||||
/* GFM caution 样式 */
|
||||
blockquote_caution {
|
||||
}
|
||||
/* GFM 通用标题 */
|
||||
blockquote_title {
|
||||
}
|
||||
/* GFM note 标题 */
|
||||
blockquote_title_note {
|
||||
}
|
||||
/* GFM tip 标题 */
|
||||
blockquote_title_tip {
|
||||
}
|
||||
/* GFM important 标题 */
|
||||
blockquote_title_important {
|
||||
}
|
||||
/* GFM warning 标题 */
|
||||
blockquote_title_warning {
|
||||
}
|
||||
/* GFM caution 标题 */
|
||||
blockquote_title_caution {
|
||||
}
|
||||
/* GFM note 段落样式 */
|
||||
blockquote_p_note {
|
||||
}
|
||||
/* GFM tip 段落样式 */
|
||||
blockquote_p_tip {
|
||||
}
|
||||
/* GFM important 段落样式 */
|
||||
blockquote_p_important {
|
||||
}
|
||||
/* GFM warning 段落样式 */
|
||||
blockquote_p_warning {
|
||||
}
|
||||
/* GFM caution 段落样式 */
|
||||
blockquote_p_caution {
|
||||
}
|
||||
/* 段落样式 */
|
||||
p {
|
||||
}
|
||||
|
@ -32,6 +32,8 @@
|
||||
--input:0 0% 89.8%;
|
||||
--ring:0 0% 3.9%;
|
||||
--radius: 0.5rem;
|
||||
|
||||
--blockquote-background: #f7f7f7;
|
||||
}
|
||||
|
||||
.dark {
|
||||
@ -62,6 +64,8 @@
|
||||
--border:0 0% 14.9%;
|
||||
--input:0 0% 14.9%;
|
||||
--ring:0 0% 83.1%;
|
||||
|
||||
--blockquote-background: #212121;
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -123,9 +123,20 @@ function copy() {
|
||||
.replace(/top:(.*?)em/g, `transform: translateY($1em)`)
|
||||
// 适配主题中的颜色变量
|
||||
.replaceAll(`var(--el-text-color-regular)`, `#3f3f3f`)
|
||||
.replaceAll(`var(--blockquote-background)`, `#f7f7f7`)
|
||||
.replaceAll(`var(--md-primary-color)`, primaryColor.value)
|
||||
.replaceAll(/--md-primary-color:.+?;/g, ``)
|
||||
|
||||
clipboardDiv.focus()
|
||||
|
||||
// edge case: 由于 svg 无法复制, 在前面插入一个空节点
|
||||
const p = document.createElement(`p`)
|
||||
p.style.fontSize = `0` // 设置字体大小为 0
|
||||
p.style.lineHeight = `0` // 行高也为 0
|
||||
p.style.margin = `0` // 避免外边距干扰
|
||||
p.innerHTML = ` `
|
||||
clipboardDiv.insertBefore(p, clipboardDiv.firstChild)
|
||||
|
||||
window.getSelection()!.removeAllRanges()
|
||||
const range = document.createRange()
|
||||
|
||||
|
@ -82,7 +82,7 @@ const defaultTheme: Theme = {
|
||||
'padding': `1em`,
|
||||
'border-radius': `8px`,
|
||||
'color': `rgba(0,0,0,0.5)`,
|
||||
'background': `#f7f7f7`,
|
||||
'background': `var(--blockquote-background)`,
|
||||
'margin': `2em 8px`,
|
||||
},
|
||||
|
||||
@ -91,7 +91,65 @@ const defaultTheme: Theme = {
|
||||
'display': `block`,
|
||||
'font-size': `1em`,
|
||||
'letter-spacing': `0.1em`,
|
||||
'color': `rgb(80, 80, 80)`,
|
||||
'color': `var(--el-text-color-regular)`,
|
||||
},
|
||||
|
||||
blockquote_note: {
|
||||
},
|
||||
|
||||
blockquote_tip: {
|
||||
},
|
||||
|
||||
blockquote_important: {
|
||||
},
|
||||
|
||||
blockquote_warning: {
|
||||
},
|
||||
|
||||
blockquote_caution: {
|
||||
},
|
||||
|
||||
// GFM 警告块标题
|
||||
blockquote_title: {
|
||||
'display': `flex`,
|
||||
'align-items': `center`,
|
||||
'gap': `0.5em`,
|
||||
'margin-bottom': `0.5em`,
|
||||
},
|
||||
|
||||
blockquote_title_note: {
|
||||
color: `#478be6`,
|
||||
},
|
||||
|
||||
blockquote_title_tip: {
|
||||
color: `#57ab5a`,
|
||||
},
|
||||
|
||||
blockquote_title_important: {
|
||||
color: `#986ee2`,
|
||||
},
|
||||
|
||||
blockquote_title_warning: {
|
||||
color: `#c69026`,
|
||||
},
|
||||
|
||||
blockquote_title_caution: {
|
||||
color: `#e5534b`,
|
||||
},
|
||||
|
||||
blockquote_p_note: {
|
||||
},
|
||||
|
||||
blockquote_p_tip: {
|
||||
},
|
||||
|
||||
blockquote_p_important: {
|
||||
},
|
||||
|
||||
blockquote_p_warning: {
|
||||
},
|
||||
|
||||
blockquote_p_caution: {
|
||||
},
|
||||
|
||||
// 代码块
|
||||
@ -230,87 +288,90 @@ const graceTheme = toMerged(defaultTheme, {
|
||||
base: {
|
||||
},
|
||||
block: {
|
||||
h1: {
|
||||
'h1': {
|
||||
'padding': `0.5em 1em`,
|
||||
'border-bottom': `2px solid var(--md-primary-color)`,
|
||||
'font-size': `1.4em`,
|
||||
'text-shadow': `2px 2px 4px rgba(0,0,0,0.1)`,
|
||||
},
|
||||
|
||||
h2: {
|
||||
'h2': {
|
||||
'padding': `0.3em 1em`,
|
||||
'border-radius': `8px`,
|
||||
'font-size': `1.3em`,
|
||||
'box-shadow': `0 4px 6px rgba(0,0,0,0.1)`,
|
||||
},
|
||||
|
||||
h3: {
|
||||
'h3': {
|
||||
'padding-left': `12px`,
|
||||
'font-size': `1.2em`,
|
||||
'border-left': `4px solid var(--md-primary-color)`,
|
||||
'border-bottom': `1px dashed var(--md-primary-color)`,
|
||||
},
|
||||
|
||||
h4: {
|
||||
'h4': {
|
||||
'font-size': `1.1em`,
|
||||
},
|
||||
|
||||
h5: {
|
||||
'h5': {
|
||||
'font-size': `1em`,
|
||||
},
|
||||
|
||||
h6: {
|
||||
'h6': {
|
||||
'font-size': `1em`,
|
||||
},
|
||||
|
||||
p: {
|
||||
'p': {
|
||||
},
|
||||
|
||||
blockquote: {
|
||||
'blockquote': {
|
||||
'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)`,
|
||||
},
|
||||
|
||||
blockquote_p: {
|
||||
'blockquote_p': {
|
||||
},
|
||||
|
||||
code_pre: {
|
||||
'markdown-alert': {
|
||||
'font-style': `italic`,
|
||||
},
|
||||
|
||||
'code_pre': {
|
||||
'box-shadow': `inset 0 0 10px rgba(0,0,0,0.05)`,
|
||||
},
|
||||
|
||||
code: {
|
||||
'code': {
|
||||
'white-space': `pre-wrap`,
|
||||
'font-family': `'Fira Code', Menlo, Operator Mono, Consolas, Monaco, monospace`,
|
||||
},
|
||||
|
||||
image: {
|
||||
'image': {
|
||||
'border-radius': `8px`,
|
||||
'box-shadow': `0 4px 8px rgba(0,0,0,0.1)`,
|
||||
},
|
||||
|
||||
ol: {
|
||||
'ol': {
|
||||
'padding-left': `1.5em`,
|
||||
},
|
||||
|
||||
ul: {
|
||||
'ul': {
|
||||
'list-style': `none`,
|
||||
'padding-left': `1.5em`,
|
||||
},
|
||||
|
||||
footnotes: {
|
||||
'footnotes': {
|
||||
|
||||
},
|
||||
|
||||
figure: {
|
||||
'figure': {
|
||||
|
||||
},
|
||||
|
||||
hr: {
|
||||
'hr': {
|
||||
height: `1px`,
|
||||
border: `none`,
|
||||
margin: `2em 0`,
|
||||
|
@ -143,7 +143,8 @@ export const useStore = defineStore(`store`, () => {
|
||||
// 更新编辑器
|
||||
const editorRefresh = () => {
|
||||
codeThemeChange()
|
||||
renderer.reset({ status: 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
|
||||
|
||||
// 去除第一行的 margin-top
|
||||
@ -157,23 +158,27 @@ export const useStore = defineStore(`store`, () => {
|
||||
outputTemp += `
|
||||
<style>
|
||||
.hljs.code__pre > .mac-sign {
|
||||
display: inline-block;
|
||||
}
|
||||
|
||||
.hljs.code__pre {
|
||||
padding: 0!important;
|
||||
}
|
||||
|
||||
.hljs.code__pre code {
|
||||
display: -webkit-box;
|
||||
padding: 0.5em 1em 1em;
|
||||
overflow-x: auto;
|
||||
text-indent: 0;
|
||||
display: flex;
|
||||
}
|
||||
</style>
|
||||
`
|
||||
}
|
||||
|
||||
outputTemp += `
|
||||
<style>
|
||||
.code__pre {
|
||||
padding: 0 !important;
|
||||
}
|
||||
|
||||
.hljs.code__pre code {
|
||||
display: -webkit-box;
|
||||
padding: 0.5em 1em 1em;
|
||||
overflow-x: auto;
|
||||
text-indent: 0;
|
||||
}
|
||||
</style>
|
||||
`
|
||||
|
||||
output.value = outputTemp
|
||||
}
|
||||
|
||||
@ -184,6 +189,7 @@ export const useStore = defineStore(`store`, () => {
|
||||
renderer.setOptions({
|
||||
theme: newTheme,
|
||||
})
|
||||
|
||||
editorRefresh()
|
||||
}
|
||||
// 初始化 CSS 编辑器
|
||||
@ -354,7 +360,7 @@ export const useStore = defineStore(`store`, () => {
|
||||
const reader = new FileReader()
|
||||
reader.readAsText(file)
|
||||
reader.onload = (event) => {
|
||||
(editor.value!).setValue((event.target !).result as string)
|
||||
(editor.value!).setValue((event.target!).result as string)
|
||||
ElMessage.success(`文档导入成功`)
|
||||
}
|
||||
}
|
||||
|
@ -1,6 +1,9 @@
|
||||
import type { PropertiesHyphen } from 'csstype'
|
||||
|
||||
export type Block = `h1` | `h2` | `h3` | `h4` | `h5` | `h6` | `p` | `blockquote` | `blockquote_p` | `code_pre` | `code` | `image` | `ol` | `ul` | `footnotes` | `figure` | `hr`
|
||||
import type { Token } from 'marked'
|
||||
|
||||
type GFMBlock = `blockquote_note` | `blockquote_tip` | `blockquote_important` | `blockquote_warning` | `blockquote_caution` | `blockquote_title` | `blockquote_title_note` | `blockquote_title_tip` | `blockquote_title_important` | `blockquote_title_warning` | `blockquote_title_caution` | `blockquote_p` | `blockquote_p_note` | `blockquote_p_tip` | `blockquote_p_important` | `blockquote_p_warning` | `blockquote_p_caution`
|
||||
export type Block = `h1` | `h2` | `h3` | `h4` | `h5` | `h6` | `p` | `blockquote` | `blockquote_p` | `code_pre` | `code` | `image` | `ol` | `ul` | `footnotes` | `figure` | `hr` | GFMBlock
|
||||
export type Inline = `listitem` | `codespan` | `link` | `wx_link` | `strong` | `table` | `thead` | `td` | `footnote` | `figcaption` | `em`
|
||||
|
||||
interface CustomCSSProperties {
|
||||
@ -12,8 +15,8 @@ export type ExtendedProperties = PropertiesHyphen & CustomCSSProperties
|
||||
|
||||
export interface Theme {
|
||||
base: ExtendedProperties
|
||||
block: Record<Block, PropertiesHyphen>
|
||||
inline: Record<Inline, PropertiesHyphen>
|
||||
block: Record<Block, ExtendedProperties>
|
||||
inline: Record<Inline, ExtendedProperties>
|
||||
}
|
||||
|
||||
export interface IOpts {
|
||||
@ -22,7 +25,7 @@ export interface IOpts {
|
||||
size: string
|
||||
isUseIndent: boolean
|
||||
legend?: string
|
||||
status?: boolean
|
||||
citeStatus?: boolean
|
||||
}
|
||||
|
||||
export type ThemeStyles = Record<Block | Inline, ExtendedProperties>
|
||||
@ -32,3 +35,39 @@ export interface IConfigOption<VT = string> {
|
||||
value: VT
|
||||
desc: string
|
||||
}
|
||||
|
||||
/**
|
||||
* Options for the `markedAlert` extension.
|
||||
*/
|
||||
export interface AlertOptions {
|
||||
className?: string
|
||||
variants?: AlertVariantItem[]
|
||||
styles?: ThemeStyles
|
||||
}
|
||||
|
||||
/**
|
||||
* 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[]
|
||||
}
|
||||
|
159
src/utils/MDAlert.ts
Normal file
159
src/utils/MDAlert.ts
Normal file
@ -0,0 +1,159 @@
|
||||
import type { AlertOptions, AlertVariantItem } from '@/types'
|
||||
import type { MarkedExtension, Tokens } from 'marked'
|
||||
import { getStyleString } from '.'
|
||||
|
||||
/**
|
||||
* 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), `i`).test(token.text),
|
||||
)
|
||||
|
||||
if (matchedVariant) {
|
||||
const {
|
||||
type: variantType,
|
||||
icon,
|
||||
title = ucfirst(variantType),
|
||||
titleClassName = `${className}-title`,
|
||||
} = matchedVariant
|
||||
const typeRegexp = new RegExp(createSyntaxPattern(variantType), `i`)
|
||||
|
||||
const { styles } = options
|
||||
|
||||
Object.assign(token, {
|
||||
type: `alert`,
|
||||
meta: {
|
||||
className,
|
||||
variant: variantType,
|
||||
icon,
|
||||
title,
|
||||
titleClassName,
|
||||
wrapperStyle: {
|
||||
...styles?.blockquote,
|
||||
...styles?.[`blockquote_${variantType}` as keyof typeof styles],
|
||||
},
|
||||
titleStyle: {
|
||||
...styles?.blockquote_title,
|
||||
...styles?.[`blockquote_title_${variantType}` as keyof typeof styles],
|
||||
},
|
||||
contentStyle: {
|
||||
...styles?.blockquote_p,
|
||||
...styles?.[`blockquote_p_${variantType}` as keyof typeof styles],
|
||||
},
|
||||
},
|
||||
})
|
||||
|
||||
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 text = this.parser.parse(tokens)
|
||||
text = text.replace(/<p .*?>/g, `<p style="${getStyleString(meta.contentStyle)}">`)
|
||||
let tmpl = `<blockquote class="${meta.className} ${meta.className}-${meta.variant}" style="${getStyleString(meta.wrapperStyle)}">\n`
|
||||
tmpl += `<p class="${meta.titleClassName}" style="${getStyleString(meta.titleStyle)}">`
|
||||
tmpl += meta.icon.replace(
|
||||
`<svg`,
|
||||
`<svg style="fill: ${meta.titleStyle?.color ?? `inherit`}"`,
|
||||
)
|
||||
tmpl += meta.title
|
||||
tmpl += `</p>\n`
|
||||
tmpl += text
|
||||
tmpl += `</blockquote>\n`
|
||||
|
||||
return tmpl
|
||||
},
|
||||
},
|
||||
],
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* The default configuration for alert variants.
|
||||
*/
|
||||
const defaultAlertVariant: AlertVariantItem[] = [
|
||||
{
|
||||
type: `note`,
|
||||
icon: `<svg class="octicon octicon-info" style="margin-right: 0.25em;" 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" style="margin-right: 0.25em;" 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" style="margin-right: 0.25em;" 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" style="margin-right: 0.25em;" 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" style="margin-right: 0.25em;" 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}])\\s*?\n*`
|
||||
}
|
||||
|
||||
/**
|
||||
* Capitalizes the first letter of a string.
|
||||
*/
|
||||
export function ucfirst(str: string) {
|
||||
return str.slice(0, 1).toUpperCase() + str.slice(1).toLowerCase()
|
||||
}
|
@ -1,4 +1,4 @@
|
||||
import type { Block, Inline, Theme } from '@/types'
|
||||
import type { Block, ExtendedProperties, Inline, Theme } from '@/types'
|
||||
|
||||
import type { PropertiesHyphen } from 'csstype'
|
||||
import { prefix } from '@/config'
|
||||
@ -34,7 +34,7 @@ export function customizeTheme(theme: Theme, options: {
|
||||
export function customCssWithTemplate(jsonString: Partial<Record<Block | Inline, PropertiesHyphen>>, color: string, theme: Theme) {
|
||||
const newTheme = customizeTheme(theme, { color })
|
||||
|
||||
const mergeProperties = <T extends Block | Inline = Block>(target: Record<T, PropertiesHyphen>, source: Partial<Record<Block | Inline, PropertiesHyphen>>, keys: T[]) => {
|
||||
const mergeProperties = <T extends Block | Inline = Block>(target: Record<T, PropertiesHyphen>, source: Partial<Record<Block | Inline | string, PropertiesHyphen>>, keys: T[]) => {
|
||||
keys.forEach((key) => {
|
||||
if (source[key]) {
|
||||
target[key] = Object.assign(target[key] || {}, source[key])
|
||||
@ -54,7 +54,23 @@ export function customCssWithTemplate(jsonString: Partial<Record<Block | Inline,
|
||||
`p`,
|
||||
`hr`,
|
||||
`blockquote`,
|
||||
`blockquote_note`,
|
||||
`blockquote_tip`,
|
||||
`blockquote_important`,
|
||||
`blockquote_warning`,
|
||||
`blockquote_caution`,
|
||||
`blockquote_p`,
|
||||
`blockquote_p_note`,
|
||||
`blockquote_p_tip`,
|
||||
`blockquote_p_important`,
|
||||
`blockquote_p_warning`,
|
||||
`blockquote_p_caution`,
|
||||
`blockquote_title`,
|
||||
`blockquote_title_note`,
|
||||
`blockquote_title_tip`,
|
||||
`blockquote_title_important`,
|
||||
`blockquote_title_warning`,
|
||||
`blockquote_title_caution`,
|
||||
`image`,
|
||||
`ul`,
|
||||
`ol`,
|
||||
@ -116,6 +132,15 @@ export function css2json(css: string): Partial<Record<Block | Inline, Properties
|
||||
return json
|
||||
}
|
||||
|
||||
/**
|
||||
* 将样式对象转换为 CSS 字符串
|
||||
* @param {ExtendedProperties} style - 样式对象
|
||||
* @returns {string} - CSS 字符串
|
||||
*/
|
||||
export function getStyleString(style: ExtendedProperties) {
|
||||
return Object.entries(style ?? {}).map(([key, value]) => `${key}: ${value}`).join(`; `)
|
||||
}
|
||||
|
||||
/**
|
||||
* 格式化内容
|
||||
* @param {string} content - 要格式化的内容
|
||||
|
@ -6,6 +6,8 @@ import hljs from 'highlight.js'
|
||||
|
||||
import { marked } from 'marked'
|
||||
import mermaid from 'mermaid'
|
||||
import { getStyleString } from '.'
|
||||
import markedAlert from './MDAlert'
|
||||
import { MDKatex } from './MDKatex'
|
||||
|
||||
marked.use(MDKatex({ nonStandard: true }))
|
||||
@ -58,9 +60,7 @@ function getStyles(styleMapping: ThemeStyles, tokenName: string, addition: strin
|
||||
if (!dict) {
|
||||
return ``
|
||||
}
|
||||
const styles = Object.entries(dict)
|
||||
.map(([key, value]) => `${key}:${value}`)
|
||||
.join(`;`)
|
||||
const styles = getStyleString(dict)
|
||||
return `style="${styles}${addition}"`
|
||||
}
|
||||
|
||||
@ -89,9 +89,9 @@ function transform(legend: string, text: string | null, title: string | null): s
|
||||
|
||||
const macCodeSvg = `
|
||||
<svg xmlns="http://www.w3.org/2000/svg" version="1.1" x="0px" y="0px" width="45px" height="13px" viewBox="0 0 450 130">
|
||||
<ellipse cx="65" cy="65" rx="50" ry="52" stroke="rgb(220,60,54)" stroke-width="2" fill="rgb(237,108,96)" />
|
||||
<ellipse cx="50" cy="65" rx="50" ry="52" stroke="rgb(220,60,54)" stroke-width="2" fill="rgb(237,108,96)" />
|
||||
<ellipse cx="225" cy="65" rx="50" ry="52" stroke="rgb(218,151,33)" stroke-width="2" fill="rgb(247,193,81)" />
|
||||
<ellipse cx="385" cy="65" rx="50" ry="52" stroke="rgb(27,161,37)" stroke-width="2" fill="rgb(100,200,86)" />
|
||||
<ellipse cx="400" cy="65" rx="50" ry="52" stroke="rgb(27,161,37)" stroke-width="2" fill="rgb(100,200,86)" />
|
||||
</svg>
|
||||
`.trim()
|
||||
|
||||
@ -126,6 +126,7 @@ export function initRenderer(opts: IOpts) {
|
||||
function setOptions(newOpts: Partial<IOpts>): void {
|
||||
opts = { ...opts, ...newOpts }
|
||||
styleMapping = buildTheme(opts)
|
||||
marked.use(markedAlert({ styles: styleMapping }))
|
||||
}
|
||||
|
||||
const buildFootnotes = () => {
|
||||
@ -211,18 +212,19 @@ export function initRenderer(opts: IOpts) {
|
||||
return `<figure ${figureStyles}><img ${imgStyles} src="${href}" title="${title}" alt="${text}"/>${subText}</figure>`
|
||||
},
|
||||
|
||||
link({ href, title, text }: Tokens.Link): string {
|
||||
link({ href, title, text, tokens }: Tokens.Link): string {
|
||||
const parsedText = this.parser.parseInline(tokens)
|
||||
if (href.startsWith(`https://mp.weixin.qq.com`)) {
|
||||
return `<a href="${href}" title="${title || text}" ${styles(`wx_link`)}>${text}</a>`
|
||||
return `<a href="${href}" title="${title || text}" ${styles(`wx_link`)}>${parsedText}</a>`
|
||||
}
|
||||
if (href === text) {
|
||||
return text
|
||||
return parsedText
|
||||
}
|
||||
if (opts.status) {
|
||||
if (opts.citeStatus) {
|
||||
const ref = addFootnote(title || text, href)
|
||||
return `<span ${styles(`link`)}>${text}<sup>[${ref}]</sup></span>`
|
||||
return `<span ${styles(`link`)}>${parsedText}<sup>[${ref}]</sup></span>`
|
||||
}
|
||||
return styledContent(`link`, text, `span`)
|
||||
return styledContent(`link`, parsedText, `span`)
|
||||
},
|
||||
|
||||
strong({ tokens }: Tokens.Strong): string {
|
||||
|
Loading…
Reference in New Issue
Block a user