Files
zeekay a166e80677 Merge upstream fumadocs dev (16.6.17) with @hanzo/docs namespace rename
Merge 131 upstream commits from fuma-nama/fumadocs dev branch, bringing
framework packages to v16.6.17. All fumadocs-* and @fumadocs/* package
references renamed to @hanzo/docs-* namespace.

Upstream additions:
- packages/content/ (@hanzo/docs-content) - Fuma Content adapter
- packages/epub/ (@hanzo/docs-epub) - EPUB export support
- MarkdownCopyButton, ViewOptionsPopover in docs page layout
- Flexsearch, Orama search integration
- Shiki v4 upgrade
- Various UI framework improvements

Hanzo customizations preserved:
- apps/docs/content/ (all Hanzo service/project docs)
- apps/docs/app/ (Hanzo homepage, AI chat, subdomain routing)
- apps/docs/components/ (AI search, error boundaries, auth)
- apps/docs/lib/ (source config, OpenAPI, metadata)
- Webpack alias layer for upstream project doc imports
- MdxErrorBoundary wrapper for graceful MDX failures
2026-03-16 14:34:53 -07:00

58 lines
1.4 KiB
TypeScript

import type { TOCItemType } from '@hanzo/docs-core/toc';
import Slugger from 'github-slugger';
import parse, { type DOMNode, domToReact } from 'html-react-parser';
import { Heading } from '@hanzo/docs-ui/components/heading';
import { ServerCodeBlock } from '@hanzo/docs-ui/components/codeblock.rsc';
export function renderFromHtml(content: string) {
const toc: TOCItemType[] = [];
const slugger = new Slugger();
const node = parse(content, {
replace(node) {
if (node.type !== 'tag') return node;
const heading = /^h(\d)$/.exec(node.name);
if (heading) {
const depth = Number(heading[1]);
const string = stringify(node);
const id = slugger.slug(string);
toc.push({
title: string,
depth,
url: `#${id}`,
});
return (
<Heading as={`h${depth}` as 'h1'} id={id}>
{domToReact(node.children as DOMNode[])}
</Heading>
);
}
if (node.name === 'pre') {
return <ServerCodeBlock lang="tsx" code={stringify(node)} />;
}
},
});
return { node, toc };
}
function stringify(node: DOMNode): string {
if (node.type === 'text') return node.data;
if ('children' in node) {
return node.children
.map((node) => {
if (node.type === 'cdata' || node.type === 'root') return;
return stringify(node);
})
.filter(Boolean)
.join('');
}
return '';
}