Spaces:
Sleeping
Sleeping
| import React, { useRef, useEffect } from 'react'; | |
| import { Message, Chat } from '../types'; | |
| import ChatMessage from './ChatMessage'; | |
| import ChatInput from './ChatInput'; | |
| interface ChatWindowProps { | |
| activeChat: Chat | null; | |
| messages: Message[]; | |
| onSendMessage: (content: string) => void; | |
| isSending: boolean; | |
| isLoading: boolean; | |
| } | |
| const ChatWindow: React.FC<ChatWindowProps> = ({ | |
| activeChat, | |
| messages, | |
| onSendMessage, | |
| isSending, | |
| isLoading, | |
| }) => { | |
| const messagesEndRef = useRef<HTMLDivElement>(null); | |
| // Auto-scroll to bottom when new messages arrive | |
| useEffect(() => { | |
| messagesEndRef.current?.scrollIntoView({ behavior: 'smooth' }); | |
| }, [messages]); | |
| return ( | |
| <div className="flex-1 flex flex-col bg-gray-50 h-full"> | |
| {/* Header */} | |
| <header className="bg-white border-b border-gray-200 px-6 py-4 flex items-center justify-between shadow-sm"> | |
| <div className="flex items-center gap-3"> | |
| <div className="bg-gradient-to-br from-indigo-500 to-purple-600 p-2 rounded-xl shadow-lg"> | |
| <svg className="w-6 h-6 text-white" fill="none" stroke="currentColor" viewBox="0 0 24 24"> | |
| <path strokeLinecap="round" strokeLinejoin="round" strokeWidth={2} d="M8 12h.01M12 12h.01M16 12h.01M21 12c0 4.418-4.03 8-9 8a9.863 9.863 0 01-4.255-.949L3 20l1.395-3.72C3.512 15.042 3 13.574 3 12c0-4.418 4.03-8 9-8s9 3.582 9 8z" /> | |
| </svg> | |
| </div> | |
| <div> | |
| <h1 className="text-xl font-bold text-gray-900"> | |
| {activeChat ? activeChat.title : 'RAG Chat'} | |
| </h1> | |
| <p className="text-xs text-gray-500">Powered by AI + Document Retrieval</p> | |
| </div> | |
| </div> | |
| {activeChat && ( | |
| <div className="flex items-center gap-2 text-xs text-gray-400"> | |
| <span className="w-2 h-2 bg-green-500 rounded-full animate-pulse"></span> | |
| Online | |
| </div> | |
| )} | |
| </header> | |
| {/* Messages Area */} | |
| <div className="flex-1 overflow-y-auto px-6 py-6"> | |
| {!activeChat ? ( | |
| // No Chat Selected State | |
| <div className="h-full flex flex-col items-center justify-center text-center"> | |
| <div className="bg-gradient-to-br from-indigo-100 to-purple-100 p-6 rounded-3xl mb-6"> | |
| <svg className="w-16 h-16 text-indigo-600" fill="none" stroke="currentColor" viewBox="0 0 24 24"> | |
| <path strokeLinecap="round" strokeLinejoin="round" strokeWidth={1.5} d="M8 12h.01M12 12h.01M16 12h.01M21 12c0 4.418-4.03 8-9 8a9.863 9.863 0 01-4.255-.949L3 20l1.395-3.72C3.512 15.042 3 13.574 3 12c0-4.418 4.03-8 9-8s9 3.582 9 8z" /> | |
| </svg> | |
| </div> | |
| <h2 className="text-2xl font-bold text-gray-800 mb-2">Welcome to RAG Chat</h2> | |
| <p className="text-gray-500 max-w-md mb-6"> | |
| Start a new conversation or select an existing chat from the sidebar to continue your discussion. | |
| </p> | |
| <div className="flex flex-wrap justify-center gap-3 text-sm"> | |
| <div className="bg-white px-4 py-2 rounded-full border border-gray-200 text-gray-600"> | |
| 💡 Ask questions about your documents | |
| </div> | |
| <div className="bg-white px-4 py-2 rounded-full border border-gray-200 text-gray-600"> | |
| 📚 Context-aware responses | |
| </div> | |
| <div className="bg-white px-4 py-2 rounded-full border border-gray-200 text-gray-600"> | |
| 💬 Conversation history | |
| </div> | |
| </div> | |
| </div> | |
| ) : isLoading ? ( | |
| // Loading Messages | |
| <div className="h-full flex items-center justify-center"> | |
| <div className="flex flex-col items-center gap-4"> | |
| <div className="relative"> | |
| <div className="w-12 h-12 border-4 border-indigo-200 rounded-full animate-spin border-t-indigo-600"></div> | |
| </div> | |
| <span className="text-gray-500 text-sm">Loading messages...</span> | |
| </div> | |
| </div> | |
| ) : messages.length === 0 ? ( | |
| // Empty Chat State | |
| <div className="h-full flex flex-col items-center justify-center text-center"> | |
| <div className="bg-gradient-to-br from-emerald-100 to-teal-100 p-5 rounded-2xl mb-4"> | |
| <svg className="w-12 h-12 text-emerald-600" fill="none" stroke="currentColor" viewBox="0 0 24 24"> | |
| <path strokeLinecap="round" strokeLinejoin="round" strokeWidth={1.5} d="M7 8h10M7 12h4m1 8l-4-4H5a2 2 0 01-2-2V6a2 2 0 012-2h14a2 2 0 012 2v8a2 2 0 01-2 2h-3l-4 4z" /> | |
| </svg> | |
| </div> | |
| <h3 className="text-lg font-semibold text-gray-800 mb-1">Start the conversation</h3> | |
| <p className="text-gray-500 text-sm max-w-sm"> | |
| Ask any question and I'll search through the documents to provide you with relevant answers. | |
| </p> | |
| </div> | |
| ) : ( | |
| // Messages List | |
| <div className="max-w-4xl mx-auto space-y-6"> | |
| {messages.map((msg) => ( | |
| <ChatMessage key={msg.id} message={msg} /> | |
| ))} | |
| {/* Typing Indicator */} | |
| {isSending && ( | |
| <div className="flex gap-4"> | |
| <div className="flex-shrink-0 w-10 h-10 rounded-full bg-gradient-to-br from-emerald-500 to-teal-600 flex items-center justify-center shadow-lg"> | |
| <svg className="w-5 h-5 text-white" fill="currentColor" viewBox="0 0 24 24"> | |
| <path d="M12 2C6.48 2 2 6.48 2 12s4.48 10 10 10 10-4.48 10-10S17.52 2 12 2zm-1 17.93c-3.95-.49-7-3.85-7-7.93 0-.62.08-1.21.21-1.79L9 15v1c0 1.1.9 2 2 2v1.93zm6.9-2.54c-.26-.81-1-1.39-1.9-1.39h-1v-3c0-.55-.45-1-1-1H8v-2h2c.55 0 1-.45 1-1V7h2c1.1 0 2-.9 2-2v-.41c2.93 1.19 5 4.06 5 7.41 0 2.08-.8 3.97-2.1 5.39z" /> | |
| </svg> | |
| </div> | |
| <div className="bg-white border border-gray-100 px-4 py-3 rounded-2xl rounded-bl-md shadow-sm"> | |
| <div className="flex gap-1"> | |
| <span className="w-2 h-2 bg-gray-400 rounded-full animate-bounce" style={{ animationDelay: '0ms' }}></span> | |
| <span className="w-2 h-2 bg-gray-400 rounded-full animate-bounce" style={{ animationDelay: '150ms' }}></span> | |
| <span className="w-2 h-2 bg-gray-400 rounded-full animate-bounce" style={{ animationDelay: '300ms' }}></span> | |
| </div> | |
| </div> | |
| </div> | |
| )} | |
| <div ref={messagesEndRef} /> | |
| </div> | |
| )} | |
| </div> | |
| {/* Input Area */} | |
| <ChatInput | |
| onSendMessage={onSendMessage} | |
| isSending={isSending} | |
| disabled={!activeChat} | |
| /> | |
| </div> | |
| ); | |
| }; | |
| export default ChatWindow; | |