edgellm / frontend /src /components /chat /ChatContainer.tsx
wu981526092's picture
add
6a50e97
raw
history blame
6.66 kB
import React from 'react'
import ReactMarkdown from 'react-markdown'
import { Button } from '@/components/ui/button'
import { Textarea } from '@/components/ui/textarea'
import { Card } from '@/components/ui/card'
import { Badge } from '@/components/ui/badge'
import { Message } from '@/types/chat'
import { Send, Square, Eye, EyeOff, Brain, User, Bot } from 'lucide-react'
interface ChatContainerProps {
messages: Message[]
input: string
setInput: (value: string) => void
onSubmit: () => void
onStop: () => void
isLoading: boolean
disabled?: boolean
placeholder?: string
}
export function ChatContainer({
messages,
input,
setInput,
onSubmit,
onStop,
isLoading,
disabled = false,
placeholder = "Type your message..."
}: ChatContainerProps) {
const [showThinking, setShowThinking] = React.useState<{ [key: string]: boolean }>({})
const handleKeyPress = (e: React.KeyboardEvent) => {
if (e.key === 'Enter' && !e.shiftKey) {
e.preventDefault()
if (!isLoading && !disabled) {
onSubmit()
}
}
}
const toggleThinking = (messageId: string) => {
setShowThinking(prev => ({
...prev,
[messageId]: !prev[messageId]
}))
}
return (
<div className="flex flex-col h-full">
{/* Messages */}
<div className="flex-1 overflow-y-auto p-4 space-y-4">
{messages.length === 0 ? (
<div className="flex items-center justify-center h-full">
<div className="text-center">
<Bot className="h-12 w-12 mx-auto text-muted-foreground mb-4" />
<h3 className="text-lg font-medium mb-2">Start a conversation</h3>
<p className="text-muted-foreground">
Ask me anything and I'll help you out!
</p>
</div>
</div>
) : (
messages.map((message) => (
<div key={message.id} className="space-y-3">
<div className={`flex items-start gap-3 ${
message.role === 'user' ? 'justify-end' : 'justify-start'
}`}>
{message.role !== 'user' && (
<div className="w-8 h-8 rounded-full bg-primary flex items-center justify-center flex-shrink-0">
<Bot className="h-4 w-4 text-primary-foreground" />
</div>
)}
<Card className={`max-w-[80%] ${
message.role === 'user'
? 'bg-primary text-primary-foreground'
: 'bg-muted'
}`}>
<div className="p-4">
<div className="flex items-center gap-2 mb-2">
{message.role === 'user' ? (
<User className="h-4 w-4" />
) : (
<Bot className="h-4 w-4" />
)}
<span className="text-sm font-medium capitalize">
{message.role === 'user' ? 'You' : 'Assistant'}
</span>
{message.model_used && (
<Badge variant="outline" className="text-xs">
{message.model_used}
</Badge>
)}
</div>
{/* Thinking content */}
{message.thinking_content && message.supports_thinking && (
<div className="mb-3">
<Button
variant="ghost"
size="sm"
onClick={() => toggleThinking(message.id)}
className="p-1 h-auto"
>
<Brain className="h-3 w-3 mr-1" />
{showThinking[message.id] ? (
<>
<EyeOff className="h-3 w-3 mr-1" />
Hide thinking
</>
) : (
<>
<Eye className="h-3 w-3 mr-1" />
Show thinking
</>
)}
</Button>
{showThinking[message.id] && (
<div className="mt-2 p-3 bg-background/50 rounded border-l-4 border-blue-500">
<div className="text-xs text-muted-foreground mb-1">Thinking process:</div>
<ReactMarkdown
components={{
p: ({ children }) => <p className="text-sm prose prose-sm max-w-none">{children}</p>
}}
>
{message.thinking_content}
</ReactMarkdown>
</div>
)}
</div>
)}
{/* Main content */}
<ReactMarkdown
components={{
p: ({ children }) => <p className="prose prose-sm max-w-none">{children}</p>
}}
>
{message.content}
</ReactMarkdown>
</div>
</Card>
{message.role === 'user' && (
<div className="w-8 h-8 rounded-full bg-muted flex items-center justify-center flex-shrink-0">
<User className="h-4 w-4" />
</div>
)}
</div>
</div>
))
)}
</div>
{/* Input area */}
<div className="border-t p-4">
<div className="flex gap-2">
<Textarea
value={input}
onChange={(e) => setInput(e.target.value)}
onKeyPress={handleKeyPress}
placeholder={placeholder}
disabled={disabled}
className="min-h-[60px] resize-none"
/>
{isLoading ? (
<Button onClick={onStop} variant="outline" size="icon">
<Square className="h-4 w-4" />
</Button>
) : (
<Button
onClick={onSubmit}
disabled={disabled || !input.trim()}
size="icon"
>
<Send className="h-4 w-4" />
</Button>
)}
</div>
</div>
</div>
)
}