Spaces:
Runtime error
Runtime error
| "use client"; | |
| import { memo, PropsWithChildren } from "react"; | |
| import ReactMarkdown, { type Components } from "react-markdown"; | |
| import remarkGfm from "remark-gfm"; | |
| import remarkMath from "remark-math"; | |
| import rehypeKatex from "rehype-katex"; | |
| import { PreBlock } from "./pre-block"; | |
| import { isJson, isString, toAny } from "lib/utils"; | |
| import JsonView from "ui/json-view"; | |
| import { LinkIcon } from "lucide-react"; | |
| import { | |
| Table, | |
| TableHeader, | |
| TableBody, | |
| TableRow, | |
| TableHead, | |
| TableCell, | |
| } from "ui/table"; | |
| const FadeIn = memo(({ children }: PropsWithChildren) => { | |
| return <span className="fade-in animate-in duration-1000">{children} </span>; | |
| }); | |
| FadeIn.displayName = "FadeIn"; | |
| export const WordByWordFadeIn = memo(({ children }: PropsWithChildren) => { | |
| const childrens = [children] | |
| .flat() | |
| .flatMap((child) => (isString(child) ? child.split(" ") : child)); | |
| return childrens.map((word, index) => | |
| isString(word) ? <FadeIn key={index}>{word}</FadeIn> : word, | |
| ); | |
| }); | |
| WordByWordFadeIn.displayName = "WordByWordFadeIn"; | |
| const components: Partial<Components> = { | |
| table: ({ node, children, ...props }) => { | |
| return ( | |
| <div className="my-4"> | |
| <Table {...props}>{children}</Table> | |
| </div> | |
| ); | |
| }, | |
| thead: ({ node, children, ...props }) => { | |
| return <TableHeader {...props}>{children}</TableHeader>; | |
| }, | |
| tbody: ({ node, children, ...props }) => { | |
| return <TableBody {...props}>{children}</TableBody>; | |
| }, | |
| tr: ({ node, children, ...props }) => { | |
| return <TableRow {...props}>{children}</TableRow>; | |
| }, | |
| th: ({ node, children, ...props }) => { | |
| return ( | |
| <TableHead {...props}> | |
| <WordByWordFadeIn>{children}</WordByWordFadeIn> | |
| </TableHead> | |
| ); | |
| }, | |
| td: ({ node, children, ...props }) => { | |
| return ( | |
| <TableCell {...props}> | |
| <WordByWordFadeIn>{children}</WordByWordFadeIn> | |
| </TableCell> | |
| ); | |
| }, | |
| code: ({ children }) => { | |
| return ( | |
| <code className="text-sm rounded-md bg-accent text-primary py-1 px-2 mx-0.5"> | |
| {children} | |
| </code> | |
| ); | |
| }, | |
| blockquote: ({ children }) => { | |
| return ( | |
| <div className="px-4"> | |
| <blockquote className="relative bg-accent/30 p-6 rounded-2xl my-6 overflow-hidden border"> | |
| <WordByWordFadeIn>{children}</WordByWordFadeIn> | |
| </blockquote> | |
| </div> | |
| ); | |
| }, | |
| p: ({ children }) => { | |
| return ( | |
| <p className="leading-6 my-4 break-words"> | |
| <WordByWordFadeIn>{children}</WordByWordFadeIn> | |
| </p> | |
| ); | |
| }, | |
| pre: ({ children }) => { | |
| return ( | |
| <div className="px-4 py-2"> | |
| <PreBlock>{children}</PreBlock> | |
| </div> | |
| ); | |
| }, | |
| ol: ({ node, children, ...props }) => { | |
| return ( | |
| <ol className="px-8 list-decimal list-outside" {...props}> | |
| {children} | |
| </ol> | |
| ); | |
| }, | |
| li: ({ node, children, ...props }) => { | |
| return ( | |
| <li className="py-2 break-words" {...props}> | |
| <WordByWordFadeIn>{children}</WordByWordFadeIn> | |
| </li> | |
| ); | |
| }, | |
| ul: ({ node, children, ...props }) => { | |
| return ( | |
| <ul className="px-8 list-outside list-disc" {...props}> | |
| {children} | |
| </ul> | |
| ); | |
| }, | |
| strong: ({ node, children, ...props }) => { | |
| return ( | |
| <span className="font-semibold" {...props}> | |
| <WordByWordFadeIn>{children}</WordByWordFadeIn> | |
| </span> | |
| ); | |
| }, | |
| a: ({ node, children, ...props }) => { | |
| return ( | |
| <a | |
| className="text-primary hover:underline flex gap-1.5 items-center" | |
| target="_blank" | |
| rel="noreferrer" | |
| {...toAny(props)} | |
| > | |
| <LinkIcon className="size-3.5" /> | |
| <WordByWordFadeIn>{children}</WordByWordFadeIn> | |
| </a> | |
| ); | |
| }, | |
| h1: ({ node, children, ...props }) => { | |
| return ( | |
| <h1 className="text-3xl font-semibold mt-6 mb-2" {...props}> | |
| <WordByWordFadeIn>{children}</WordByWordFadeIn> | |
| </h1> | |
| ); | |
| }, | |
| h2: ({ node, children, ...props }) => { | |
| return ( | |
| <h2 className="text-2xl font-semibold mt-6 mb-2" {...props}> | |
| <WordByWordFadeIn>{children}</WordByWordFadeIn> | |
| </h2> | |
| ); | |
| }, | |
| h3: ({ node, children, ...props }) => { | |
| return ( | |
| <h3 className="text-xl font-semibold mt-6 mb-2" {...props}> | |
| <WordByWordFadeIn>{children}</WordByWordFadeIn> | |
| </h3> | |
| ); | |
| }, | |
| h4: ({ node, children, ...props }) => { | |
| return ( | |
| <h4 className="text-lg font-semibold mt-6 mb-2" {...props}> | |
| <WordByWordFadeIn>{children}</WordByWordFadeIn> | |
| </h4> | |
| ); | |
| }, | |
| h5: ({ node, children, ...props }) => { | |
| return ( | |
| <h5 className="text-base font-semibold mt-6 mb-2" {...props}> | |
| <WordByWordFadeIn>{children}</WordByWordFadeIn> | |
| </h5> | |
| ); | |
| }, | |
| h6: ({ node, children, ...props }) => { | |
| return ( | |
| <h6 className="text-sm font-semibold mt-6 mb-2" {...props}> | |
| <WordByWordFadeIn>{children}</WordByWordFadeIn> | |
| </h6> | |
| ); | |
| }, | |
| img: ({ node, children, ...props }) => { | |
| const { src, alt, ...rest } = props; | |
| return src ? ( | |
| // eslint-disable-next-line @next/next/no-img-element | |
| <img className="mx-auto rounded-lg" src={src} alt={alt} {...rest} /> | |
| ) : null; | |
| }, | |
| }; | |
| const NonMemoizedMarkdown = ({ children }: { children: string }) => { | |
| return ( | |
| <article className="w-full h-full relative"> | |
| {isJson(children) ? ( | |
| <JsonView data={children} /> | |
| ) : ( | |
| <ReactMarkdown | |
| components={components} | |
| remarkPlugins={[remarkGfm, remarkMath]} | |
| rehypePlugins={[rehypeKatex]} | |
| > | |
| {children} | |
| </ReactMarkdown> | |
| )} | |
| </article> | |
| ); | |
| }; | |
| export const Markdown = memo( | |
| NonMemoizedMarkdown, | |
| (prevProps, nextProps) => prevProps.children === nextProps.children, | |
| ); | |