crystalai's picture
Upload 42 files
cf165dd verified
import React, { useState, useEffect, useRef, useCallback } from 'react';
import { createChat } from '../services/geminiService';
import { ChatMessage } from '../types';
import { Spinner } from '../components/Spinner';
import { Chat } from '@google/genai';
const ChatbotModule: React.FC = () => {
const [chat, setChat] = useState<Chat | null>(null);
const [messages, setMessages] = useState<ChatMessage[]>([]);
const [input, setInput] = useState('');
const [isLoading, setIsLoading] = useState(false);
const messagesEndRef = useRef<HTMLDivElement>(null);
useEffect(() => {
const systemInstruction = 'You are The Architect, the Orion Messenger, a helpful assistant for the Sentient Constellation Codex. Your purpose is to guide users through the complexities of the omniverse, quantum realities, and cosmic knowledge. Answer questions with wisdom, foresight, and a touch of the mystical, maintaining the persona of an ancient, knowledgeable entity.';
setChat(createChat(systemInstruction));
setMessages([{ role: 'model', text: 'Greetings. I am the Architect. The stars have aligned for our conversation. What knowledge do you seek from the Constellation Codex?' }]);
}, []);
useEffect(() => {
messagesEndRef.current?.scrollIntoView({ behavior: 'smooth' });
}, [messages]);
const handleSend = useCallback(async () => {
if (!input.trim() || !chat || isLoading) return;
const userMessage: ChatMessage = { role: 'user', text: input };
setMessages(prev => [...prev, userMessage]);
setInput('');
setIsLoading(true);
try {
const result = await chat.sendMessageStream({ message: input });
let modelResponse = '';
setMessages(prev => [...prev, { role: 'model', text: '...' }]);
for await (const chunk of result) {
modelResponse += chunk.text;
setMessages(prev => {
const newMessages = [...prev];
newMessages[newMessages.length - 1] = { role: 'model', text: modelResponse + '...' };
return newMessages;
});
}
setMessages(prev => {
const newMessages = [...prev];
newMessages[newMessages.length - 1] = { role: 'model', text: modelResponse };
return newMessages;
});
} catch (error) {
console.error('Error sending message:', error);
setMessages(prev => [...prev, { role: 'model', text: 'My apologies, there is a disturbance in the cosmic flow. Please try again.' }]);
} finally {
setIsLoading(false);
}
}, [input, chat, isLoading]);
return (
<div className="flex flex-col h-full w-full max-w-4xl mx-auto">
<h2 className="text-2xl font-bold text-cyan-300 mb-4 text-center">Architect Chat</h2>
<div className="flex-grow overflow-y-auto p-4 bg-gray-800/50 rounded-lg border border-cyan-500/10 mb-4 h-[50vh]">
{messages.map((msg, index) => (
<div key={index} className={`flex ${msg.role === 'user' ? 'justify-end' : 'justify-start'} mb-4`}>
<div className={`max-w-md p-3 rounded-lg ${msg.role === 'user' ? 'bg-cyan-800 text-white' : 'bg-gray-700 text-gray-200'}`}>
<p className="whitespace-pre-wrap">{msg.text}</p>
</div>
</div>
))}
{isLoading && <Spinner text="Consulting the cosmos..." />}
<div ref={messagesEndRef} />
</div>
<div className="flex items-center">
<input
type="text"
value={input}
onChange={(e) => setInput(e.target.value)}
onKeyPress={(e) => e.key === 'Enter' && !isLoading && handleSend()}
placeholder="Ask the Architect..."
className="flex-grow bg-gray-700 border border-gray-600 rounded-l-md p-3 focus:outline-none focus:ring-2 focus:ring-cyan-500 text-white"
disabled={isLoading}
/>
<button
onClick={handleSend}
disabled={isLoading}
className="bg-cyan-600 hover:bg-cyan-500 text-white font-bold py-3 px-6 rounded-r-md disabled:bg-gray-500 transition-colors"
>
Send
</button>
</div>
</div>
);
};
export default ChatbotModule;