import { IconCheck, IconChevronDown, IconCopy, } from "@tabler/icons-react" import hljs from "highlight.js/lib/core" import json from "highlight.js/lib/languages/json" import { type ComponentProps, type ReactNode, useState } from "react" import { useTranslation } from "react-i18next" import { useCopyToClipboard } from "@/hooks/use-copy-to-clipboard" import { cn } from "@/lib/utils" import { extractCodeBlockFromPreNode, type MarkdownNode, } from "./message-code-block.utils" import { Button } from "@/components/ui/button" const CODE_LABEL_FONT_FAMILY = 'ui-monospace, "SFMono-Regular", Menlo, Monaco, Consolas, "Liberation Mono", "Courier New", "PingFang SC", "Hiragino Sans GB", "Microsoft YaHei UI", "Microsoft YaHei", monospace' hljs.registerLanguage("json", json) interface MessageCodeBlockProps { code: string language?: string | null label?: string children?: ReactNode className?: string bodyClassName?: string wrapLongLines?: boolean } interface MarkdownCodeBlockProps extends ComponentProps<"pre"> { node?: MarkdownNode } function getHighlightedHtml(code: string, language?: string | null) { if (!language) { return null } try { return hljs.highlight(code, { language }).value } catch { return null } } export function MessageCodeBlock({ code, language = null, label, children, className, bodyClassName, wrapLongLines = false, }: MessageCodeBlockProps) { const { t } = useTranslation() const { copy, isCopied } = useCopyToClipboard() const [isExpanded, setIsExpanded] = useState(true) const blockLabel = label ?? (language ? language.toLocaleLowerCase() : t("chat.codeLabel").toLocaleLowerCase()) const copyLabel = isCopied ? t("chat.copiedLabel") : t("chat.copyCode") const expandLabel = isExpanded ? t("chat.collapseCode") : t("chat.expandCode") const highlightedHtml = !children ? getHighlightedHtml(code, language) : null return (
{children ?? (
highlightedHtml ? (
) : (
{code}
)
)}
)}