From 0a59253074b5993636de129e771e29154cec37a6 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=E4=BA=8C=E5=8F=89=E6=A0=91=E6=A0=91?= Date: Fri, 9 Jan 2026 19:39:17 +0800 Subject: [PATCH] =?UTF-8?q?feat(markdown):=20=E6=B7=BB=E5=8A=A0GitHub?= =?UTF-8?q?=E9=A3=8E=E6=A0=BC=E8=AD=A6=E5=91=8A=E6=94=AF=E6=8C=81?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit 实现GitHub风格警告块([!NOTE], [!TIP]等)到标准容器指令的转换 更新astro配置使用自定义插件 --- astro.config.mjs | 4 +- src/plugins/remark-github-admonitions.js | 48 ++++++++++++++++++++++++ 2 files changed, 50 insertions(+), 2 deletions(-) create mode 100644 src/plugins/remark-github-admonitions.js diff --git a/astro.config.mjs b/astro.config.mjs index 537e5311e..454d0ff32 100644 --- a/astro.config.mjs +++ b/astro.config.mjs @@ -14,7 +14,7 @@ import rehypeExternalLinks from "rehype-external-links"; import rehypeKatex from "rehype-katex"; import rehypeSlug from "rehype-slug"; import remarkDirective from "remark-directive"; /* Handle directives */ -import remarkGithubAdmonitionsToDirectives from "remark-github-admonitions-to-directives"; +import { remarkGithubAdmonitions } from "./src/plugins/remark-github-admonitions.js"; import remarkMath from "remark-math"; import remarkSectionize from "remark-sectionize"; import { imageFallbackConfig, siteConfig } from "./src/config.ts"; @@ -122,7 +122,7 @@ export default defineConfig({ remarkMath, remarkReadingTime, remarkExcerpt, - remarkGithubAdmonitionsToDirectives, + remarkGithubAdmonitions, remarkDirective, remarkSectionize, parseDirectiveNode, diff --git a/src/plugins/remark-github-admonitions.js b/src/plugins/remark-github-admonitions.js new file mode 100644 index 000000000..6c680347e --- /dev/null +++ b/src/plugins/remark-github-admonitions.js @@ -0,0 +1,48 @@ +import { visit } from 'unist-util-visit'; + +export function remarkGithubAdmonitions() { + return (tree) => { + visit(tree, 'blockquote', (node, index, parent) => { + const children = node.children; + if (!children || children.length === 0) return; + + const firstChild = children[0]; + if (firstChild.type !== 'paragraph') return; + + const firstTextNode = firstChild.children[0]; + if (!firstTextNode || firstTextNode.type !== 'text') return; + + const text = firstTextNode.value; + // Match [!TYPE] at the start of the text, allowing for optional whitespace + const match = text.match(/^\[!(NOTE|TIP|IMPORTANT|WARNING|CAUTION)\]/i); + + if (match) { + const type = match[1].toLowerCase(); + + // Remove the [!TYPE] text + let newFirstTextValue = text.slice(match[0].length); + + // If there's a newline or space immediately after, trim it + if (newFirstTextValue.startsWith('\n') || newFirstTextValue.startsWith(' ')) { + newFirstTextValue = newFirstTextValue.slice(1); + } + + // Update the text node + firstTextNode.value = newFirstTextValue; + + // If the first paragraph becomes empty (just whitespace), remove it + if (newFirstTextValue.trim() === '' && firstChild.children.length === 1) { + node.children.shift(); + } + + // Transform the node to containerDirective + node.type = 'containerDirective'; + node.name = type; + node.attributes = {}; + + // Ensure data exists + node.data = node.data || {}; + } + }); + }; +}