@@ -10,12 +10,12 @@ import parse, {
1010 Element ,
1111 HTMLReactParserOptions ,
1212} from 'html-react-parser'
13- import { marked } from 'marked'
14- import { gfmHeadingId } from 'marked-gfm-heading-id'
15- import markedAlert from 'marked-alert'
1613import mermaid from 'mermaid'
1714import { useToast } from '~/components/ToastProvider'
1815import { twMerge } from 'tailwind-merge'
16+ import { useMarkdownHeadings } from '~/components/MarkdownHeadingContext'
17+ import { renderMarkdown } from '~/utils/markdown'
18+ import { Tabs } from '~/components/Tabs'
1919
2020const CustomHeading = ( {
2121 Comp,
@@ -194,7 +194,7 @@ export function CodeBlock({
194194 return (
195195 < div
196196 className = { twMerge (
197- 'w-full max-w-full relative not-prose border border-gray-500/20 rounded-md [&_pre]:rounded-md' ,
197+ 'codeblock w-full max-w-full relative not-prose border border-gray-500/20 rounded-md [&_pre]:rounded-md [*[data-tab]_&]:only:border-0 ' ,
198198 props . className
199199 ) }
200200 style = { props . style }
@@ -288,6 +288,35 @@ const getHighlighter = cache(async (language: string, themes: string[]) => {
288288const options : HTMLReactParserOptions = {
289289 replace : ( domNode ) => {
290290 if ( domNode instanceof Element && domNode . attribs ) {
291+ if ( domNode . name === 'md-comment-component' ) {
292+ const componentName = domNode . attribs [ 'data-component' ]
293+ const rawAttributes = domNode . attribs [ 'data-attributes' ]
294+ const attributes : Record < string , any > = { }
295+ try {
296+ Object . assign ( attributes , JSON . parse ( rawAttributes ) )
297+ } catch {
298+ // ignore JSON parse errors and fall back to empty props
299+ }
300+
301+ switch ( componentName ?. toLowerCase ( ) ) {
302+ case 'tabs' : {
303+ const tabs = attributes . tabs
304+ const panelElements = domNode . children ?. filter (
305+ ( child ) : child is Element =>
306+ child instanceof Element && child . name === 'md-tab-panel'
307+ )
308+
309+ const children = panelElements ?. map ( ( panel ) =>
310+ domToReact ( panel . children as any , options )
311+ )
312+
313+ return < Tabs tabs = { tabs } children = { children as any } />
314+ }
315+ default :
316+ return < div > { domToReact ( domNode . children as any , options ) } </ div >
317+ }
318+ }
319+
291320 const replacer = markdownComponents [ domNode . name ]
292321 if ( replacer ) {
293322 return React . createElement (
@@ -302,24 +331,35 @@ const options: HTMLReactParserOptions = {
302331 } ,
303332}
304333
305- type MarkdownProps = { rawContent ?: string ; htmlMarkup ?: string }
334+ type MarkdownProps = {
335+ rawContent ?: string
336+ htmlMarkup ?: string
337+ }
306338
307339export function Markdown ( { rawContent, htmlMarkup } : MarkdownProps ) {
308- return React . useMemo ( ( ) => {
309- if ( rawContent ) {
310- const markup = marked . use (
311- { gfm : true } ,
312- gfmHeadingId ( ) ,
313- markedAlert ( )
314- ) ( rawContent ) as string
340+ const { setHeadings } = useMarkdownHeadings ( )
315341
316- return parse ( markup , options )
342+ const rendered = React . useMemo ( ( ) => {
343+ if ( rawContent ) {
344+ return renderMarkdown ( rawContent )
317345 }
318346
319347 if ( htmlMarkup ) {
320- return parse ( htmlMarkup , options )
348+ return { markup : htmlMarkup , headings : [ ] }
321349 }
322350
323- return null
351+ return { markup : '' , headings : [ ] }
324352 } , [ rawContent , htmlMarkup ] )
353+
354+ React . useEffect ( ( ) => {
355+ setHeadings ( rendered . headings )
356+ } , [ rendered . headings , setHeadings ] )
357+
358+ return React . useMemo ( ( ) => {
359+ if ( ! rendered . markup ) {
360+ return null
361+ }
362+
363+ return parse ( rendered . markup , options )
364+ } , [ rendered . markup ] )
325365}
0 commit comments