diff --git a/docs/guide/markdown.md b/docs/guide/markdown.md index 7dccbe7e..25a7ee66 100644 --- a/docs/guide/markdown.md +++ b/docs/guide/markdown.md @@ -263,6 +263,42 @@ Wraps in a
}) ``` +## 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: diff --git a/src/client/theme-default/styles/components/custom-block.css b/src/client/theme-default/styles/components/custom-block.css index 7af89807..a960381c 100644 --- a/src/client/theme-default/styles/components/custom-block.css +++ b/src/client/theme-default/styles/components/custom-block.css @@ -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); @@ -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); @@ -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); diff --git a/src/client/theme-default/styles/vars.css b/src/client/theme-default/styles/vars.css index d85304cd..80525155 100644 --- a/src/client/theme-default/styles/vars.css +++ b/src/client/theme-default/styles/vars.css @@ -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; @@ -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; @@ -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); @@ -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); } /** @@ -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); @@ -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); diff --git a/src/node/markdown/markdown.ts b/src/node/markdown/markdown.ts index 5b6978c5..bf81f38d 100644 --- a/src/node/markdown/markdown.ts +++ b/src/node/markdown/markdown.ts @@ -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' @@ -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, diff --git a/src/node/markdown/plugins/containers.ts b/src/node/markdown/plugins/containers.ts index 2a70e217..5008850b 100644 --- a/src/node/markdown/plugins/containers.ts +++ b/src/node/markdown/plugins/containers.ts @@ -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 } diff --git a/src/node/markdown/plugins/githubAlerts.ts b/src/node/markdown/plugins/githubAlerts.ts new file mode 100644 index 00000000..fd084f59 --- /dev/null +++ b/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 + + 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 `

${title}

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