import React, { useState, useRef, useEffect } from 'react';
interface Message {
id: string;
role: 'user' | 'assistant';
content: string;
timestamp: Date;
}
const ChatBubble: React.FC<{ message: Message }> = ({ message }) => {
const isUser = message.role === 'user';
return (
<div style={{
display: 'flex',
justifyContent: isUser ? 'flex-end' : 'flex-start',
marginBottom: '16px',
padding: '0 16px'
}}>
<div style={{
display: 'flex',
alignItems: 'flex-start',
gap: '12px',
maxWidth: '80%',
flexDirection: isUser ? 'row-reverse' : 'row'
}}>
<div style={{
width: '36px',
height: '36px',
borderRadius: '8px',
background: isUser ? 'linear-gradient(135deg, #667eea 0%, #764ba2 100%)' : 'linear-gradient(135deg, #11998e 0%, #38ef7d 100%)',
display: 'flex',
alignItems: 'center',
justifyContent: 'center',
color: 'white',
fontSize: '14px',
fontWeight: 'bold',
flexShrink: 0
}}>
{isUser ? 'U' : 'AI'}
</div>
<div style={{
background: isUser ? '#e3f2fd' : '#f5f5f5',
padding: '12px 16px',
borderRadius: isUser ? '16px 16px 4px 16px' : '16px 16px 16px 4px',
color: '#333',
lineHeight: '1.5'
}}>
{message.content}
</div>
</div>
</div>
);
};
const TypingIndicator: React.FC = () => (
<div style={{
display: 'flex',
alignItems: 'center',
gap: '12px',
padding: '0 16px',
marginBottom: '16px'
}}>
<div style={{
width: '36px',
height: '36px',
borderRadius: '8px',
background: 'linear-gradient(135deg, #11998e 0%, #38ef7d 100%)',
display: 'flex',
alignItems: 'center',
justifyContent: 'center',
color: 'white',
fontSize: '14px',
fontWeight: 'bold'
}}>
AI
</div>
<div style={{
background: '#f5f5f5',
padding: '16px 20px',
borderRadius: '16px 16px 16px 4px',
display: 'flex',
gap: '4px'
}}>
{[0, 1, 2].map((i) => (
<div
key={i}
style={{
width: '8px',
height: '8px',
borderRadius: '50%',
background: '#999',
animation: 'bounce 1.4s infinite ease-in-out',
animationDelay: `${i * 0.16}s`
}}
/>
))}
</div>
</div>
);
const responses = [
"That's a great question! Let me help you with that.",
"I understand what you're looking for. Here's my suggestion...",
"Interesting! Based on my knowledge, I can tell you that...",
"Thanks for asking! Here's what I think about this topic.",
"Let me break this down for you step by step."
];
export default function App() {
const [messages, setMessages] = useState<Message[]>([
{
id: '1',
role: 'assistant',
content: 'Hello! I\'m a ChatGPT-style assistant demo inspired by NextChat. How can I help you today?',
timestamp: new Date()
}
]);
const [input, setInput] = useState('');
const [isTyping, setIsTyping] = useState(false);
const messagesEndRef = useRef<HTMLDivElement>(null);
const scrollToBottom = () => {
messagesEndRef.current?.scrollIntoView({ behavior: 'smooth' });
};
useEffect(() => {
scrollToBottom();
}, [messages, isTyping]);
const handleSend = () => {
if (!input.trim() || isTyping) return;
const userMessage: Message = {
id: Date.now().toString(),
role: 'user',
content: input.trim(),
timestamp: new Date()
};
setMessages(prev => [...prev, userMessage]);
setInput('');
setIsTyping(true);
setTimeout(() => {
const aiMessage: Message = {
id: (Date.now() + 1).toString(),
role: 'assistant',
content: responses[Math.floor(Math.random() * responses.length)],
timestamp: new Date()
};
setMessages(prev => [...prev, aiMessage]);
setIsTyping(false);
}, 1500);
};
const handleKeyPress = (e: React.KeyboardEvent) => {
if (e.key === 'Enter' && !e.shiftKey) {
e.preventDefault();
handleSend();
}
};
return (
<div style={{
height: '100vh',
display: 'flex',
flexDirection: 'column',
background: '#fafafa',
fontFamily: '-apple-system, BlinkMacSystemFont, "Segoe UI", Roboto, sans-serif'
}}>
<style>
{`
@keyframes bounce {
0%, 80%, 100% { transform: translateY(0); }
40% { transform: translateY(-6px); }
}
`}
</style>
{}
<div style={{
background: 'linear-gradient(135deg, #667eea 0%, #764ba2 100%)',
color: 'white',
padding: '16px 24px',
display: 'flex',
alignItems: 'center',
gap: '12px',
boxShadow: '0 2px 8px rgba(0,0,0,0.15)'
}}>
<svg width="32" height="32" viewBox="0 0 24 24" fill="none" stroke="currentColor" strokeWidth="2">
<path d="M21 15a2 2 0 0 1-2 2H7l-4 4V5a2 2 0 0 1 2-2h14a2 2 0 0 1 2 2z"/>
</svg>
<div>
<h1 style={{ margin: 0, fontSize: '18px', fontWeight: 600 }}>NextChat Demo</h1>
<p style={{ margin: 0, fontSize: '12px', opacity: 0.9 }}>ChatGPT-style interface</p>
</div>
</div>
{}
<div style={{
flex: 1,
overflowY: 'auto',
padding: '20px 0'
}}>
{messages.map(message => (
<ChatBubble key={message.id} message={message} />
))}
{isTyping && <TypingIndicator />}
<div ref={messagesEndRef} />
</div>
{}
<div style={{
padding: '16px 24px',
background: 'white',
borderTop: '1px solid #e0e0e0'
}}>
<div style={{
display: 'flex',
gap: '12px',
maxWidth: '800px',
margin: '0 auto'
}}>
<input
type="text"
value={input}
onChange={(e) => setInput(e.target.value)}
onKeyPress={handleKeyPress}
placeholder="Type your message..."
disabled={isTyping}
style={{
flex: 1,
padding: '14px 18px',
border: '2px solid #e0e0e0',
borderRadius: '24px',
fontSize: '15px',
outline: 'none',
transition: 'border-color 0.2s',
background: isTyping ? '#f5f5f5' : 'white'
}}
onFocus={(e) => e.target.style.borderColor = '#667eea'}
onBlur={(e) => e.target.style.borderColor = '#e0e0e0'}
/>
<button
onClick={handleSend}
disabled={!input.trim() || isTyping}
style={{
padding: '14px 24px',
background: input.trim() && !isTyping
? 'linear-gradient(135deg, #667eea 0%, #764ba2 100%)'
: '#ccc',
color: 'white',
border: 'none',
borderRadius: '24px',
fontSize: '15px',
fontWeight: 600,
cursor: input.trim() && !isTyping ? 'pointer' : 'not-allowed',
transition: 'transform 0.1s, opacity 0.2s',
display: 'flex',
alignItems: 'center',
gap: '8px'
}}
onMouseDown={(e) => {
if (input.trim() && !isTyping) {
e.currentTarget.style.transform = 'scale(0.95)';
}
}}
onMouseUp={(e) => e.currentTarget.style.transform = 'scale(1)'}
>
<svg width="18" height="18" viewBox="0 0 24 24" fill="none" stroke="currentColor" strokeWidth="2">
<line x1="22" y1="2" x2="11" y2="13"/>
<polygon points="22 2 15 22 11 13 2 9 22 2"/>
</svg>
Send
</button>
</div>
<p style={{
textAlign: 'center',
fontSize: '12px',
color: '#999',
marginTop: '12px',
marginBottom: 0
}}>
This is a demo UI inspired by NextChat - a ChatGPT web interface
</p>
</div>
</div>
);
}