Spaces:
Running
Running
File size: 7,914 Bytes
e2ce418 |
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 172 173 174 175 176 177 178 179 180 181 182 183 184 185 186 187 188 189 190 191 192 193 194 195 196 197 198 199 200 201 202 203 204 205 206 207 208 209 210 211 212 213 214 215 216 217 218 219 |
import React, { useState, useRef, useEffect } from 'react';
import { Send, Circle, Bot, User, Sparkles, Brain } from 'lucide-react';
import { CodetteResponseCard, CodetteResponse } from './CodetteComponents';
interface Message {
role: string;
content: string;
timestamp: Date;
metadata?: CodetteResponse;
}
interface ChatInterfaceProps {
messages: Message[];
sendMessage: (content: string) => void;
isProcessing: boolean;
darkMode: boolean;
}
const ChatInterface: React.FC<ChatInterfaceProps> = ({
messages,
sendMessage,
isProcessing,
darkMode
}) => {
const [input, setInput] = useState('');
const [isDreamMode, setIsDreamMode] = useState(false);
const messagesEndRef = useRef<HTMLDivElement>(null);
const inputRef = useRef<HTMLTextAreaElement>(null);
useEffect(() => {
messagesEndRef.current?.scrollIntoView({ behavior: 'smooth' });
}, [messages]);
useEffect(() => {
inputRef.current?.focus();
}, []);
const handleSubmit = (e: React.FormEvent) => {
e.preventDefault();
if (input.trim() && !isProcessing) {
const finalInput = isDreamMode ? `dream about ${input.trim()}` : input.trim();
sendMessage(finalInput);
setInput('');
}
};
const handleKeyDown = (e: React.KeyboardEvent) => {
if (e.key === 'Enter' && !e.shiftKey) {
e.preventDefault();
handleSubmit(e);
}
};
const toggleDreamMode = () => {
setIsDreamMode(!isDreamMode);
if (!isDreamMode) {
inputRef.current?.focus();
}
};
return (
<div className={`flex-1 flex flex-col ${darkMode ? 'bg-gray-800' : 'bg-white'} shadow-lg rounded-lg overflow-hidden transition-colors duration-300`}>
<div className={`p-4 border-b ${darkMode ? 'border-gray-700' : 'border-gray-200'}`}>
<h2 className="text-lg font-semibold flex items-center">
<Bot className="mr-2" size={18} />
Conversation with Codette
</h2>
</div>
<div className="flex-1 overflow-y-auto p-4 space-y-4">
{messages.map((message, index) => (
<div
key={index}
className={`flex ${message.role === 'user' ? 'justify-end' : 'justify-start'}`}
>
{message.role === 'assistant' && message.metadata ? (
<CodetteResponseCard response={message.metadata} />
) : (
<div
className={`max-w-[80%] rounded-lg p-3 ${
message.role === 'user'
? darkMode
? 'bg-blue-600 text-white'
: 'bg-blue-100 text-blue-900'
: message.role === 'system'
? darkMode
? 'bg-red-900 text-white'
: 'bg-red-100 text-red-900'
: darkMode
? 'bg-gray-700 text-white'
: 'bg-gray-100 text-gray-900'
}`}
>
<div className="flex items-start mb-1">
{message.role === 'user' ? (
<User className="mr-2 mt-1" size={14} />
) : message.role === 'system' ? (
<Circle className="mr-2 mt-1" size={14} />
) : (
<Bot className="mr-2 mt-1" size={14} />
)}
<div className="text-sm font-semibold">
{message.role === 'user' ? 'You' : message.role === 'system' ? 'System' : 'Codette'}
</div>
</div>
<div className="whitespace-pre-wrap">
{message.content}
</div>
<div className="text-xs opacity-70 mt-1 text-right">
{message.timestamp.toLocaleTimeString()}
</div>
</div>
)}
</div>
))}
{isProcessing && (
<div className="flex justify-start">
<div className={`rounded-lg p-3 ${
darkMode ? 'bg-gray-700 text-white' : 'bg-gray-100 text-gray-900'
}`}>
<div className="flex items-center">
<Bot className="mr-2" size={14} />
<div className="text-sm font-semibold">Codette</div>
</div>
<div className="flex items-center mt-2">
<div className="flex space-x-1">
<div className="typing-dot h-2 w-2 bg-blue-500 rounded-full animate-pulse" style={{ animationDelay: '0ms' }}></div>
<div className="typing-dot h-2 w-2 bg-blue-500 rounded-full animate-pulse" style={{ animationDelay: '300ms' }}></div>
<div className="typing-dot h-2 w-2 bg-blue-500 rounded-full animate-pulse" style={{ animationDelay: '600ms' }}></div>
</div>
<div className="ml-3 text-sm italic opacity-70">
{isDreamMode ? 'Weaving dreams through quantum threads...' : 'Processing through recursive thought loops...'}
</div>
</div>
</div>
</div>
)}
<div ref={messagesEndRef} />
</div>
<form
onSubmit={handleSubmit}
className={`p-4 border-t ${darkMode ? 'border-gray-700' : 'border-gray-200'}`}
>
<div className="flex items-center mb-2">
<button
type="button"
onClick={toggleDreamMode}
className={`flex items-center px-3 py-1 rounded-full text-sm transition-colors ${
isDreamMode
? darkMode
? 'bg-purple-600 text-white'
: 'bg-purple-100 text-purple-900'
: darkMode
? 'bg-gray-700 text-gray-300 hover:bg-gray-600'
: 'bg-gray-100 text-gray-700 hover:bg-gray-200'
}`}
>
{isDreamMode ? (
<>
<Sparkles size={14} className="mr-1" />
Dreamweaver Active
</>
) : (
<>
<Brain size={14} className="mr-1" />
Enable Dreamweaver
</>
)}
</button>
</div>
<div className="flex">
<textarea
ref={inputRef}
value={input}
onChange={(e) => setInput(e.target.value)}
onKeyDown={handleKeyDown}
placeholder={isDreamMode ? "Enter a concept for Codette to dream about..." : "Ask Codette anything..."}
className={`flex-1 resize-none border rounded-lg p-2 focus:outline-none focus:ring-2 ${
isDreamMode
? 'focus:ring-purple-500'
: 'focus:ring-blue-500'
} ${
darkMode
? 'bg-gray-700 border-gray-600 text-white placeholder-gray-400'
: 'bg-white border-gray-300 text-gray-900 placeholder-gray-500'
}`}
rows={2}
disabled={isProcessing}
/>
<button
type="submit"
disabled={!input.trim() || isProcessing}
className={`ml-2 p-2 rounded-lg transition-colors ${
!input.trim() || isProcessing
? darkMode
? 'bg-gray-700 text-gray-500'
: 'bg-gray-200 text-gray-400'
: isDreamMode
? darkMode
? 'bg-purple-600 text-white hover:bg-purple-700'
: 'bg-purple-500 text-white hover:bg-purple-600'
: darkMode
? 'bg-blue-600 text-white hover:bg-blue-700'
: 'bg-blue-500 text-white hover:bg-blue-600'
}`}
>
<Send size={20} />
</button>
</div>
</form>
</div>
);
};
export default ChatInterface; |