Skip to content

Commit

Permalink
feat: support GitHub-flavored alerts (#3482)
Browse files Browse the repository at this point in the history
Co-authored-by: Divyansh Singh <40380293+brc-dd@users.noreply.github.com>
  • Loading branch information
antfu and brc-dd committed Jan 21, 2024
1 parent 77a318c commit ac87d19
Show file tree
Hide file tree
Showing 6 changed files with 203 additions and 0 deletions.
36 changes: 36 additions & 0 deletions docs/guide/markdown.md
Expand Up @@ -263,6 +263,42 @@ Wraps in a <div class="vp-raw">
})
```

## GitHub-flavored Alerts

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).

```md
> [!NOTE]
> Highlights information that users should take into account, even when skimming.

> [!TIP]
> Optional information to help a user be more successful.

> [!IMPORTANT]
> Crucial information necessary for users to succeed.

> [!WARNING]
> Critical content demanding immediate user attention due to potential risks.

> [!CAUTION]
> Negative potential consequences of an action.
```

> [!NOTE]
> Highlights information that users should take into account, even when skimming.
> [!TIP]
> Optional information to help a user be more successful.
> [!IMPORTANT]
> Crucial information necessary for users to succeed.
> [!WARNING]
> Critical content demanding immediate user attention due to potential risks.
> [!CAUTION]
> Negative potential consequences of an action.
## Syntax Highlighting in Code Blocks

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:
Expand Down
60 changes: 60 additions & 0 deletions src/client/theme-default/styles/components/custom-block.css
Expand Up @@ -27,6 +27,26 @@
background-color: var(--vp-custom-block-info-code-bg);
}

.custom-block.note {
border-color: var(--vp-custom-block-note-border);
color: var(--vp-custom-block-note-text);
background-color: var(--vp-custom-block-note-bg);
}

.custom-block.note a,
.custom-block.note code {
color: var(--vp-c-brand-1);
}

.custom-block.note a:hover,
.custom-block.note a:hover > code {
color: var(--vp-c-brand-2);
}

.custom-block.note code {
background-color: var(--vp-custom-block-note-code-bg);
}

.custom-block.tip {
border-color: var(--vp-custom-block-tip-border);
color: var(--vp-custom-block-tip-text);
Expand All @@ -47,6 +67,26 @@
background-color: var(--vp-custom-block-tip-code-bg);
}

.custom-block.important {
border-color: var(--vp-custom-block-important-border);
color: var(--vp-custom-block-important-text);
background-color: var(--vp-custom-block-important-bg);
}

.custom-block.important a,
.custom-block.important code {
color: var(--vp-c-important-1);
}

.custom-block.important a:hover,
.custom-block.important a:hover > code {
color: var(--vp-c-important-2);
}

.custom-block.important code {
background-color: var(--vp-custom-block-important-code-bg);
}

.custom-block.warning {
border-color: var(--vp-custom-block-warning-border);
color: var(--vp-custom-block-warning-text);
Expand Down Expand Up @@ -87,6 +127,26 @@
background-color: var(--vp-custom-block-danger-code-bg);
}

.custom-block.caution {
border-color: var(--vp-custom-block-caution-border);
color: var(--vp-custom-block-caution-text);
background-color: var(--vp-custom-block-caution-bg);
}

.custom-block.caution a,
.custom-block.caution code {
color: var(--vp-c-caution-1);
}

.custom-block.caution a:hover,
.custom-block.caution a:hover > code {
color: var(--vp-c-caution-2);
}

.custom-block.caution code {
background-color: var(--vp-custom-block-caution-code-bg);
}

.custom-block.details {
border-color: var(--vp-custom-block-details-border);
color: var(--vp-custom-block-details-text);
Expand Down
40 changes: 40 additions & 0 deletions src/client/theme-default/styles/vars.css
Expand Up @@ -54,6 +54,11 @@
--vp-c-indigo-3: #5672cd;
--vp-c-indigo-soft: rgba(100, 108, 255, 0.14);

--vp-c-purple-1: #6f42c1;
--vp-c-purple-2: #7e4cc9;
--vp-c-purple-3: #8e5cd9;
--vp-c-purple-soft: rgba(159, 122, 234, 0.14);

--vp-c-green-1: #18794e;
--vp-c-green-2: #299764;
--vp-c-green-3: #30a46c;
Expand Down Expand Up @@ -83,6 +88,11 @@
--vp-c-indigo-3: #3e63dd;
--vp-c-indigo-soft: rgba(100, 108, 255, 0.16);

--vp-c-purple-1: #c8abfa;
--vp-c-purple-2: #a879e6;
--vp-c-purple-3: #8e5cd9;
--vp-c-purple-soft: rgba(159, 122, 234, 0.16);

--vp-c-green-1: #3dd68c;
--vp-c-green-2: #30a46c;
--vp-c-green-3: #298459;
Expand Down Expand Up @@ -215,11 +225,21 @@
--vp-c-tip-3: var(--vp-c-brand-3);
--vp-c-tip-soft: var(--vp-c-brand-soft);

--vp-c-note-1: var(--vp-c-brand-1);
--vp-c-note-2: var(--vp-c-brand-2);
--vp-c-note-3: var(--vp-c-brand-3);
--vp-c-note-soft: var(--vp-c-brand-soft);

--vp-c-success-1: var(--vp-c-green-1);
--vp-c-success-2: var(--vp-c-green-2);
--vp-c-success-3: var(--vp-c-green-3);
--vp-c-success-soft: var(--vp-c-green-soft);

--vp-c-important-1: var(--vp-c-purple-1);
--vp-c-important-2: var(--vp-c-purple-2);
--vp-c-important-3: var(--vp-c-purple-3);
--vp-c-important-soft: var(--vp-c-purple-soft);

--vp-c-warning-1: var(--vp-c-yellow-1);
--vp-c-warning-2: var(--vp-c-yellow-2);
--vp-c-warning-3: var(--vp-c-yellow-3);
Expand All @@ -229,6 +249,11 @@
--vp-c-danger-2: var(--vp-c-red-2);
--vp-c-danger-3: var(--vp-c-red-3);
--vp-c-danger-soft: var(--vp-c-red-soft);

--vp-c-caution-1: var(--vp-c-red-1);
--vp-c-caution-2: var(--vp-c-red-2);
--vp-c-caution-3: var(--vp-c-red-3);
--vp-c-caution-soft: var(--vp-c-red-soft);
}

/**
Expand Down Expand Up @@ -394,11 +419,21 @@
--vp-custom-block-info-bg: var(--vp-c-default-soft);
--vp-custom-block-info-code-bg: var(--vp-c-default-soft);

--vp-custom-block-note-border: transparent;
--vp-custom-block-note-text: var(--vp-c-text-1);
--vp-custom-block-note-bg: var(--vp-c-default-soft);
--vp-custom-block-note-code-bg: var(--vp-c-default-soft);

--vp-custom-block-tip-border: transparent;
--vp-custom-block-tip-text: var(--vp-c-text-1);
--vp-custom-block-tip-bg: var(--vp-c-tip-soft);
--vp-custom-block-tip-code-bg: var(--vp-c-tip-soft);

--vp-custom-block-important-border: transparent;
--vp-custom-block-important-text: var(--vp-c-text-1);
--vp-custom-block-important-bg: var(--vp-c-important-soft);
--vp-custom-block-important-code-bg: var(--vp-c-important-soft);

--vp-custom-block-warning-border: transparent;
--vp-custom-block-warning-text: var(--vp-c-text-1);
--vp-custom-block-warning-bg: var(--vp-c-warning-soft);
Expand All @@ -409,6 +444,11 @@
--vp-custom-block-danger-bg: var(--vp-c-danger-soft);
--vp-custom-block-danger-code-bg: var(--vp-c-danger-soft);

--vp-custom-block-caution-border: transparent;
--vp-custom-block-caution-text: var(--vp-c-text-1);
--vp-custom-block-caution-bg: var(--vp-c-caution-soft);
--vp-custom-block-caution-code-bg: var(--vp-c-caution-soft);

--vp-custom-block-details-border: var(--vp-custom-block-info-border);
--vp-custom-block-details-text: var(--vp-custom-block-info-text);
--vp-custom-block-details-bg: var(--vp-custom-block-info-bg);
Expand Down
2 changes: 2 additions & 0 deletions src/node/markdown/markdown.ts
Expand Up @@ -35,6 +35,7 @@ import { lineNumberPlugin } from './plugins/lineNumbers'
import { linkPlugin } from './plugins/link'
import { preWrapperPlugin } from './plugins/preWrapper'
import { snippetPlugin } from './plugins/snippet'
import { gitHubAlertsPlugin } from './plugins/githubAlerts'

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

Expand Down Expand Up @@ -208,6 +209,7 @@ export const createMarkdownRenderer = async (
.use(preWrapperPlugin, { hasSingleTheme })
.use(snippetPlugin, srcDir)
.use(containerPlugin, { hasSingleTheme }, options.container)
.use(gitHubAlertsPlugin, options.container)
.use(imagePlugin, options.image)
.use(
linkPlugin,
Expand Down
3 changes: 3 additions & 0 deletions src/node/markdown/plugins/containers.ts
Expand Up @@ -129,8 +129,11 @@ function createCodeGroup(options: Options): ContainerArgs {

export interface ContainerOptions {
infoLabel?: string
noteLabel?: string
tipLabel?: string
warningLabel?: string
dangerLabel?: string
detailsLabel?: string
importantLabel?: string
cautionLabel?: string
}
62 changes: 62 additions & 0 deletions src/node/markdown/plugins/githubAlerts.ts
@@ -0,0 +1,62 @@
import type MarkdownIt from 'markdown-it'
import type { ContainerOptions } from './containers'

const markerRE =
/^\[\!(TIP|NOTE|INFO|IMPORTANT|WARNING|CAUTION|DANGER)\]([^\n\r]*)/i

export const gitHubAlertsPlugin = (
md: MarkdownIt,
options?: ContainerOptions
) => {
const titleMark = {
tip: options?.tipLabel || 'TIP',
note: options?.noteLabel || 'NOTE',
info: options?.infoLabel || 'INFO',
important: options?.importantLabel || 'IMPORTANT',
warning: options?.warningLabel || 'WARNING',
caution: options?.cautionLabel || 'CAUTION',
danger: options?.dangerLabel || 'DANGER'
} as Record<string, string>

md.core.ruler.after('block', 'github-alerts', (state) => {
const tokens = state.tokens
for (let i = 0; i < tokens.length; i++) {
if (tokens[i].type === 'blockquote_open') {
const open = tokens[i]
const startIndex = i
while (tokens[i]?.type !== 'blockquote_close' && i <= tokens.length)
i += 1
const close = tokens[i]
const endIndex = i
const firstContent = tokens
.slice(startIndex, endIndex + 1)
.find((token) => token.type === 'inline')
if (!firstContent) continue
const match = firstContent.content.match(markerRE)
if (!match) continue
const type = match[1].toLowerCase()
const title = match[2].trim() || titleMark[type] || capitalize(type)
firstContent.content = firstContent.content
.slice(match[0].length)
.trimStart()
open.type = 'github_alert_open'
open.tag = 'div'
open.meta = {
title,
type
}
close.type = 'github_alert_close'
close.tag = 'div'
}
}
})
md.renderer.rules.github_alert_open = function (tokens, idx) {
const { title, type } = tokens[idx].meta
const attrs = ''
return `<div class="${type} custom-block github-alert"${attrs}><p class="custom-block-title">${title}</p>\n`
}
}

function capitalize(str: string) {
return str.charAt(0).toUpperCase() + str.slice(1)
}

0 comments on commit ac87d19

Please sign in to comment.