码桶

发现社区成员的开源项目

奶狗 /

ng-webot

公开
main
ng-webot/web/src/components/Markdown.tsx
Markdown.tsx1.7 KB
import { useMemo } from 'react'
import { marked } from 'marked'
import DOMPurify from 'dompurify'
import { cn } from '@/lib/utils'

/**
 * Markdown 渲染组件(支持富文本 + 代码块),输出经 DOMPurify 消毒防 XSS。
 * 用于兑换弹窗公告等需要富文本展示的场景。
 */
export function Markdown({ content, className }: { content: string; className?: string }) {
  const html = useMemo(() => {
    try {
      const raw = marked.parse(content || '', { async: false, breaks: true }) as string
      return DOMPurify.sanitize(raw)
    } catch {
      return ''
    }
  }, [content])

  if (!content) return null
  return (
    <div
      className={cn(
        'text-sm leading-relaxed break-words space-y-2',
        // 代码块 / 行内代码
        '[&_pre]:overflow-x-auto [&_pre]:rounded-md [&_pre]:bg-zinc-900 [&_pre]:p-3 [&_pre]:text-xs [&_pre]:text-zinc-100',
        '[&_code]:font-mono [&_:not(pre)>code]:rounded [&_:not(pre)>code]:bg-muted [&_:not(pre)>code]:px-1 [&_:not(pre)>code]:py-0.5 [&_:not(pre)>code]:text-[13px]',
        // 标题 / 列表 / 引用 / 链接 / 表格
        '[&_h1]:text-base [&_h1]:font-bold [&_h2]:text-[15px] [&_h2]:font-bold [&_h3]:text-sm [&_h3]:font-semibold',
        '[&_ul]:list-disc [&_ul]:pl-5 [&_ol]:list-decimal [&_ol]:pl-5',
        '[&_blockquote]:border-l-2 [&_blockquote]:pl-3 [&_blockquote]:text-muted-foreground',
        '[&_a]:text-primary [&_a]:underline',
        '[&_table]:w-full [&_table]:text-xs [&_th]:border [&_th]:px-2 [&_th]:py-1 [&_td]:border [&_td]:px-2 [&_td]:py-1',
        '[&_img]:max-w-full [&_hr]:my-2',
        className,
      )}
      dangerouslySetInnerHTML={{ __html: html }}
    />
  )
}