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 (
{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;
}