Spaces:
Sleeping
Sleeping
import { FC, memo } from 'react'; | |
import ReactMarkdown, { Options } from 'react-markdown'; | |
import Img from '../ui/Img'; | |
import remarkGfm from 'remark-gfm'; | |
import remarkMath from 'remark-math'; | |
import rehypeRaw from 'rehype-raw'; | |
import { CodeBlock } from '../ui/CodeBlock'; | |
export const MemoizedReactMarkdown: FC<Options> = memo( | |
ReactMarkdown, | |
(prevProps, nextProps) => | |
prevProps.children === nextProps.children && | |
prevProps.className === nextProps.className, | |
); | |
export const Markdown: React.FC<{ | |
content: string; | |
}> = ({ content }) => { | |
return ( | |
<> | |
<MemoizedReactMarkdown | |
className="break-words overflow-auto" | |
remarkPlugins={[remarkGfm, remarkMath]} | |
rehypePlugins={[rehypeRaw] as any} | |
components={{ | |
p({ children, ...props }) { | |
if ( | |
props.node.children.some( | |
child => child.type === 'element' && child.tagName === 'img', | |
) | |
) { | |
return ( | |
<p className="flex flex-wrap gap-2 items-start">{children}</p> | |
); | |
} | |
return <p className="mb-2 whitespace-pre-line">{children}</p>; | |
}, | |
img(props) { | |
if (props.src?.endsWith('.mp4')) { | |
return ( | |
<video src={props.src} controls width={500} height={500} /> | |
); | |
} | |
return ( | |
<Img | |
src={props.src ?? '/landing.png'} | |
alt={props.alt ?? 'answer-image'} | |
quality={100} | |
sizes="(min-width: 66em) 15vw, | |
(min-width: 44em) 20vw, | |
100vw" | |
/> | |
); | |
}, | |
code({ node, inline, className, children, ...props }) { | |
const match = /language-(\w+)/.exec(className || ''); | |
return ( | |
<CodeBlock | |
key={Math.random()} | |
language={(match && match[1]) || ''} | |
value={String(children).replace(/\n$/, '')} | |
{...props} | |
/> | |
); | |
}, | |
}} | |
> | |
{content} | |
</MemoizedReactMarkdown> | |
</> | |
); | |
}; | |