Spaces:
Running
Running
import { memo, useMemo } from 'react'; | |
import ReactMarkdown, { type Components } from 'react-markdown'; | |
import type { BundledLanguage } from 'shiki'; | |
import { createScopedLogger } from '~/utils/logger'; | |
import { rehypePlugins, remarkPlugins, allowedHTMLElements } from '~/utils/markdown'; | |
import { Artifact } from './Artifact'; | |
import { CodeBlock } from './CodeBlock'; | |
import styles from './Markdown.module.scss'; | |
const logger = createScopedLogger('MarkdownComponent'); | |
interface MarkdownProps { | |
children: string; | |
html?: boolean; | |
limitedMarkdown?: boolean; | |
} | |
export const Markdown = memo(({ children, html = false, limitedMarkdown = false }: MarkdownProps) => { | |
logger.trace('Render'); | |
const components = useMemo(() => { | |
return { | |
div: ({ className, children, node, ...props }) => { | |
if (className?.includes('__boltArtifact__')) { | |
const messageId = node?.properties.dataMessageId as string; | |
if (!messageId) { | |
logger.error(`Invalid message id ${messageId}`); | |
} | |
return <Artifact messageId={messageId} />; | |
} | |
return ( | |
<div className={className} {...props}> | |
{children} | |
</div> | |
); | |
}, | |
pre: (props) => { | |
const { children, node, ...rest } = props; | |
const [firstChild] = node?.children ?? []; | |
if ( | |
firstChild && | |
firstChild.type === 'element' && | |
firstChild.tagName === 'code' && | |
firstChild.children[0].type === 'text' | |
) { | |
const { className, ...rest } = firstChild.properties; | |
const [, language = 'plaintext'] = /language-(\w+)/.exec(String(className) || '') ?? []; | |
return <CodeBlock code={firstChild.children[0].value} language={language as BundledLanguage} {...rest} />; | |
} | |
return <pre {...rest}>{children}</pre>; | |
}, | |
} satisfies Components; | |
}, []); | |
return ( | |
<ReactMarkdown | |
allowedElements={allowedHTMLElements} | |
className={styles.MarkdownContent} | |
components={components} | |
remarkPlugins={remarkPlugins(limitedMarkdown)} | |
rehypePlugins={rehypePlugins(html)} | |
> | |
{children} | |
</ReactMarkdown> | |
); | |
}); | |