'use client' import { useEffect, useRef } from 'react' import { cn } from '@/lib/utils' import type { ChatMessage } from './use-docs-chat' interface ChatMessagesProps { messages: ChatMessage[] isStreaming: boolean } /** * Message list component with auto-scroll and streaming cursor * * UI Specs (uit FO): * - User messages: rechts, bg-amber-100, rounded * - Assistant messages: links, bg-slate-100, rounded * - Streaming: pulserende cursor ▊ */ export function ChatMessages({ messages, isStreaming }: ChatMessagesProps) { const scrollRef = useRef(null) // Auto-scroll to bottom on new messages useEffect(() => { if (scrollRef.current) { scrollRef.current.scrollTop = scrollRef.current.scrollHeight } }, [messages]) return (
{messages.map((message) => ( ))}
) } interface MessageBubbleProps { message: ChatMessage isStreaming: boolean } function MessageBubble({ message, isStreaming }: MessageBubbleProps) { const isUser = message.role === 'user' return (
) } interface MessageContentProps { content: string isStreaming: boolean } function MessageContent({ content, isStreaming }: MessageContentProps) { if (!content && isStreaming) { return } // Split content into paragraphs and render with proper spacing const paragraphs = content.split('\n\n') return (
{paragraphs.map((paragraph, idx) => (

{renderInlineFormatting(paragraph)} {isStreaming && idx === paragraphs.length - 1 && }

))}
) } /** * Render basic inline formatting (bold, bullet points) */ function renderInlineFormatting(text: string) { // Handle bullet points const lines = text.split('\n') return lines.map((line, idx) => { const isBullet = line.startsWith('• ') || line.startsWith('- ') if (isBullet) { return ( {line} {idx < lines.length - 1 && '\n'} ) } // Handle **bold** text const parts = line.split(/(\*\*[^*]+\*\*)/) const formatted = parts.map((part, partIdx) => { if (part.startsWith('**') && part.endsWith('**')) { return ( {part.slice(2, -2)} ) } return part }) return ( {formatted} {idx < lines.length - 1 && '\n'} ) }) } /** * Pulsating streaming cursor */ function StreamingCursor() { return ( ) }