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] 支持自定义 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] 支持色盘取色,快速替换文章整体色调
|
||||||
|
@ -33,6 +33,54 @@ blockquote {
|
|||||||
/* 引用段落样式 */
|
/* 引用段落样式 */
|
||||||
blockquote_p {
|
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 {
|
p {
|
||||||
}
|
}
|
||||||
|
@ -32,6 +32,8 @@
|
|||||||
--input:0 0% 89.8%;
|
--input:0 0% 89.8%;
|
||||||
--ring:0 0% 3.9%;
|
--ring:0 0% 3.9%;
|
||||||
--radius: 0.5rem;
|
--radius: 0.5rem;
|
||||||
|
|
||||||
|
--blockquote-background: #f7f7f7;
|
||||||
}
|
}
|
||||||
|
|
||||||
.dark {
|
.dark {
|
||||||
@ -62,6 +64,8 @@
|
|||||||
--border:0 0% 14.9%;
|
--border:0 0% 14.9%;
|
||||||
--input:0 0% 14.9%;
|
--input:0 0% 14.9%;
|
||||||
--ring:0 0% 83.1%;
|
--ring:0 0% 83.1%;
|
||||||
|
|
||||||
|
--blockquote-background: #212121;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -123,9 +123,20 @@ function copy() {
|
|||||||
.replace(/top:(.*?)em/g, `transform: translateY($1em)`)
|
.replace(/top:(.*?)em/g, `transform: translateY($1em)`)
|
||||||
// 适配主题中的颜色变量
|
// 适配主题中的颜色变量
|
||||||
.replaceAll(`var(--el-text-color-regular)`, `#3f3f3f`)
|
.replaceAll(`var(--el-text-color-regular)`, `#3f3f3f`)
|
||||||
|
.replaceAll(`var(--blockquote-background)`, `#f7f7f7`)
|
||||||
.replaceAll(`var(--md-primary-color)`, primaryColor.value)
|
.replaceAll(`var(--md-primary-color)`, primaryColor.value)
|
||||||
.replaceAll(/--md-primary-color:.+?;/g, ``)
|
.replaceAll(/--md-primary-color:.+?;/g, ``)
|
||||||
|
|
||||||
clipboardDiv.focus()
|
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()
|
window.getSelection()!.removeAllRanges()
|
||||||
const range = document.createRange()
|
const range = document.createRange()
|
||||||
|
|
||||||
|
@ -82,7 +82,7 @@ const defaultTheme: Theme = {
|
|||||||
'padding': `1em`,
|
'padding': `1em`,
|
||||||
'border-radius': `8px`,
|
'border-radius': `8px`,
|
||||||
'color': `rgba(0,0,0,0.5)`,
|
'color': `rgba(0,0,0,0.5)`,
|
||||||
'background': `#f7f7f7`,
|
'background': `var(--blockquote-background)`,
|
||||||
'margin': `2em 8px`,
|
'margin': `2em 8px`,
|
||||||
},
|
},
|
||||||
|
|
||||||
@ -91,7 +91,65 @@ const defaultTheme: Theme = {
|
|||||||
'display': `block`,
|
'display': `block`,
|
||||||
'font-size': `1em`,
|
'font-size': `1em`,
|
||||||
'letter-spacing': `0.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: {
|
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)`,
|
||||||
'border-radius': `6px`,
|
'border-radius': `6px`,
|
||||||
'color': `rgba(0,0,0,0.6)`,
|
'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)`,
|
'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)`,
|
'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`,
|
||||||
|
@ -143,7 +143,8 @@ export const useStore = defineStore(`store`, () => {
|
|||||||
// 更新编辑器
|
// 更新编辑器
|
||||||
const editorRefresh = () => {
|
const editorRefresh = () => {
|
||||||
codeThemeChange()
|
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
|
let outputTemp = marked.parse(editor.value!.getValue()) as string
|
||||||
|
|
||||||
// 去除第一行的 margin-top
|
// 去除第一行的 margin-top
|
||||||
@ -157,23 +158,27 @@ export const useStore = defineStore(`store`, () => {
|
|||||||
outputTemp += `
|
outputTemp += `
|
||||||
<style>
|
<style>
|
||||||
.hljs.code__pre > .mac-sign {
|
.hljs.code__pre > .mac-sign {
|
||||||
display: inline-block;
|
display: flex;
|
||||||
}
|
|
||||||
|
|
||||||
.hljs.code__pre {
|
|
||||||
padding: 0!important;
|
|
||||||
}
|
|
||||||
|
|
||||||
.hljs.code__pre code {
|
|
||||||
display: -webkit-box;
|
|
||||||
padding: 0.5em 1em 1em;
|
|
||||||
overflow-x: auto;
|
|
||||||
text-indent: 0;
|
|
||||||
}
|
}
|
||||||
</style>
|
</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
|
output.value = outputTemp
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -184,6 +189,7 @@ export const useStore = defineStore(`store`, () => {
|
|||||||
renderer.setOptions({
|
renderer.setOptions({
|
||||||
theme: newTheme,
|
theme: newTheme,
|
||||||
})
|
})
|
||||||
|
|
||||||
editorRefresh()
|
editorRefresh()
|
||||||
}
|
}
|
||||||
// 初始化 CSS 编辑器
|
// 初始化 CSS 编辑器
|
||||||
@ -354,7 +360,7 @@ export const useStore = defineStore(`store`, () => {
|
|||||||
const reader = new FileReader()
|
const reader = new FileReader()
|
||||||
reader.readAsText(file)
|
reader.readAsText(file)
|
||||||
reader.onload = (event) => {
|
reader.onload = (event) => {
|
||||||
(editor.value!).setValue((event.target !).result as string)
|
(editor.value!).setValue((event.target!).result as string)
|
||||||
ElMessage.success(`文档导入成功`)
|
ElMessage.success(`文档导入成功`)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -1,6 +1,9 @@
|
|||||||
import type { PropertiesHyphen } from 'csstype'
|
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`
|
export type Inline = `listitem` | `codespan` | `link` | `wx_link` | `strong` | `table` | `thead` | `td` | `footnote` | `figcaption` | `em`
|
||||||
|
|
||||||
interface CustomCSSProperties {
|
interface CustomCSSProperties {
|
||||||
@ -12,8 +15,8 @@ export type ExtendedProperties = PropertiesHyphen & CustomCSSProperties
|
|||||||
|
|
||||||
export interface Theme {
|
export interface Theme {
|
||||||
base: ExtendedProperties
|
base: ExtendedProperties
|
||||||
block: Record<Block, PropertiesHyphen>
|
block: Record<Block, ExtendedProperties>
|
||||||
inline: Record<Inline, PropertiesHyphen>
|
inline: Record<Inline, ExtendedProperties>
|
||||||
}
|
}
|
||||||
|
|
||||||
export interface IOpts {
|
export interface IOpts {
|
||||||
@ -22,7 +25,7 @@ export interface IOpts {
|
|||||||
size: string
|
size: string
|
||||||
isUseIndent: boolean
|
isUseIndent: boolean
|
||||||
legend?: string
|
legend?: string
|
||||||
status?: boolean
|
citeStatus?: boolean
|
||||||
}
|
}
|
||||||
|
|
||||||
export type ThemeStyles = Record<Block | Inline, ExtendedProperties>
|
export type ThemeStyles = Record<Block | Inline, ExtendedProperties>
|
||||||
@ -32,3 +35,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[]
|
||||||
|
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 type { PropertiesHyphen } from 'csstype'
|
||||||
import { prefix } from '@/config'
|
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) {
|
export function customCssWithTemplate(jsonString: Partial<Record<Block | Inline, PropertiesHyphen>>, color: string, theme: Theme) {
|
||||||
const newTheme = customizeTheme(theme, { color })
|
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) => {
|
keys.forEach((key) => {
|
||||||
if (source[key]) {
|
if (source[key]) {
|
||||||
target[key] = Object.assign(target[key] || {}, source[key])
|
target[key] = Object.assign(target[key] || {}, source[key])
|
||||||
@ -54,7 +54,23 @@ export function customCssWithTemplate(jsonString: Partial<Record<Block | Inline,
|
|||||||
`p`,
|
`p`,
|
||||||
`hr`,
|
`hr`,
|
||||||
`blockquote`,
|
`blockquote`,
|
||||||
|
`blockquote_note`,
|
||||||
|
`blockquote_tip`,
|
||||||
|
`blockquote_important`,
|
||||||
|
`blockquote_warning`,
|
||||||
|
`blockquote_caution`,
|
||||||
`blockquote_p`,
|
`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`,
|
`image`,
|
||||||
`ul`,
|
`ul`,
|
||||||
`ol`,
|
`ol`,
|
||||||
@ -116,6 +132,15 @@ export function css2json(css: string): Partial<Record<Block | Inline, Properties
|
|||||||
return json
|
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 - 要格式化的内容
|
* @param {string} content - 要格式化的内容
|
||||||
|
@ -6,6 +6,8 @@ import hljs from 'highlight.js'
|
|||||||
|
|
||||||
import { marked } from 'marked'
|
import { marked } from 'marked'
|
||||||
import mermaid from 'mermaid'
|
import mermaid from 'mermaid'
|
||||||
|
import { getStyleString } from '.'
|
||||||
|
import markedAlert from './MDAlert'
|
||||||
import { MDKatex } from './MDKatex'
|
import { MDKatex } from './MDKatex'
|
||||||
|
|
||||||
marked.use(MDKatex({ nonStandard: true }))
|
marked.use(MDKatex({ nonStandard: true }))
|
||||||
@ -58,9 +60,7 @@ function getStyles(styleMapping: ThemeStyles, tokenName: string, addition: strin
|
|||||||
if (!dict) {
|
if (!dict) {
|
||||||
return ``
|
return ``
|
||||||
}
|
}
|
||||||
const styles = Object.entries(dict)
|
const styles = getStyleString(dict)
|
||||||
.map(([key, value]) => `${key}:${value}`)
|
|
||||||
.join(`;`)
|
|
||||||
return `style="${styles}${addition}"`
|
return `style="${styles}${addition}"`
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -89,9 +89,9 @@ function transform(legend: string, text: string | null, title: string | null): s
|
|||||||
|
|
||||||
const macCodeSvg = `
|
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">
|
<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="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>
|
</svg>
|
||||||
`.trim()
|
`.trim()
|
||||||
|
|
||||||
@ -126,6 +126,7 @@ export function initRenderer(opts: IOpts) {
|
|||||||
function setOptions(newOpts: Partial<IOpts>): void {
|
function setOptions(newOpts: Partial<IOpts>): void {
|
||||||
opts = { ...opts, ...newOpts }
|
opts = { ...opts, ...newOpts }
|
||||||
styleMapping = buildTheme(opts)
|
styleMapping = buildTheme(opts)
|
||||||
|
marked.use(markedAlert({ styles: styleMapping }))
|
||||||
}
|
}
|
||||||
|
|
||||||
const buildFootnotes = () => {
|
const buildFootnotes = () => {
|
||||||
@ -211,18 +212,19 @@ export function initRenderer(opts: IOpts) {
|
|||||||
return `<figure ${figureStyles}><img ${imgStyles} src="${href}" title="${title}" alt="${text}"/>${subText}</figure>`
|
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`)) {
|
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) {
|
if (href === text) {
|
||||||
return text
|
return parsedText
|
||||||
}
|
}
|
||||||
if (opts.status) {
|
if (opts.citeStatus) {
|
||||||
const ref = addFootnote(title || text, href)
|
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 {
|
strong({ tokens }: Tokens.Strong): string {
|
||||||
|
Loading…
Reference in New Issue
Block a user