92 lines
2.5 KiB
TypeScript
92 lines
2.5 KiB
TypeScript
import { useState, useRef, useEffect } from 'react'
|
|
import type { KeyboardEvent } from 'react'
|
|
import ReactMarkdown from 'react-markdown'
|
|
import { IconSend } from '@tabler/icons-react'
|
|
import { ToolCallCard } from './ToolCallCard'
|
|
import type { ChatMessage } from '../hooks/useChat'
|
|
|
|
interface ChatPanelProps {
|
|
messages: ChatMessage[]
|
|
isStreaming: boolean
|
|
onSend: (content: string) => void
|
|
sessionId: string | null
|
|
}
|
|
|
|
export function ChatPanel({ messages, isStreaming, onSend, sessionId }: ChatPanelProps) {
|
|
const [input, setInput] = useState('')
|
|
const messagesEndRef = useRef<HTMLDivElement>(null)
|
|
|
|
useEffect(() => {
|
|
messagesEndRef.current?.scrollIntoView({ behavior: 'smooth' })
|
|
}, [messages])
|
|
|
|
function handleSend() {
|
|
if (input.trim() && !isStreaming) {
|
|
onSend(input.trim())
|
|
setInput('')
|
|
}
|
|
}
|
|
|
|
function handleKeyDown(e: KeyboardEvent<HTMLInputElement>) {
|
|
if (e.key === 'Enter' && !e.shiftKey) {
|
|
e.preventDefault()
|
|
handleSend()
|
|
}
|
|
}
|
|
|
|
if (sessionId === null) {
|
|
return (
|
|
<div className='chat-panel'>
|
|
<div
|
|
style={{
|
|
display: 'flex',
|
|
alignItems: 'center',
|
|
justifyContent: 'center',
|
|
height: '100%',
|
|
}}
|
|
>
|
|
<span style={{ color: '#6a6a8a', fontSize: '1.1rem' }}>
|
|
Select or create a session to start researching
|
|
</span>
|
|
</div>
|
|
</div>
|
|
)
|
|
}
|
|
|
|
return (
|
|
<div className='chat-panel'>
|
|
<div className='chat-messages'>
|
|
{messages.map(message => (
|
|
<div key={message.id}>
|
|
{message.role === 'assistant' ? (
|
|
<div>
|
|
<ReactMarkdown>{message.content}</ReactMarkdown>
|
|
{message.toolCalls?.map(tc => (
|
|
<ToolCallCard key={tc.id} toolCall={tc} />
|
|
))}
|
|
</div>
|
|
) : (
|
|
<div>{message.content}</div>
|
|
)}
|
|
</div>
|
|
))}
|
|
{isStreaming && messages.length > 0 && <div>Researching...</div>}
|
|
<div ref={messagesEndRef} />
|
|
</div>
|
|
<div className='chat-input-area'>
|
|
<input
|
|
placeholder='Ask a research question...'
|
|
value={input}
|
|
onChange={e => setInput(e.target.value)}
|
|
onKeyDown={handleKeyDown}
|
|
disabled={isStreaming}
|
|
autoFocus
|
|
/>
|
|
<button onClick={handleSend} disabled={isStreaming || !input.trim()}>
|
|
<IconSend size={18} />
|
|
</button>
|
|
</div>
|
|
</div>
|
|
)
|
|
}
|