/* * @Author: ZhaoYing * @Date: 2026-02-02 15:17:31 * @Last Modified by: ZhaoYing * @Last Modified time: 2026-02-02 15:17:31 */ /** * RbMarkdown Component * * A comprehensive markdown renderer with support for: * - Standard markdown syntax (headings, lists, tables, etc.) * - Code syntax highlighting * - Math equations (KaTeX) * - Mermaid diagrams * - ECharts visualizations * - SVG rendering * - Audio/video embedding * - Interactive form elements * - HTML comments visibility toggle * - Editable mode with live preview * * @component */ import { useState, useRef, useEffect, type FC } from 'react' import { Image, Input, Select, Form, Checkbox, Radio, ColorPicker, DatePicker, TimePicker, InputNumber, Slider } from 'antd' import ReactMarkdown from 'react-markdown' import RemarkGfm from 'remark-gfm' import RemarkMath from 'remark-math' import RemarkBreaks from 'remark-breaks' import RehypeKatex from 'rehype-katex' import RehypeRaw from 'rehype-raw' import Code from './Code' import VideoBlock from './VideoBlock' import AudioBlock from './AudioBlock' import Link from './Link' import RbButton from './RbButton' /** Props interface for RbMarkdown component */ interface RbMarkdownProps { /** Markdown content to render */ content: string; /** Whether to display HTML comments (default: false) */ showHtmlComments?: boolean; /** Whether the content is editable (default: false) */ editable?: boolean; /** Callback fired when content changes in edit mode */ onContentChange?: (content: string) => void; /** Additional CSS classes */ className?: string; } /** Custom component mappings for markdown elements */ const components = { h1: ({ children, ...props }: any) =>

{children}

, h2: ({ children, ...props }: any) =>

{children}

, h3: ({ children, ...props }: any) =>

{children}

, h4: ({ children, ...props }: any) =>

{children}

, h5: ({ children, ...props }: any) =>
{children}
, h6: ({ children, ...props }: any) =>
{children}
, ul: ({ children, ...props }: any) => , ol: ({ children, ...props }: any) =>
    {children}
, li: ({ children, ...props }: any) =>
  • {children}
  • , blockquote: ({ children, ...props }: any) =>
    {children}
    , p: ({ children, ...props }: any) =>

    {children}

    , strong: ({ children, ...props }: any) => {children}, em: ({ children, ...props }: any) => {children}, del: ({ children, ...props }: any) => {children}, span: ({ children, style, ...restProps }: any) => { // Apply special styling for HTML comment spans if (style?.color === '#999') { return {children} } return {children} }, code: ({ children, className, ...props }: any) => , img: ({ src, alt, ...props }: any) => {alt}, video: ({ src, ...props }: any) => , audio: ({ src, ...props }: any) => , a: ({ href, children, ...props }: any) => {children}, button: ({ children }: any) => {[children]}, table: ({ children, ...props }: any) =>
    {children}
    , tr: ({ children, ...props }: any) => {children}, th: ({ children, ...props }: any) => {children}, td: ({ children, ...props }: any) => {children}, input: ({ children, ...props }: any) => { switch (props.type) { case 'color': return case 'time': return case 'date': return case 'datetime': case 'datetime-local': return case 'week': return case 'month': return case 'number': return case 'search': return case 'range': return case 'submit': case 'button': return {[props.value || children]} case 'checkbox': return {children} case 'password': return case 'radio': return {children} default: return } }, select: ({ children, ...props }: any) => , textarea: ({ children, ...props }: any) => {children}, form: ({ children, ...props }: any) =>
    {children}
    , } const RbMarkdown: FC = ({ content, showHtmlComments = false, editable = false, onContentChange, className }) => { const [editContent, setEditContent] = useState(content) const textareaRef = useRef(null) /** Sync edit content when external content changes */ useEffect(() => { setEditContent(prev => prev !== content ? content : prev) }, [content]) /** Handle textarea content changes and trigger callback */ const handleTextareaChange = (e: React.ChangeEvent) => { const newContent = e.target.value setEditContent(newContent) /** Trigger real-time content change callback */ onContentChange?.(newContent) } /** * Process content based on showHtmlComments flag * Converts HTML comments to visible text when showHtmlComments is true * Uses special span markup to display comments with styling */ const processedContent = showHtmlComments ? (editable ? editContent : content).replace(//g, (_match, commentContent) => { /** Convert to styled text using span with html-comment class */ const escaped = commentContent.trim().replace(//g, '>') return `<!-- ${escaped} -->` }) : (editable ? editContent : content) /** Render textarea in edit mode */ if (editable) { return (
    {/* Edit area with textarea */}
    ) } /** Handle keyboard shortcuts (e.g., Ctrl+C for copy) */ const handleKeyDown = (e: React.KeyboardEvent) => { if ((e.ctrlKey || e.metaKey) && e.key === 'c') { const selection = window.getSelection() if (selection && selection.toString()) { navigator.clipboard.writeText(selection.toString()) } } } /** Render markdown preview mode */ return (
    { // return (tree) => { // const iterate = (node: any) => { // if (node.type === 'element' && !node.properties?.src && node.properties?.ref && node.properties.ref.startsWith('{') && node.properties.ref.endsWith('}')) // delete node.properties.ref // if (node.children) // node.children.forEach(iterate) // } // tree.children.forEach(iterate) // } // }, ]} remarkPlugins={[RemarkGfm, RemarkMath, RemarkBreaks]} remarkRehypeOptions={{ allowDangerousHtml: true, }} > {processedContent}
    ) } export default RbMarkdown