import remarkGfm from 'remark-gfm' import ReactMarkdown from 'react-markdown' import { Prism as SyntaxHighlighter } from 'react-syntax-highlighter' import hljs from 'highlight.js' import { JSX } from 'react' import type { Element } from 'hast' import { tv } from 'tailwind-variants' import { CopyToClipboard } from './CopyToClipboard' const LANGUAGES_SUBSET_DETECTION = [ 'c', 'cpp', 'csharp', 'css', 'elixir', 'go', 'groovy', 'haskell', 'html', 'java', 'javascript', 'json', 'kotlin', 'markdown', 'php', 'python', 'ruby', 'rust', 'scala', 'sql', 'typescript', 'yaml', ] interface Props { children: string isInverted?: boolean } const CodeBlock = ({ language, children, }: { language: string children: string }) => { if (!children) return null return (
{String(children).replace(/\n$/, '')} {language && ( )}
) } const CodeInline = ({ language, children, }: { language: string children: string }) => { if (!children) return null return ( {String(children).replace(/\n$/, '')} ) } function Code({ children, className = '', node, }: JSX.IntrinsicElements['code'] & { node?: Element | undefined }) { if (!node?.position || !children || typeof children !== 'string') { console.error('Could not parse code node', node) return <>{children} } const detectedLanguage = hljs.highlightAuto(children, LANGUAGES_SUBSET_DETECTION).language ?? 'plaintext' const match = /language-(\w+)/.exec(className || '') const language: string = (match ? match[1] : detectedLanguage) ?? 'plaintext' if (node.position.start.line === node.position.end.line) { return {children} } return {children} } const markdownStyles = tv({ base: [ 'prose', 'prose-sm prose-code:text-sm', 'prose-h1:mb-2 prose-h1:text-lg prose-h1:font-semibold', 'prose-h2:mb-2 prose-h2:text-lg prose-h2:font-semibold', 'prose-h3:mb-2 prose-h3:text-lg prose-h3:font-semibold', 'prose-h4:mb-2 prose-h4:text-lg prose-h4:font-semibold', 'prose-h5:mb-2 prose-h5:text-lg prose-h5:font-semibold', 'prose-h6:mb-2 prose-h6:text-lg prose-h6:font-semibold', 'prose max-w-none prose-p:leading-relaxed', '[--tw-prose-pre-code:theme(textColor.secondary)]', '[--tw-prose-pre-bg:theme(colors.gray.200)]', // 'prose-pre:p-4 prose-pre:shadow-md', ], variants: { isInverted: { true: 'prose-invert', false: '', }, }, }) export function Markdown({ children, isInverted = false }: Props) { return ( {children} ) }, pre: ({ children }) => children, img({ src, alt }) { return {alt} }, }} remarkPlugins={[remarkGfm]} > {children} ) }