import type { Element } from "hast"; import { ChevronDown, ChevronUp } from "lucide-react"; import { memo, useMemo } from "react"; import ReactMarkdown from "react-markdown"; import rehypeKatex from "rehype-katex"; import rehypeRaw from "rehype-raw"; import rehypeSanitize from "rehype-sanitize"; import remarkBreaks from "remark-breaks"; import remarkGfm from "remark-gfm"; import remarkMath from "remark-math"; import { cn } from "@/lib/utils"; import { useTranslate } from "@/utils/i18n"; import { rehypeHeadingId } from "@/utils/rehype-plugins/rehype-heading-id"; import { remarkDisableSetext } from "@/utils/remark-plugins/remark-disable-setext"; import { extractMentionUsernames, remarkMention } from "@/utils/remark-plugins/remark-mention"; import { remarkPreserveType } from "@/utils/remark-plugins/remark-preserve-type"; import { remarkSplitMixedTaskLists } from "@/utils/remark-plugins/remark-split-mixed-task-lists"; import { remarkTag } from "@/utils/remark-plugins/remark-tag"; import { CodeBlock } from "./CodeBlock"; import { isMentionNode, isTagNode, isTaskListItemNode } from "./ConditionalComponent"; import { COMPACT_MODE_CONFIG, SANITIZE_SCHEMA } from "./constants"; import { useCompactLabel, useCompactMode } from "./hooks"; import { Mention } from "./Mention"; import { useResolvedMentionUsernames } from "./MentionResolutionContext"; import { Blockquote, Heading, HorizontalRule, Image, InlineCode, Link, List, ListItem, Paragraph } from "./markdown"; import { Table, TableBody, TableCell, TableHead, TableHeaderCell, TableRow } from "./Table"; import { Tag } from "./Tag"; import { TaskListItem } from "./TaskListItem"; import { TrustedIframe } from "./TrustedIframe"; import type { MemoContentProps } from "./types"; function getMentionUsername(node: Element, children?: React.ReactNode): string { const dataMention = node.properties?.["data-mention"]; if (typeof dataMention === "string" && dataMention !== "") { return dataMention; } const camelDataMention = (node.properties as Record | undefined)?.dataMention; if (typeof camelDataMention === "string" && camelDataMention !== "") { return camelDataMention; } const text = Array.isArray(children) ? children.join("") : children; if (typeof text === "string" && text.startsWith("@")) { return text.slice(1).toLowerCase(); } return ""; } const MemoContent = (props: MemoContentProps) => { const { className, contentClassName, content, onClick, onDoubleClick } = props; const t = useTranslate(); const { containerRef: memoContentContainerRef, mode: showCompactMode, toggle: toggleCompactMode, } = useCompactMode(Boolean(props.compact)); const mentionUsernames = useMemo(() => extractMentionUsernames(content), [content]); const resolvedMentionUsernames = useResolvedMentionUsernames(mentionUsernames); const compactLabel = useCompactLabel(showCompactMode, t as (key: string) => string); return (
*:last-child]:mb-0", "[&_.katex-display]:max-w-full", "[&_.katex-display]:overflow-x-auto", "[&_.katex-display]:overflow-y-hidden", showCompactMode === "ALL" && "overflow-hidden", contentClassName, )} style={showCompactMode === "ALL" ? { maxHeight: `${COMPACT_MODE_CONFIG.maxHeightVh}vh` } : undefined} onMouseUp={onClick} onDoubleClick={onDoubleClick} > & { node?: Element }) => { const { node, ...rest } = inputProps; if (node && isTaskListItemNode(node)) { return ; } return ; }) as React.ComponentType>, span: ((spanProps: React.ComponentProps<"span"> & { node?: Element }) => { const { node, ...rest } = spanProps; if (node && isMentionNode(node)) { const username = getMentionUsername(node, spanProps.children); return ; } if (node && isTagNode(node)) { return ; } return ; }) as React.ComponentType>, // Headings h1: ({ children, ...props }) => ( {children} ), h2: ({ children, ...props }) => ( {children} ), h3: ({ children, ...props }) => ( {children} ), h4: ({ children, ...props }) => ( {children} ), h5: ({ children, ...props }) => ( {children} ), h6: ({ children, ...props }) => ( {children} ), // Block elements p: ({ children, ...props }) => {children}, blockquote: ({ children, ...props }) =>
{children}
, hr: (props) => , // Lists ul: ({ children, ...props }) => {children}, ol: ({ children, ...props }) => ( {children} ), li: ({ children, ...props }) => {children}, // Inline elements a: ({ children, ...props }) => {children}, code: ({ children, ...props }) => {children}, iframe: TrustedIframe as React.ComponentType>, img: ({ ...props }) => , // Code blocks pre: CodeBlock, // Tables table: ({ children, ...props }) => {children}
, thead: ({ children, ...props }) => {children}, tbody: ({ children, ...props }) => {children}, tr: ({ children, ...props }) => {children}, th: ({ children, ...props }) => {children}, td: ({ children, ...props }) => {children}, }} > {content}
{showCompactMode === "ALL" && (
)}
{showCompactMode !== undefined && (
)}
); }; export default memo(MemoContent);