Skip to content

Commit 24f0fd6

Browse files
authored
feat: add markup extension (#1003)
1 parent 65856a9 commit 24f0fd6

File tree

7 files changed

+123
-2
lines changed

7 files changed

+123
-2
lines changed

README.md

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -36,6 +36,7 @@ Markdown 文档自动即时渲染为微信图文,让你不再为微信内容
3636
- [x] 提供对 Mermaid 图表的渲染和 [GFM 警告块](https://github.com/orgs/community/discussions/16925)的支持
3737
- [x] 提供 PlantUML 渲染支持
3838
- [x] 提供 ruby 注音扩展支持,支持两种格式:[文字]{注音}、[文字]^(注音),支持 ```````-` 分隔符
39+
- [x] 提供 Markdown 标记语法扩展支持,支持高亮(==文本==)、下划线(++文本++)、波浪线(~文本~)
3940
- [x] 丰富的代码块高亮主题,提升代码可读性
4041
- [x] 允许自定义主题色和 CSS 样式,灵活定制展示效果
4142
- [x] 提供多图上传功能,并可自定义配置图床

apps/web/src/assets/example/markdown.md

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -31,6 +31,9 @@ Markdown 中的段落就是一行接一行的文本。要创建新段落,只
3131
- **粗体**:用两个星号或下划线包裹文字,如 `**粗体**``__粗体__`
3232
- _斜体_:用一个星号或下划线包裹文字,如 `*斜体*``_斜体_`
3333
- ~~删除线~~:用两个波浪线包裹文字,如 `~~删除线~~`
34+
- ==高亮==:用两个等号包裹文字,如 `==高亮==`
35+
- ++下划线++:用两个加号包裹文字,如 `++下划线++`
36+
- ~波浪线~:用一个波浪线包裹文字,如 `~波浪线~`
3437

3538
这些简单的标记可以让你的内容更有层次感和重点突出。
3639

packages/core/src/extensions/index.ts

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -2,6 +2,7 @@
22
export * from './alert'
33
export * from './footnotes'
44
export * from './katex'
5+
export * from './markup'
56
export * from './plantuml'
67
export * from './ruby'
78
export * from './slider'
Lines changed: 91 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,91 @@
1+
import type { MarkupOptions } from '@md/shared'
2+
import type { MarkedExtension } from 'marked'
3+
import { getStyleString } from '../utils'
4+
5+
/**
6+
* 扩展标记语法:
7+
* - 高亮: ==文本==
8+
* - 下划线: ++文本++
9+
* - 波浪线: ~文本~
10+
*/
11+
export function markedMarkup(options: MarkupOptions = {}): MarkedExtension {
12+
const { styles } = options
13+
14+
return {
15+
extensions: [
16+
// 高亮语法 ==文本==
17+
{
18+
name: `markup_highlight`,
19+
level: `inline`,
20+
start(src: string) {
21+
return src.match(/==(?!=)/)?.index
22+
},
23+
tokenizer(src: string) {
24+
const rule = /^==((?:[^=]|=(?!=))+)==/
25+
const match = rule.exec(src)
26+
if (match) {
27+
return {
28+
type: `markup_highlight`,
29+
raw: match[0],
30+
text: match[1],
31+
}
32+
}
33+
},
34+
renderer(token: any) {
35+
const style = getStyleString(styles?.markup_highlight ?? {})
36+
return `<span class="markup-highlight" style="${style}">${token.text}</span>`
37+
},
38+
},
39+
40+
// 下划线语法 ++文本++
41+
{
42+
name: `markup_underline`,
43+
level: `inline`,
44+
start(src: string) {
45+
return src.match(/\+\+(?!\+)/)?.index
46+
},
47+
tokenizer(src: string) {
48+
const rule = /^\+\+((?:[^+]|\+(?!\+))+)\+\+/
49+
const match = rule.exec(src)
50+
if (match) {
51+
return {
52+
type: `markup_underline`,
53+
raw: match[0],
54+
text: match[1],
55+
}
56+
}
57+
},
58+
renderer(token: any) {
59+
const style = getStyleString(styles?.markup_underline ?? {})
60+
return `<span class="markup-underline" style="${style}">${token.text}</span>`
61+
},
62+
},
63+
64+
// 波浪线语法 ~文本~
65+
{
66+
name: `markup_wavyline`,
67+
level: `inline`,
68+
start(src: string) {
69+
// 查找单个 ~ 但不是连续的 ~~
70+
return src.match(/~(?!~)/)?.index
71+
},
72+
tokenizer(src: string) {
73+
// 匹配 ~文本~ 但确保不是 ~~文本~~
74+
const rule = /^~([^~\n]+)~(?!~)/
75+
const match = rule.exec(src)
76+
if (match) {
77+
return {
78+
type: `markup_wavyline`,
79+
raw: match[0],
80+
text: match[1],
81+
}
82+
}
83+
},
84+
renderer(token: any) {
85+
const style = getStyleString(styles?.markup_wavyline ?? {})
86+
return `<span class="markup-wavyline" style="${style}">${token.text}</span>`
87+
},
88+
},
89+
],
90+
}
91+
}

packages/core/src/renderer/renderer-impl.ts

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -8,7 +8,7 @@ import hljs from 'highlight.js'
88
import { marked } from 'marked'
99
import mermaid from 'mermaid'
1010
import readingTime from 'reading-time'
11-
import { markedAlert, markedFootnotes, markedPlantUML, markedRuby, markedSlider, markedToc, MDKatex } from '../extensions'
11+
import { markedAlert, markedFootnotes, markedMarkup, markedPlantUML, markedRuby, markedSlider, markedToc, MDKatex } from '../extensions'
1212
import { getStyleString } from '../utils'
1313

1414
marked.setOptions({
@@ -197,6 +197,7 @@ export function initRenderer(opts: IOpts): RendererAPI {
197197
MDKatex({ nonStandard: true }, styles(`inline_katex`, `;line-height: 1;`), styles(`block_katex`, `;text-align: center;`),
198198
),
199199
)
200+
marked.use(markedMarkup({ styles: styleMapping }))
200201
}
201202
}
202203

@@ -416,6 +417,7 @@ export function initRenderer(opts: IOpts): RendererAPI {
416417
}
417418

418419
marked.use({ renderer })
420+
marked.use(markedMarkup({ styles: styleMapping }))
419421
marked.use(markedToc())
420422
marked.use(markedSlider({ styles: styleMapping }))
421423
marked.use(markedAlert({ styles: styleMapping }))

packages/shared/src/configs/theme.ts

Lines changed: 17 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -311,6 +311,23 @@ const defaultTheme: Theme = {
311311
'max-width': `100%`,
312312
'overflow-x': `auto`,
313313
},
314+
315+
markup_highlight: {
316+
'background-color': `var(--md-primary-color)`,
317+
'padding': `2px 4px`,
318+
'border-radius': `2px`,
319+
'color': `#fff`,
320+
},
321+
markup_underline: {
322+
'text-decoration': `underline`,
323+
'text-decoration-color': `var(--md-primary-color)`,
324+
},
325+
markup_wavyline: {
326+
'text-decoration': `underline wavy`,
327+
'text-decoration-color': `var(--md-primary-color)`,
328+
'text-decoration-thickness': `2px`,
329+
},
330+
314331
},
315332
}
316333

packages/shared/src/types/common.ts

Lines changed: 7 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -7,7 +7,7 @@ type GFMBlock = `blockquote_note` | `blockquote_tip` | `blockquote_info` | `bloc
77
| `blockquote_p` | `blockquote_p_note` | `blockquote_p_tip` | `blockquote_p_info` | `blockquote_p_important` | `blockquote_p_warning` | `blockquote_p_caution`
88

99
export type Block = `container` | `h1` | `h2` | `h3` | `h4` | `h5` | `h6` | `p` | `blockquote` | `blockquote_p` | `code_pre` | `code` | `image` | `ol` | `ul` | `footnotes` | `figure` | `hr` | `block_katex` | GFMBlock
10-
export type Inline = `listitem` | `codespan` | `link` | `wx_link` | `strong` | `table` | `thead` | `th` | `td` | `footnote` | `figcaption` | `em` | `inline_katex`
10+
export type Inline = `listitem` | `codespan` | `link` | `wx_link` | `strong` | `table` | `thead` | `th` | `td` | `footnote` | `figcaption` | `em` | `inline_katex` | MarkupStyleKeys
1111

1212
interface CustomCSSProperties {
1313
[`--md-primary-color`]?: string
@@ -80,6 +80,12 @@ export interface Alert {
8080
tokens: Token[]
8181
}
8282

83+
export type MarkupStyleKeys = `markup_highlight` | `markup_underline` | `markup_wavyline`
84+
85+
export interface MarkupOptions {
86+
styles?: Partial<Record<MarkupStyleKeys, ExtendedProperties>>
87+
}
88+
8389
export interface PostAccount {
8490
avatar: string
8591
displayName: string

0 commit comments

Comments
 (0)