diff --git a/.changeset/few-stingrays-fetch.md b/.changeset/few-stingrays-fetch.md new file mode 100644 index 000000000..d47319df2 --- /dev/null +++ b/.changeset/few-stingrays-fetch.md @@ -0,0 +1,5 @@ +--- +'fumadocs-core': patch +--- + +Fix `remarkAdmonition` missing some types from Docusaurus diff --git a/packages/core/src/mdx-plugins/remark-admonition.ts b/packages/core/src/mdx-plugins/remark-admonition.ts index 1dc8894a5..c13c3802a 100644 --- a/packages/core/src/mdx-plugins/remark-admonition.ts +++ b/packages/core/src/mdx-plugins/remark-admonition.ts @@ -5,7 +5,6 @@ import { flattenNode } from '@/mdx-plugins/remark-utils'; export interface RemarkAdmonitionOptions { tag?: string; - types?: string[]; /** * Map type to another type @@ -21,78 +20,80 @@ export interface RemarkAdmonitionOptions { export function remarkAdmonition( options: RemarkAdmonitionOptions = {}, ): Transformer { - const types = options.types ?? ['warn', 'info', 'error']; const tag = options.tag ?? ':::'; // compatible with Docusaurus const typeMap = options.typeMap ?? { + info: 'info', + warn: 'warn', + note: 'info', tip: 'info', warning: 'warn', danger: 'error', }; - function replaceNodes(nodes: RootContent[]): RootContent[] { - if (nodes.length === 0) return nodes; - let open = -1, - end = -1; + function replaceNodes(nodes: RootContent[]) { + if (nodes.length === 0) return; - const attributes = []; + let open = -1; + let attributes = []; + // if children contain nested admonitions + let hasIntercept = false; for (let i = 0; i < nodes.length; i++) { if (nodes[i].type !== 'paragraph') continue; const text = flattenNode(nodes[i]); - const start = types.find((type) => text.startsWith(`${tag}${type}`)); + const typeName = Object.keys(typeMap).find((type) => + text.startsWith(`${tag}${type}`), + ); + + if (typeName) { + if (open !== -1) { + hasIntercept = true; + continue; + } - if (start) { - if (open !== -1) throw new Error('Nested callout is not supported'); open = i; attributes.push({ type: 'mdxJsxAttribute', name: 'type', - value: start in typeMap ? typeMap[start] : start, + value: typeMap[typeName], }); - const rest = text.slice(`${tag}${start}`.length); - if (rest.startsWith('[') && rest.endsWith(']')) { + const meta = text.slice(`${tag}${typeName}`.length); + if (meta.startsWith('[') && meta.endsWith(']')) { attributes.push({ type: 'mdxJsxAttribute', name: 'title', - value: rest.slice(1, -1), + value: meta.slice(1, -1), }); } } if (open !== -1 && text === tag) { - end = i; - break; + const children = nodes.slice(open + 1, i); + + nodes.splice(open, i - open + 1, { + type: 'mdxJsxFlowElement', + name: 'Callout', + attributes, + children: hasIntercept ? replaceNodes(children) : children, + } as RootContent); + open = -1; + hasIntercept = false; + attributes = []; + i = open + 1; } } - - if (open === -1 || end === -1) return nodes; - - return [ - ...nodes.slice(0, open), - { - type: 'mdxJsxFlowElement', - name: 'Callout', - attributes, - children: nodes.slice(open + 1, end), - } as RootContent, - ...replaceNodes(nodes.slice(end + 1)), - ]; } return (tree) => { visit(tree, (node) => { - if (!('children' in node)) return 'skip'; - - const result = replaceNodes(node.children); - if (result === node.children) return; + if (!('children' in node)) return; - node.children = result; - return 'skip'; + replaceNodes(node.children); }); }; } diff --git a/packages/core/test/fixtures/remark-admonition.md b/packages/core/test/fixtures/remark-admonition.md index 3a9533e92..abbbfc955 100644 --- a/packages/core/test/fixtures/remark-admonition.md +++ b/packages/core/test/fixtures/remark-admonition.md @@ -1,4 +1,4 @@ -:::info +:::tip Hello World diff --git a/packages/ui/src/layouts/docs/sidebar.tsx b/packages/ui/src/layouts/docs/sidebar.tsx index f84051cf8..05eb42076 100644 --- a/packages/ui/src/layouts/docs/sidebar.tsx +++ b/packages/ui/src/layouts/docs/sidebar.tsx @@ -59,7 +59,7 @@ interface InternalContext { } const itemVariants = cva( - 'relative flex flex-row items-center gap-2 rounded-md p-2 text-start text-fd-muted-foreground [overflow-wrap:anywhere] md:py-1.5 [&_svg]:size-4 [&_svg]:shrink-0', + 'relative flex flex-row items-center gap-2 rounded-md px-2 text-start text-fd-muted-foreground [overflow-wrap:anywhere] py-1.5 [&_svg]:size-4 [&_svg]:shrink-0', { variants: { active: { @@ -359,7 +359,11 @@ export function SidebarFolderLink({ } export function SidebarFolderContent(props: CollapsibleContentProps) { - return {props.children}; + return ( + +
{props.children}
+
+ ); } export function SidebarCollapseTrigger( @@ -519,12 +523,9 @@ function Border({ depth, active }: { depth: number; active?: boolean }) { return (
); }