"use client"; import { useMemo } from "react"; interface ChatMessageProps { role: "user" | "assistant"; content: string; } /** * Lightweight inline markdown renderer — handles bold, italic, code, * bullet lists, and numbered lists without a full markdown library. */ function renderMarkdown(text: string) { const lines = text.split("\n"); const elements: React.ReactNode[] = []; let listItems: React.ReactNode[] = []; let listType: "ul" | "ol" | null = null; const flushList = () => { if (listItems.length > 0 && listType) { const Tag = listType; elements.push( {listItems} , ); listItems = []; listType = null; } }; for (const [i, line] of lines.entries()) { // Bullet list: "- item" or "* item" const bulletMatch = line.match(/^[\s]*[-*]\s+(.*)/); if (bulletMatch?.[1]) { if (listType !== "ul") flushList(); listType = "ul"; listItems.push(
  • {inlineFormat(bulletMatch[1])}
  • ); continue; } // Numbered list: "1. item" const numMatch = line.match(/^[\s]*\d+\.\s+(.*)/); if (numMatch?.[1]) { if (listType !== "ol") flushList(); listType = "ol"; listItems.push(
  • {inlineFormat(numMatch[1])}
  • ); continue; } // Not a list item — flush any pending list flushList(); // Empty line → spacing if (line.trim() === "") { elements.push(
    ); continue; } // Regular paragraph elements.push(

    {inlineFormat(line)}

    ); } flushList(); return elements; } /** Parse inline formatting: **bold**, *italic*, `code` */ function inlineFormat(text: string): React.ReactNode { // Split by inline patterns, preserving delimiters const parts: React.ReactNode[] = []; // Regex: **bold**, *italic*, `code` const regex = /(\*\*(.+?)\*\*|\*(.+?)\*|`(.+?)`)/g; let lastIndex = 0; let match: RegExpExecArray | null; while ((match = regex.exec(text)) !== null) { // Text before this match if (match.index > lastIndex) { parts.push(text.slice(lastIndex, match.index)); } if (match[2]) { // **bold** parts.push({match[2]}); } else if (match[3]) { // *italic* parts.push({match[3]}); } else if (match[4]) { // `code` parts.push( {match[4]} , ); } lastIndex = match.index + match[0].length; } // Remaining text if (lastIndex < text.length) { parts.push(text.slice(lastIndex)); } return parts.length === 1 ? parts[0] : <>{parts}; } export function ChatMessage({ role, content }: ChatMessageProps) { const isUser = role === "user"; const rendered = useMemo(() => (isUser ? null : renderMarkdown(content)), [isUser, content]); return (
    {isUser ? ( {content} ) : ( <> AI Generated
    {rendered}
    )}
    ); } export function TypingIndicator() { return (
    ); }