import { useMemo } from "react"; interface SimpleMarkdownProps { content: string; className?: string; } /** * A simplistic markdown renderer that handles: * - Newlines (paragraphs) * - Bold (**text**) * - Inline code (`code`) * - Code blocks (```code```) * - Headers (# ## ###) * - Lists (- item) */ export function SimpleMarkdown({ content, className = "" }: SimpleMarkdownProps) { const rendered = useMemo(() => { if (!content) return null; // Split by code blocks first to handle them separately const parts = content.split(/(```[\s\S]*?```)/g); return parts.map((part, partIndex) => { // Handle code blocks if (part.startsWith("```") && part.endsWith("```")) { const code = part.slice(3, -3).replace(/^\w+\n/, ""); // Remove language hint return (
            {code.trim()}
          
); } // Split by newlines and process each line const lines = part.split("\n"); return lines.map((line, lineIndex) => { const key = `${partIndex}-${lineIndex}`; // Skip empty lines but add spacing if (!line.trim()) { return
; } // Headers if (line.startsWith("### ")) { return (
{processInline(line.slice(4))}
); } if (line.startsWith("## ")) { return (
{processInline(line.slice(3))}
); } if (line.startsWith("# ")) { return (
{processInline(line.slice(2))}
); } // List items if (line.match(/^[-*]\s/)) { return (
- {processInline(line.slice(2))}
); } // Numbered list items if (line.match(/^\d+\.\s/)) { const match = line.match(/^(\d+)\.\s(.*)$/); if (match) { return (
{match[1]}. {processInline(match[2])}
); } } // Regular paragraph return
{processInline(line)}
; }); }); }, [content]); return
{rendered}
; } /** * Process inline markdown: bold, inline code */ function processInline(text: string): React.ReactNode { if (!text) return null; // Split by inline code and bold patterns const parts: React.ReactNode[] = []; let remaining = text; let keyIndex = 0; while (remaining.length > 0) { // Check for inline code first const codeMatch = remaining.match(/^(.*?)`([^`]+)`(.*)$/); if (codeMatch) { if (codeMatch[1]) { parts.push(...processInlineBold(codeMatch[1], keyIndex++)); } parts.push( {codeMatch[2]} ); remaining = codeMatch[3]; continue; } // No more inline code, process bold in remaining text parts.push(...processInlineBold(remaining, keyIndex)); break; } return parts.length === 1 ? parts[0] : parts; } /** * Process bold text (**text**) */ function processInlineBold(text: string, startKey: number): React.ReactNode[] { const parts: React.ReactNode[] = []; let remaining = text; let keyIndex = startKey; while (remaining.length > 0) { const boldMatch = remaining.match(/^(.*?)\*\*([^*]+)\*\*(.*)$/); if (boldMatch) { if (boldMatch[1]) { parts.push({boldMatch[1]}); } parts.push( {boldMatch[2]} ); remaining = boldMatch[3]; continue; } // No more bold patterns if (remaining) { parts.push({remaining}); } break; } return parts; }