| import ReactMarkdown, { type Components } from "react-markdown" |
| import rehypeRaw from "rehype-raw" |
| import remarkGfm from "remark-gfm" |
|
|
| import { cn } from "@/lib/utils" |
|
|
| type MarkdownContentProps = { |
| markdown: string |
| className?: string |
| } |
|
|
| const markdownComponents: Components = { |
| h1: ({ className, ...props }) => ( |
| <h1 |
| className={cn( |
| "mt-8 scroll-m-20 text-3xl font-semibold tracking-tight first:mt-0 sm:text-4xl", |
| className |
| )} |
| {...props} |
| /> |
| ), |
| h2: ({ className, ...props }) => ( |
| <h2 |
| className={cn( |
| "mt-8 scroll-m-20 border-b border-border/70 pb-2 text-2xl font-semibold tracking-tight first:mt-0", |
| className |
| )} |
| {...props} |
| /> |
| ), |
| h3: ({ className, ...props }) => ( |
| <h3 |
| className={cn( |
| "mt-6 scroll-m-20 text-xl font-semibold tracking-tight", |
| className |
| )} |
| {...props} |
| /> |
| ), |
| h4: ({ className, ...props }) => ( |
| <h4 className={cn("mt-5 text-lg font-semibold", className)} {...props} /> |
| ), |
| p: ({ className, ...props }) => ( |
| <p className={cn("leading-7 [&:not(:first-child)]:mt-4", className)} {...props} /> |
| ), |
| ul: ({ className, ...props }) => ( |
| <ul className={cn("my-4 ml-6 list-disc space-y-2", className)} {...props} /> |
| ), |
| ol: ({ className, ...props }) => ( |
| <ol className={cn("my-4 ml-6 list-decimal space-y-2", className)} {...props} /> |
| ), |
| li: ({ className, ...props }) => ( |
| <li className={cn("pl-1", className)} {...props} /> |
| ), |
| blockquote: ({ className, ...props }) => ( |
| <blockquote |
| className={cn( |
| "mt-4 border-l-4 border-border bg-muted/40 py-1 pl-4 italic text-foreground/80", |
| className |
| )} |
| {...props} |
| /> |
| ), |
| a: ({ className, ...props }) => ( |
| <a |
| className={cn( |
| "font-medium text-foreground underline decoration-border underline-offset-4 transition-colors hover:text-muted-foreground", |
| className |
| )} |
| rel="noreferrer" |
| target="_blank" |
| {...props} |
| /> |
| ), |
| code: ({ className, ...props }) => ( |
| <code |
| className={cn( |
| "rounded-md bg-muted px-1.5 py-0.5 font-mono text-[0.85em]", |
| className |
| )} |
| {...props} |
| /> |
| ), |
| pre: ({ className, ...props }) => ( |
| <pre |
| className={cn( |
| "mt-4 overflow-x-auto rounded-xl border border-border bg-zinc-950 px-4 py-3 font-mono text-sm text-zinc-50", |
| className |
| )} |
| {...props} |
| /> |
| ), |
| hr: ({ className, ...props }) => ( |
| <hr className={cn("my-8 border-border/70", className)} {...props} /> |
| ), |
| table: ({ className, ...props }) => ( |
| <div className="my-6 overflow-x-auto rounded-xl border border-border/70"> |
| <table className={cn("w-full border-collapse text-sm", className)} {...props} /> |
| </div> |
| ), |
| thead: ({ className, ...props }) => ( |
| <thead className={cn("bg-muted/50", className)} {...props} /> |
| ), |
| tbody: ({ className, ...props }) => ( |
| <tbody className={cn("[&_tr:last-child]:border-b-0", className)} {...props} /> |
| ), |
| tr: ({ className, ...props }) => ( |
| <tr className={cn("border-b border-border/70", className)} {...props} /> |
| ), |
| th: ({ className, ...props }) => ( |
| <th |
| className={cn( |
| "px-4 py-3 text-left font-medium text-foreground whitespace-nowrap", |
| className |
| )} |
| {...props} |
| /> |
| ), |
| td: ({ className, ...props }) => ( |
| <td |
| className={cn( |
| "px-4 py-3 align-top text-muted-foreground whitespace-pre-wrap", |
| className |
| )} |
| {...props} |
| /> |
| ), |
| img: ({ className, alt, ...props }) => ( |
| <img |
| alt={alt ?? ""} |
| className={cn( |
| "my-6 w-full rounded-2xl border border-border/70 object-cover shadow-sm", |
| className |
| )} |
| {...props} |
| /> |
| ), |
| } |
|
|
| export function MarkdownContent({ |
| markdown, |
| className, |
| }: MarkdownContentProps) { |
| return ( |
| <div className={cn("text-[15px] text-foreground", className)}> |
| <ReactMarkdown |
| components={markdownComponents} |
| rehypePlugins={[rehypeRaw]} |
| remarkPlugins={[remarkGfm]} |
| > |
| {markdown} |
| </ReactMarkdown> |
| </div> |
| ) |
| } |
|
|