Barazo default frontend
barazo.forum
1/**
2 * MarkdownContent - Renders markdown content with DOMPurify sanitization.
3 * Used for topic and reply content display.
4 * @see specs/prd-web.md Section 4 (Topic Components)
5 */
6
7import { sanitize } from 'isomorphic-dompurify'
8import { marked } from 'marked'
9import { cn } from '@/lib/utils'
10
11interface MarkdownContentProps {
12 content: string
13 className?: string
14}
15
16// Configure marked for safe defaults
17marked.setOptions({
18 breaks: true,
19 gfm: true,
20})
21
22// Configure marked renderer for links
23const renderer = new marked.Renderer()
24renderer.link = ({ href, text }: { href: string; text: string }) => {
25 return `<a href="${href}" rel="noopener noreferrer">${text}</a>`
26}
27
28marked.use({ renderer })
29
30/**
31 * Renders markdown content, sanitized against XSS.
32 * Supports: headings, bold, italic, links, code blocks, lists, blockquotes.
33 */
34export function MarkdownContent({ content, className }: MarkdownContentProps) {
35 const rawHtml = marked.parse(content, { async: false }) as string
36
37 const cleanHtml = sanitize(rawHtml, {
38 ALLOWED_TAGS: [
39 'p',
40 'br',
41 'strong',
42 'em',
43 'a',
44 'code',
45 'pre',
46 'blockquote',
47 'ul',
48 'ol',
49 'li',
50 'h1',
51 'h2',
52 'h3',
53 'h4',
54 'h5',
55 'h6',
56 'hr',
57 'img',
58 'table',
59 'thead',
60 'tbody',
61 'tr',
62 'th',
63 'td',
64 'del',
65 'sup',
66 'sub',
67 'span',
68 ],
69 ALLOWED_ATTR: ['href', 'src', 'alt', 'title', 'class', 'rel', 'target'],
70 ALLOW_DATA_ATTR: false,
71 })
72
73 return (
74 <div
75 className={cn(
76 'prose prose-sm max-w-none dark:prose-invert',
77 'prose-headings:text-foreground prose-p:text-foreground',
78 'prose-a:text-primary prose-a:underline prose-a:decoration-primary/50 hover:prose-a:decoration-primary',
79 'prose-code:rounded prose-code:bg-muted prose-code:px-1 prose-code:py-0.5 prose-code:text-sm',
80 'prose-pre:bg-muted prose-pre:rounded-lg',
81 'prose-blockquote:border-l-primary',
82 className
83 )}
84 dangerouslySetInnerHTML={{ __html: cleanHtml }}
85 />
86 )
87}