ui/ markdown support
This commit is contained in:
@@ -5,6 +5,100 @@ import { Button } from "@/components/ui/button";
|
|||||||
import { UserProfile } from "@/components/auth/user-profile";
|
import { UserProfile } from "@/components/auth/user-profile";
|
||||||
import { useSession } from "@/lib/auth-client";
|
import { useSession } from "@/lib/auth-client";
|
||||||
import { useState, type ReactNode } from "react";
|
import { useState, type ReactNode } from "react";
|
||||||
|
import ReactMarkdown from "react-markdown";
|
||||||
|
import type { Components } from "react-markdown";
|
||||||
|
import type { CodeComponent } from "react-markdown/lib/ast-to-react";
|
||||||
|
|
||||||
|
const H1: React.FC<React.HTMLAttributes<HTMLHeadingElement>> = (props) => (
|
||||||
|
<h1 className="mt-2 mb-3 text-2xl font-bold" {...props} />
|
||||||
|
);
|
||||||
|
const H2: React.FC<React.HTMLAttributes<HTMLHeadingElement>> = (props) => (
|
||||||
|
<h2 className="mt-2 mb-2 text-xl font-semibold" {...props} />
|
||||||
|
);
|
||||||
|
const H3: React.FC<React.HTMLAttributes<HTMLHeadingElement>> = (props) => (
|
||||||
|
<h3 className="mt-2 mb-2 text-lg font-semibold" {...props} />
|
||||||
|
);
|
||||||
|
const Paragraph: React.FC<React.HTMLAttributes<HTMLParagraphElement>> = (
|
||||||
|
props
|
||||||
|
) => <p className="mb-3 leading-7 text-sm" {...props} />;
|
||||||
|
const UL: React.FC<React.HTMLAttributes<HTMLUListElement>> = (props) => (
|
||||||
|
<ul className="mb-3 ml-5 list-disc space-y-1 text-sm" {...props} />
|
||||||
|
);
|
||||||
|
const OL: React.FC<React.OlHTMLAttributes<HTMLOListElement>> = (props) => (
|
||||||
|
<ol className="mb-3 ml-5 list-decimal space-y-1 text-sm" {...props} />
|
||||||
|
);
|
||||||
|
const LI: React.FC<React.LiHTMLAttributes<HTMLLIElement>> = (props) => (
|
||||||
|
<li className="leading-6" {...props} />
|
||||||
|
);
|
||||||
|
const Anchor: React.FC<React.AnchorHTMLAttributes<HTMLAnchorElement>> = (
|
||||||
|
props
|
||||||
|
) => (
|
||||||
|
<a
|
||||||
|
className="underline underline-offset-2 text-primary hover:opacity-90"
|
||||||
|
rel="noreferrer noopener"
|
||||||
|
{...props}
|
||||||
|
/>
|
||||||
|
);
|
||||||
|
const Blockquote: React.FC<React.BlockquoteHTMLAttributes<HTMLElement>> = (
|
||||||
|
props
|
||||||
|
) => (
|
||||||
|
<blockquote
|
||||||
|
className="mb-3 border-l-2 border-border pl-3 text-muted-foreground"
|
||||||
|
{...props}
|
||||||
|
/>
|
||||||
|
);
|
||||||
|
const Code: CodeComponent = ({ inline, children, ...props }) => {
|
||||||
|
if (inline) {
|
||||||
|
return (
|
||||||
|
<code className="rounded bg-muted px-1 py-0.5 text-xs" {...props}>
|
||||||
|
{children}
|
||||||
|
</code>
|
||||||
|
);
|
||||||
|
}
|
||||||
|
return (
|
||||||
|
<pre className="mb-3 w-full overflow-x-auto rounded-md bg-muted p-3">
|
||||||
|
<code className="text-xs leading-5" {...props}>
|
||||||
|
{children}
|
||||||
|
</code>
|
||||||
|
</pre>
|
||||||
|
);
|
||||||
|
};
|
||||||
|
const HR: React.FC<React.HTMLAttributes<HTMLHRElement>> = (props) => (
|
||||||
|
<hr className="my-4 border-border" {...props} />
|
||||||
|
);
|
||||||
|
const Table: React.FC<React.TableHTMLAttributes<HTMLTableElement>> = (
|
||||||
|
props
|
||||||
|
) => (
|
||||||
|
<div className="mb-3 overflow-x-auto">
|
||||||
|
<table className="w-full border-collapse text-sm" {...props} />
|
||||||
|
</div>
|
||||||
|
);
|
||||||
|
const TH: React.FC<React.ThHTMLAttributes<HTMLTableCellElement>> = (props) => (
|
||||||
|
<th
|
||||||
|
className="border border-border bg-muted px-2 py-1 text-left"
|
||||||
|
{...props}
|
||||||
|
/>
|
||||||
|
);
|
||||||
|
const TD: React.FC<React.TdHTMLAttributes<HTMLTableCellElement>> = (props) => (
|
||||||
|
<td className="border border-border px-2 py-1" {...props} />
|
||||||
|
);
|
||||||
|
|
||||||
|
const markdownComponents: Components = {
|
||||||
|
h1: H1,
|
||||||
|
h2: H2,
|
||||||
|
h3: H3,
|
||||||
|
p: Paragraph,
|
||||||
|
ul: UL,
|
||||||
|
ol: OL,
|
||||||
|
li: LI,
|
||||||
|
a: Anchor,
|
||||||
|
blockquote: Blockquote,
|
||||||
|
code: Code,
|
||||||
|
hr: HR,
|
||||||
|
table: Table,
|
||||||
|
th: TH,
|
||||||
|
td: TD,
|
||||||
|
};
|
||||||
|
|
||||||
type TextPart = { type?: string; text?: string };
|
type TextPart = { type?: string; text?: string };
|
||||||
type MaybePartsMessage = {
|
type MaybePartsMessage = {
|
||||||
@@ -21,7 +115,15 @@ function renderMessageContent(message: MaybePartsMessage): ReactNode {
|
|||||||
? message.content
|
? message.content
|
||||||
: [];
|
: [];
|
||||||
return parts.map((p, idx) =>
|
return parts.map((p, idx) =>
|
||||||
p?.type === "text" && p.text ? <span key={idx}>{p.text}</span> : null
|
p?.type === "text" && p.text ? (
|
||||||
|
<ReactMarkdown
|
||||||
|
key={idx}
|
||||||
|
linkTarget="_blank"
|
||||||
|
components={markdownComponents}
|
||||||
|
>
|
||||||
|
{p.text}
|
||||||
|
</ReactMarkdown>
|
||||||
|
) : null
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
Reference in New Issue
Block a user