import type { Root as MdastRoot } from "mdast"; import type { Text } from "mdast"; import { visit, SKIP } from "unist-util-visit"; import { splitInlineShortcodes } from "@/lib/inlineShortcodes"; const QUICK_RE = /==|\[(?:kbd|color|bg)[=\]]/i; type MdastParent = { children: Array>; type: string }; /** 行内短代码 → mdast 节点(mdast-util-to-hast 支持 data.hName / data.hProperties) */ function segToNodes( seg: Exclude, null>[number] ): Record[] { if (seg.type === "text") return [{ type: "text", value: seg.value }]; if (seg.type === "mark" || seg.type === "kbd") { return [ { type: "emphasis", data: { hName: seg.type }, children: [{ type: "text", value: seg.value }], }, ]; } const style = [ seg.fg ? `color:${seg.fg}` : null, seg.bg ? `background-color:${seg.bg}` : null, ] .filter(Boolean) .join(";"); return [ { type: "emphasis", data: { hName: "span", hProperties: { style } }, children: [{ type: "text", value: seg.value }], }, ]; } /** * remark 插件:把文本节点中的 ==高亮== / [kbd] / [color] / [bg] 展开为 * mark/kbd/span 节点。code/inlineCode 是字面节点没有子文本,天然免疫; * 未命中短代码的文本原样保留。 */ export function remarkInlineShortcodes() { return (tree: MdastRoot) => { visit(tree, "text", (node: Text, index, parent) => { const p = parent as unknown as MdastParent | null; if (!p || index === undefined) return; const value = node.value; if (!QUICK_RE.test(value)) return; const segs = splitInlineShortcodes(value); if (!segs) return; const nodes = segs.flatMap(segToNodes); p.children.splice(index, 1, ...nodes); return [SKIP, index + nodes.length]; }); }; }