From 4a102c6507314b28b91ff69a831ea0c976f46dc0 Mon Sep 17 00:00:00 2001 From: vsxd Date: Mon, 16 Mar 2026 20:12:52 +0800 Subject: [PATCH] Fix SKILL.md frontmatter rendering --- web/package.json | 3 + web/pnpm-lock.yaml | 58 ++++++++++++++++++++ web/src/features/skill/markdown-renderer.tsx | 20 ++++++- 3 files changed, 80 insertions(+), 1 deletion(-) diff --git a/web/package.json b/web/package.json index 492b0662..5864c4cb 100644 --- a/web/package.json +++ b/web/package.json @@ -28,14 +28,17 @@ "react-dropzone": "^15.0.0", "react-i18next": "^16.5.8", "react-markdown": "^10.1.0", + "remark-frontmatter": "^5.0.0", "rehype-highlight": "^7.0.2", "rehype-sanitize": "^6.0.0", "remark-gfm": "^4.0.1", "sonner": "^2.0.7", "tailwind-merge": "^2.2.1", + "unist-util-visit": "^5.0.0", "zustand": "^5.0.11" }, "devDependencies": { + "@types/mdast": "^4.0.4", "@types/react": "^19.0.0", "@types/react-dom": "^19.0.0", "@typescript-eslint/eslint-plugin": "^7.0.0", diff --git a/web/pnpm-lock.yaml b/web/pnpm-lock.yaml index 16dc3788..d86a7f30 100644 --- a/web/pnpm-lock.yaml +++ b/web/pnpm-lock.yaml @@ -56,6 +56,9 @@ importers: rehype-sanitize: specifier: ^6.0.0 version: 6.0.0 + remark-frontmatter: + specifier: ^5.0.0 + version: 5.0.0 remark-gfm: specifier: ^4.0.1 version: 4.0.1 @@ -65,10 +68,16 @@ importers: tailwind-merge: specifier: ^2.2.1 version: 2.6.1 + unist-util-visit: + specifier: ^5.0.0 + version: 5.1.0 zustand: specifier: ^5.0.11 version: 5.0.11(@types/react@19.2.14)(react@19.2.4)(use-sync-external-store@1.6.0(react@19.2.4)) devDependencies: + '@types/mdast': + specifier: ^4.0.4 + version: 4.0.4 '@types/react': specifier: ^19.0.0 version: 19.2.14 @@ -1362,6 +1371,9 @@ packages: fastq@1.20.1: resolution: {integrity: sha512-GGToxJ/w1x32s/D2EKND7kTil4n8OVk/9mycTc4VDza13lOvpUZTGX3mFSCtV9ksdGBVzvsyAVLM6mHFThxXxw==} + fault@2.0.1: + resolution: {integrity: sha512-WtySTkS4OKev5JtpHXnib4Gxiurzh5NCGvWrFaZ34m6JehfTUhKZvn9njTfw48t6JumVQOmrKqpmGcdwxnhqBQ==} + fdir@6.5.0: resolution: {integrity: sha512-tIbYtZbucOs0BRGqPJkshJUYdL+SDH7dVM8gjy+ERp3WAUjLEFJE+02kanyHtwjWOnwrKYBiwAmM0p4kLJAnXg==} engines: {node: '>=12.0.0'} @@ -1394,6 +1406,10 @@ packages: flatted@3.4.1: resolution: {integrity: sha512-IxfVbRFVlV8V/yRaGzk0UVIcsKKHMSfYw66T/u4nTwlWteQePsxe//LjudR1AMX4tZW3WFCh3Zqa/sjlqpbURQ==} + format@0.2.2: + resolution: {integrity: sha512-wzsgA6WOq+09wrU1tsJ09udeR/YZRaeArL9e1wPbFg3GG2yDnC2ldKpxs4xunpFF9DgqCqOIra3bc1HWrJ37Ww==} + engines: {node: '>=0.4.x'} + fraction.js@5.3.4: resolution: {integrity: sha512-1X1NTtiJphryn/uLQz3whtY6jK3fTqoE3ohKs0tT+Ujr1W59oopxmoEh7Lu5p6vBaPbgoM0bzveAW4Qi5RyWDQ==} @@ -1654,6 +1670,9 @@ packages: mdast-util-from-markdown@2.0.3: resolution: {integrity: sha512-W4mAWTvSlKvf8L6J+VN9yLSqQ9AOAAvHuoDAmPkz4dHf553m5gVj2ejadHJhoJmcmxEnOv6Pa8XJhpxE93kb8Q==} + mdast-util-frontmatter@2.0.1: + resolution: {integrity: sha512-LRqI9+wdgC25P0URIJY9vwocIzCcksduHQ9OF2joxQoyTNVduwLAFUzjoopuRJbJAReaKrNQKAZKL3uCMugWJA==} + mdast-util-gfm-autolink-literal@2.0.1: resolution: {integrity: sha512-5HVP2MKaP6L+G6YaxPNjuL0BPrq9orG3TsrZ9YXbA3vDw/ACI4MEsnoDpn6ZNm7GnZgtAcONJyPhOP8tNJQavQ==} @@ -1700,6 +1719,9 @@ packages: micromark-core-commonmark@2.0.3: resolution: {integrity: sha512-RDBrHEMSxVFLg6xvnXmb1Ayr2WzLAWjeSATAoxwKYJV94TeNavgoIdA0a9ytzDSVzBy2YKFK+emCPOEibLeCrg==} + micromark-extension-frontmatter@2.0.0: + resolution: {integrity: sha512-C4AkuM3dA58cgZha7zVnuVxBhDsbttIMiytjgsM2XbHAB2faRVaHRle40558FBN+DJcrLNCoqG5mlrpdU4cRtg==} + micromark-extension-gfm-autolink-literal@2.1.0: resolution: {integrity: sha512-oOg7knzhicgQ3t4QCjCWgTmfNhvQbDDnJeVu9v81r7NltNCVmhPy1fJRX27pISafdjL+SVc4d3l48Gb6pbRypw==} @@ -2063,6 +2085,9 @@ packages: rehype-sanitize@6.0.0: resolution: {integrity: sha512-CsnhKNsyI8Tub6L4sm5ZFsme4puGfc6pYylvXo1AeqaGbjOYyzNv3qZPwvs0oMJ39eryyeOdmxwUIo94IpEhqg==} + remark-frontmatter@5.0.0: + resolution: {integrity: sha512-XTFYvNASMe5iPN0719nPrdItC9aU0ssC4v14mH1BCi1u0n1gAocqcujWUrByftZTbLhRtiKRyjYTSIOcr69UVQ==} + remark-gfm@4.0.1: resolution: {integrity: sha512-1quofZ2RQ9EWdeN34S79+KExV1764+wCUGop5CPL1WGdD0ocPpu91lzPGbwWMECpEpd42kJGQwzRfyov9j4yNg==} @@ -3674,6 +3699,10 @@ snapshots: dependencies: reusify: 1.1.0 + fault@2.0.1: + dependencies: + format: 0.2.2 + fdir@6.5.0(picomatch@4.0.3): optionalDependencies: picomatch: 4.0.3 @@ -3703,6 +3732,8 @@ snapshots: flatted@3.4.1: {} + format@0.2.2: {} + fraction.js@5.3.4: {} fs.realpath@1.0.0: {} @@ -3970,6 +4001,17 @@ snapshots: transitivePeerDependencies: - supports-color + mdast-util-frontmatter@2.0.1: + dependencies: + '@types/mdast': 4.0.4 + devlop: 1.1.0 + escape-string-regexp: 5.0.0 + mdast-util-from-markdown: 2.0.3 + mdast-util-to-markdown: 2.1.2 + micromark-extension-frontmatter: 2.0.0 + transitivePeerDependencies: + - supports-color + mdast-util-gfm-autolink-literal@2.0.1: dependencies: '@types/mdast': 4.0.4 @@ -4120,6 +4162,13 @@ snapshots: micromark-util-symbol: 2.0.1 micromark-util-types: 2.0.2 + micromark-extension-frontmatter@2.0.0: + dependencies: + fault: 2.0.1 + micromark-util-character: 2.1.1 + micromark-util-symbol: 2.0.1 + micromark-util-types: 2.0.2 + micromark-extension-gfm-autolink-literal@2.1.0: dependencies: micromark-util-character: 2.1.1 @@ -4558,6 +4607,15 @@ snapshots: '@types/hast': 3.0.4 hast-util-sanitize: 5.0.2 + remark-frontmatter@5.0.0: + dependencies: + '@types/mdast': 4.0.4 + mdast-util-frontmatter: 2.0.1 + micromark-extension-frontmatter: 2.0.0 + unified: 11.0.5 + transitivePeerDependencies: + - supports-color + remark-gfm@4.0.1: dependencies: '@types/mdast': 4.0.4 diff --git a/web/src/features/skill/markdown-renderer.tsx b/web/src/features/skill/markdown-renderer.tsx index 5e01e119..c0da8d07 100644 --- a/web/src/features/skill/markdown-renderer.tsx +++ b/web/src/features/skill/markdown-renderer.tsx @@ -1,13 +1,31 @@ import ReactMarkdown from 'react-markdown' import rehypeHighlight from 'rehype-highlight' import rehypeSanitize from 'rehype-sanitize' +import remarkFrontmatter from 'remark-frontmatter' import remarkGfm from 'remark-gfm' +import type { Root } from 'mdast' +import { visit } from 'unist-util-visit' interface MarkdownRendererProps { content: string className?: string } +function remarkStripFrontmatter() { + return (tree: Root) => { + visit(tree, (node, index, parent) => { + if (!parent || index === undefined) { + return + } + + const nodeType = String(node.type) + if (nodeType === 'yaml' || nodeType === 'toml') { + parent.children.splice(index, 1) + } + }) + } +} + export function MarkdownRenderer({ content, className }: MarkdownRendererProps) { const containerClassName = [ className, @@ -19,7 +37,7 @@ export function MarkdownRenderer({ content, className }: MarkdownRendererProps) return (
(