Skip to content

Commit ac87d19

Browse files
antfubrc-dd
andauthored
feat: support GitHub-flavored alerts (#3482)
Co-authored-by: Divyansh Singh <[email protected]>
1 parent 77a318c commit ac87d19

File tree

6 files changed

+203
-0
lines changed

6 files changed

+203
-0
lines changed

Diff for: docs/guide/markdown.md

+36
Original file line numberDiff line numberDiff line change
@@ -263,6 +263,42 @@ Wraps in a <div class="vp-raw">
263263
})
264264
```
265265

266+
## GitHub-flavored Alerts
267+
268+
VitePress also supports [GitHub-flavored alerts](https://docs.github.com/en/get-started/writing-on-github/getting-started-with-writing-and-formatting-on-github/basic-writing-and-formatting-syntax#alerts) to render as callouts. They will be rendered the same as the [custom containers](#custom-containers).
269+
270+
```md
271+
> [!NOTE]
272+
> Highlights information that users should take into account, even when skimming.
273+
274+
> [!TIP]
275+
> Optional information to help a user be more successful.
276+
277+
> [!IMPORTANT]
278+
> Crucial information necessary for users to succeed.
279+
280+
> [!WARNING]
281+
> Critical content demanding immediate user attention due to potential risks.
282+
283+
> [!CAUTION]
284+
> Negative potential consequences of an action.
285+
```
286+
287+
> [!NOTE]
288+
> Highlights information that users should take into account, even when skimming.
289+
290+
> [!TIP]
291+
> Optional information to help a user be more successful.
292+
293+
> [!IMPORTANT]
294+
> Crucial information necessary for users to succeed.
295+
296+
> [!WARNING]
297+
> Critical content demanding immediate user attention due to potential risks.
298+
299+
> [!CAUTION]
300+
> Negative potential consequences of an action.
301+
266302
## Syntax Highlighting in Code Blocks
267303

268304
VitePress uses [Shikiji](https://github.com/antfu/shikiji) (an improved version of [Shiki](https://shiki.matsu.io/)) to highlight language syntax in Markdown code blocks, using coloured text. Shiki supports a wide variety of programming languages. All you need to do is append a valid language alias to the beginning backticks for the code block:

Diff for: src/client/theme-default/styles/components/custom-block.css

+60
Original file line numberDiff line numberDiff line change
@@ -27,6 +27,26 @@
2727
background-color: var(--vp-custom-block-info-code-bg);
2828
}
2929

30+
.custom-block.note {
31+
border-color: var(--vp-custom-block-note-border);
32+
color: var(--vp-custom-block-note-text);
33+
background-color: var(--vp-custom-block-note-bg);
34+
}
35+
36+
.custom-block.note a,
37+
.custom-block.note code {
38+
color: var(--vp-c-brand-1);
39+
}
40+
41+
.custom-block.note a:hover,
42+
.custom-block.note a:hover > code {
43+
color: var(--vp-c-brand-2);
44+
}
45+
46+
.custom-block.note code {
47+
background-color: var(--vp-custom-block-note-code-bg);
48+
}
49+
3050
.custom-block.tip {
3151
border-color: var(--vp-custom-block-tip-border);
3252
color: var(--vp-custom-block-tip-text);
@@ -47,6 +67,26 @@
4767
background-color: var(--vp-custom-block-tip-code-bg);
4868
}
4969

70+
.custom-block.important {
71+
border-color: var(--vp-custom-block-important-border);
72+
color: var(--vp-custom-block-important-text);
73+
background-color: var(--vp-custom-block-important-bg);
74+
}
75+
76+
.custom-block.important a,
77+
.custom-block.important code {
78+
color: var(--vp-c-important-1);
79+
}
80+
81+
.custom-block.important a:hover,
82+
.custom-block.important a:hover > code {
83+
color: var(--vp-c-important-2);
84+
}
85+
86+
.custom-block.important code {
87+
background-color: var(--vp-custom-block-important-code-bg);
88+
}
89+
5090
.custom-block.warning {
5191
border-color: var(--vp-custom-block-warning-border);
5292
color: var(--vp-custom-block-warning-text);
@@ -87,6 +127,26 @@
87127
background-color: var(--vp-custom-block-danger-code-bg);
88128
}
89129

130+
.custom-block.caution {
131+
border-color: var(--vp-custom-block-caution-border);
132+
color: var(--vp-custom-block-caution-text);
133+
background-color: var(--vp-custom-block-caution-bg);
134+
}
135+
136+
.custom-block.caution a,
137+
.custom-block.caution code {
138+
color: var(--vp-c-caution-1);
139+
}
140+
141+
.custom-block.caution a:hover,
142+
.custom-block.caution a:hover > code {
143+
color: var(--vp-c-caution-2);
144+
}
145+
146+
.custom-block.caution code {
147+
background-color: var(--vp-custom-block-caution-code-bg);
148+
}
149+
90150
.custom-block.details {
91151
border-color: var(--vp-custom-block-details-border);
92152
color: var(--vp-custom-block-details-text);

Diff for: src/client/theme-default/styles/vars.css

+40
Original file line numberDiff line numberDiff line change
@@ -54,6 +54,11 @@
5454
--vp-c-indigo-3: #5672cd;
5555
--vp-c-indigo-soft: rgba(100, 108, 255, 0.14);
5656

57+
--vp-c-purple-1: #6f42c1;
58+
--vp-c-purple-2: #7e4cc9;
59+
--vp-c-purple-3: #8e5cd9;
60+
--vp-c-purple-soft: rgba(159, 122, 234, 0.14);
61+
5762
--vp-c-green-1: #18794e;
5863
--vp-c-green-2: #299764;
5964
--vp-c-green-3: #30a46c;
@@ -83,6 +88,11 @@
8388
--vp-c-indigo-3: #3e63dd;
8489
--vp-c-indigo-soft: rgba(100, 108, 255, 0.16);
8590

91+
--vp-c-purple-1: #c8abfa;
92+
--vp-c-purple-2: #a879e6;
93+
--vp-c-purple-3: #8e5cd9;
94+
--vp-c-purple-soft: rgba(159, 122, 234, 0.16);
95+
8696
--vp-c-green-1: #3dd68c;
8797
--vp-c-green-2: #30a46c;
8898
--vp-c-green-3: #298459;
@@ -215,11 +225,21 @@
215225
--vp-c-tip-3: var(--vp-c-brand-3);
216226
--vp-c-tip-soft: var(--vp-c-brand-soft);
217227

228+
--vp-c-note-1: var(--vp-c-brand-1);
229+
--vp-c-note-2: var(--vp-c-brand-2);
230+
--vp-c-note-3: var(--vp-c-brand-3);
231+
--vp-c-note-soft: var(--vp-c-brand-soft);
232+
218233
--vp-c-success-1: var(--vp-c-green-1);
219234
--vp-c-success-2: var(--vp-c-green-2);
220235
--vp-c-success-3: var(--vp-c-green-3);
221236
--vp-c-success-soft: var(--vp-c-green-soft);
222237

238+
--vp-c-important-1: var(--vp-c-purple-1);
239+
--vp-c-important-2: var(--vp-c-purple-2);
240+
--vp-c-important-3: var(--vp-c-purple-3);
241+
--vp-c-important-soft: var(--vp-c-purple-soft);
242+
223243
--vp-c-warning-1: var(--vp-c-yellow-1);
224244
--vp-c-warning-2: var(--vp-c-yellow-2);
225245
--vp-c-warning-3: var(--vp-c-yellow-3);
@@ -229,6 +249,11 @@
229249
--vp-c-danger-2: var(--vp-c-red-2);
230250
--vp-c-danger-3: var(--vp-c-red-3);
231251
--vp-c-danger-soft: var(--vp-c-red-soft);
252+
253+
--vp-c-caution-1: var(--vp-c-red-1);
254+
--vp-c-caution-2: var(--vp-c-red-2);
255+
--vp-c-caution-3: var(--vp-c-red-3);
256+
--vp-c-caution-soft: var(--vp-c-red-soft);
232257
}
233258

234259
/**
@@ -394,11 +419,21 @@
394419
--vp-custom-block-info-bg: var(--vp-c-default-soft);
395420
--vp-custom-block-info-code-bg: var(--vp-c-default-soft);
396421

422+
--vp-custom-block-note-border: transparent;
423+
--vp-custom-block-note-text: var(--vp-c-text-1);
424+
--vp-custom-block-note-bg: var(--vp-c-default-soft);
425+
--vp-custom-block-note-code-bg: var(--vp-c-default-soft);
426+
397427
--vp-custom-block-tip-border: transparent;
398428
--vp-custom-block-tip-text: var(--vp-c-text-1);
399429
--vp-custom-block-tip-bg: var(--vp-c-tip-soft);
400430
--vp-custom-block-tip-code-bg: var(--vp-c-tip-soft);
401431

432+
--vp-custom-block-important-border: transparent;
433+
--vp-custom-block-important-text: var(--vp-c-text-1);
434+
--vp-custom-block-important-bg: var(--vp-c-important-soft);
435+
--vp-custom-block-important-code-bg: var(--vp-c-important-soft);
436+
402437
--vp-custom-block-warning-border: transparent;
403438
--vp-custom-block-warning-text: var(--vp-c-text-1);
404439
--vp-custom-block-warning-bg: var(--vp-c-warning-soft);
@@ -409,6 +444,11 @@
409444
--vp-custom-block-danger-bg: var(--vp-c-danger-soft);
410445
--vp-custom-block-danger-code-bg: var(--vp-c-danger-soft);
411446

447+
--vp-custom-block-caution-border: transparent;
448+
--vp-custom-block-caution-text: var(--vp-c-text-1);
449+
--vp-custom-block-caution-bg: var(--vp-c-caution-soft);
450+
--vp-custom-block-caution-code-bg: var(--vp-c-caution-soft);
451+
412452
--vp-custom-block-details-border: var(--vp-custom-block-info-border);
413453
--vp-custom-block-details-text: var(--vp-custom-block-info-text);
414454
--vp-custom-block-details-bg: var(--vp-custom-block-info-bg);

Diff for: src/node/markdown/markdown.ts

+2
Original file line numberDiff line numberDiff line change
@@ -35,6 +35,7 @@ import { lineNumberPlugin } from './plugins/lineNumbers'
3535
import { linkPlugin } from './plugins/link'
3636
import { preWrapperPlugin } from './plugins/preWrapper'
3737
import { snippetPlugin } from './plugins/snippet'
38+
import { gitHubAlertsPlugin } from './plugins/githubAlerts'
3839

3940
export type { Header } from '../shared'
4041

@@ -208,6 +209,7 @@ export const createMarkdownRenderer = async (
208209
.use(preWrapperPlugin, { hasSingleTheme })
209210
.use(snippetPlugin, srcDir)
210211
.use(containerPlugin, { hasSingleTheme }, options.container)
212+
.use(gitHubAlertsPlugin, options.container)
211213
.use(imagePlugin, options.image)
212214
.use(
213215
linkPlugin,

Diff for: src/node/markdown/plugins/containers.ts

+3
Original file line numberDiff line numberDiff line change
@@ -129,8 +129,11 @@ function createCodeGroup(options: Options): ContainerArgs {
129129

130130
export interface ContainerOptions {
131131
infoLabel?: string
132+
noteLabel?: string
132133
tipLabel?: string
133134
warningLabel?: string
134135
dangerLabel?: string
135136
detailsLabel?: string
137+
importantLabel?: string
138+
cautionLabel?: string
136139
}

Diff for: src/node/markdown/plugins/githubAlerts.ts

+62
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,62 @@
1+
import type MarkdownIt from 'markdown-it'
2+
import type { ContainerOptions } from './containers'
3+
4+
const markerRE =
5+
/^\[\!(TIP|NOTE|INFO|IMPORTANT|WARNING|CAUTION|DANGER)\]([^\n\r]*)/i
6+
7+
export const gitHubAlertsPlugin = (
8+
md: MarkdownIt,
9+
options?: ContainerOptions
10+
) => {
11+
const titleMark = {
12+
tip: options?.tipLabel || 'TIP',
13+
note: options?.noteLabel || 'NOTE',
14+
info: options?.infoLabel || 'INFO',
15+
important: options?.importantLabel || 'IMPORTANT',
16+
warning: options?.warningLabel || 'WARNING',
17+
caution: options?.cautionLabel || 'CAUTION',
18+
danger: options?.dangerLabel || 'DANGER'
19+
} as Record<string, string>
20+
21+
md.core.ruler.after('block', 'github-alerts', (state) => {
22+
const tokens = state.tokens
23+
for (let i = 0; i < tokens.length; i++) {
24+
if (tokens[i].type === 'blockquote_open') {
25+
const open = tokens[i]
26+
const startIndex = i
27+
while (tokens[i]?.type !== 'blockquote_close' && i <= tokens.length)
28+
i += 1
29+
const close = tokens[i]
30+
const endIndex = i
31+
const firstContent = tokens
32+
.slice(startIndex, endIndex + 1)
33+
.find((token) => token.type === 'inline')
34+
if (!firstContent) continue
35+
const match = firstContent.content.match(markerRE)
36+
if (!match) continue
37+
const type = match[1].toLowerCase()
38+
const title = match[2].trim() || titleMark[type] || capitalize(type)
39+
firstContent.content = firstContent.content
40+
.slice(match[0].length)
41+
.trimStart()
42+
open.type = 'github_alert_open'
43+
open.tag = 'div'
44+
open.meta = {
45+
title,
46+
type
47+
}
48+
close.type = 'github_alert_close'
49+
close.tag = 'div'
50+
}
51+
}
52+
})
53+
md.renderer.rules.github_alert_open = function (tokens, idx) {
54+
const { title, type } = tokens[idx].meta
55+
const attrs = ''
56+
return `<div class="${type} custom-block github-alert"${attrs}><p class="custom-block-title">${title}</p>\n`
57+
}
58+
}
59+
60+
function capitalize(str: string) {
61+
return str.charAt(0).toUpperCase() + str.slice(1)
62+
}

0 commit comments

Comments
 (0)