/** Minimal markdown renderer for PRD docs (headings, lists, tables, * fenced code blocks, bold, inline code). No external dependencies. */ import Box from '@mui/material/Box'; import Typography from '@mui/material/Typography'; function Inline({ text }: { text: string }) { const parts = text.split(/(\*\*[^*]+\*\*|`[^`]+`)/g).filter(Boolean); return ( <> {parts.map((p, i) => { if (p.startsWith('**') && p.endsWith('**')) { return {p.slice(2, -2)}; } if (p.startsWith('`') && p.endsWith('`')) { return ( {p.slice(1, -1)} ); } return {p}; })} ); } export default function Markdown({ source }: { source: string }) { const lines = source.split('\n'); const blocks: React.ReactNode[] = []; let i = 0; let key = 0; while (i < lines.length) { const line = lines[i]; if (line.startsWith('```')) { const code: string[] = []; i += 1; while (i < lines.length && !lines[i].startsWith('```')) { code.push(lines[i]); i += 1; } i += 1; blocks.push( {code.join('\n')} ); continue; } const h = line.match(/^(#{1,4})\s+(.*)$/); if (h) { const level = h[1].length; blocks.push( ); i += 1; continue; } if (/^\s*[-*]\s+/.test(line)) { const items: string[] = []; while (i < lines.length && /^\s*[-*]\s+/.test(lines[i])) { items.push(lines[i].replace(/^\s*[-*]\s+/, '')); i += 1; } blocks.push( {items.map((it, j) => ( ))} ); continue; } if (/^\s*\d+\.\s+/.test(line)) { const items: string[] = []; while (i < lines.length && /^\s*\d+\.\s+/.test(lines[i])) { items.push(lines[i].replace(/^\s*\d+\.\s+/, '')); i += 1; } blocks.push( {items.map((it, j) => ( ))} ); continue; } if (line.trim() === '') { i += 1; continue; } const para: string[] = [line]; i += 1; while (i < lines.length && lines[i].trim() !== '' && !/^(#{1,4}\s|```|\s*[-*]\s|\s*\d+\.\s)/.test(lines[i])) { para.push(lines[i]); i += 1; } blocks.push( ); } return {blocks}; }