Bin
2025-12-16 9e0b2ba2c317b1a86212f24cbae3195ad1f3dbfa
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
import ReactMarkdown from "react-markdown";
import type { Components } from "react-markdown";
import rehypeRaw from "rehype-raw";
 
// Custom markdown components with Tailwind styling
const markdownComponents: Components = {
  // Headings
  h1: (props) => <h1 className="text-display-small font-bold mb-wide mt-wider" {...props} />,
  h2: (props) => <h2 className="text-headline-large font-bold mb-base mt-wide" {...props} />,
  h3: (props) => <h3 className="text-headline-medium font-semibold mb-base mt-wide" {...props} />,
  h4: (props) => <h4 className="text-headline-small font-semibold mb-tight mt-base" {...props} />,
  h5: (props) => <h5 className="text-title-large font-semibold mb-tight mt-base" {...props} />,
  h6: (props) => <h6 className="text-title-medium font-semibold mb-tight mt-base" {...props} />,
 
  // Paragraphs
  p: (props) => <p className="text-body-medium mb-base leading-body-medium" {...props} />,
 
  // Lists
  ul: (props) => <ul className="list-disc pl-base mb-base space-y-tighter" {...props} />,
  ol: (props) => <ol className="list-decimal pl-base mb-base space-y-tighter" {...props} />,
  li: (props) => <li className="text-body-medium pl-tight ml-base" {...props} />,
 
  // Code
  code: ({ inline, ...props }: { inline?: boolean } & React.HTMLAttributes<HTMLElement>) => {
    if (inline) {
      return (
        <code
          className="bg-neutral-emphasis text-neutral-content px-tighter py-tightest rounded-smallest font-mono text-body-smaller"
          {...props}
        />
      );
    }
    return <code className="font-mono text-body-small" {...props} />;
  },
  pre: (props) => (
    <pre
      className="bg-neutral-surface-inset border border-neutral-border rounded-small p-base mb-base overflow-x-auto"
      {...props}
    />
  ),
 
  // Blockquotes
  blockquote: (props) => (
    <blockquote
      className="border-l-4 border-primary-border pl-base ml-base mb-base italic text-neutral-content-subtle"
      {...props}
    />
  ),
 
  // Links
  a: (props) => <a className="text-primary-content hover:text-primary-content-hover underline" {...props} />,
 
  // Horizontal rule
  hr: (props) => <hr className="border-neutral-border my-wide" {...props} />,
 
  // Tables
  table: (props) => (
    <div className="overflow-x-auto mb-base">
      <table className="min-w-full border-collapse border border-neutral-border" {...props} />
    </div>
  ),
  thead: (props) => <thead className="bg-neutral-surface" {...props} />,
  tbody: (props) => <tbody {...props} />,
  tr: (props) => <tr className="border-b border-neutral-border" {...props} />,
  th: (props) => (
    <th className="border border-neutral-border px-base py-tight text-left font-semibold text-body-medium" {...props} />
  ),
  td: (props) => <td className="border border-neutral-border px-base py-tight text-body-medium" {...props} />,
 
  // Strong and emphasis
  strong: (props) => <strong className="font-bold" {...props} />,
  em: (props) => <em className="italic" {...props} />,
 
  // Strikethrough
  del: (props) => <del className="line-through text-neutral-content-subtle" {...props} />,
};
 
interface MarkdownProps {
  text: string;
  allowHtml?: boolean;
}
 
export const Markdown = ({ text, allowHtml = false }: MarkdownProps) => {
  return (
    <ReactMarkdown rehypePlugins={allowHtml ? [rehypeRaw] : undefined} components={markdownComponents}>
      {text}
    </ReactMarkdown>
  );
};