| import { forwardRef, useState } from 'react' |
| import MessageBubble from './MessageBubble' |
|
|
| const SUGGESTIONS = [ |
| { emoji: 'π', title: 'Latest AI news', text: 'What are the latest developments in AI this week?' }, |
| { emoji: 'π‘', title: 'Explain quantum computing', text: 'Explain quantum computing in simple terms' }, |
| { emoji: 'π', title: 'Write a cover letter', text: 'Write me a professional cover letter for a software engineer role' }, |
| { emoji: 'π', title: 'Debug my code', text: 'Help me debug and optimize my Python code' }, |
| { emoji: 'π', title: 'Data analysis help', text: 'Help me analyze data and create visualizations' }, |
| { emoji: 'π¨', title: 'Creative writing', text: 'Write a short sci-fi story set in the year 2150' }, |
| ] |
|
|
| const EmptyState = ({ onSuggestion }) => ( |
| <div className="empty-state"> |
| <div className="empty-logo">β¦</div> |
| <div className="empty-title">What can I help with?</div> |
| <div className="empty-sub"> |
| Ask me anything β code, research, writing, math, or just a conversation. |
| I'm powered by the world's best AI models. |
| </div> |
| <div className="suggestion-grid"> |
| {SUGGESTIONS.map((s, i) => ( |
| <button key={s.title} className="suggestion-card" style={{ animation: `fadeUp 0.4s var(--ease) ${0.1 + i*0.05}s both` }} onClick={() => onSuggestion(s.text)}> |
| <strong>{s.emoji} {s.title}</strong> |
| {s.text} |
| </button> |
| ))} |
| </div> |
| </div> |
| ) |
|
|
| const TypingIndicator = () => ( |
| <div className="msg-row" style={{ animationDelay: '0ms' }}> |
| <div className="msg-indicator ai">β¦</div> |
| <div className="msg-card" style={{ padding: '14px 20px' }}> |
| <div className="typing-wave"> |
| <div className="typing-dot" /> |
| <div className="typing-dot" /> |
| <div className="typing-dot" /> |
| </div> |
| </div> |
| </div> |
| ) |
|
|
| const ChatArea = forwardRef(function ChatArea({ messages, loading, onSuggestion, user }, ref) { |
| const [showFab, setShowFab] = useState(false) |
|
|
| const handleScroll = (e) => { |
| const { scrollTop, scrollHeight, clientHeight } = e.target |
| if (scrollHeight - scrollTop - clientHeight > 200) { |
| setShowFab(true) |
| } else { |
| setShowFab(false) |
| } |
| } |
|
|
| const scrollToBottom = () => { |
| if (ref.current) { |
| ref.current.scrollTo({ top: ref.current.scrollHeight, behavior: 'smooth' }) |
| } |
| } |
|
|
| return ( |
| <div className="chat-area" ref={ref} onScroll={handleScroll}> |
| <div className="chat-inner"> |
| {messages.length === 0 && !loading ? ( |
| <EmptyState onSuggestion={onSuggestion} /> |
| ) : ( |
| <> |
| {messages.map((m, i) => ( |
| <MessageBubble key={m._id || i} message={m} index={i} user={user} /> |
| ))} |
| {loading && (messages.length === 0 || messages[messages.length - 1].role !== 'assistant') && <TypingIndicator />} |
| </> |
| )} |
| </div> |
| <button |
| className={`scroll-fab ${showFab ? 'visible' : ''}`} |
| onClick={scrollToBottom} |
| title="Scroll to bottom" |
| > |
| <svg width="20" height="20" viewBox="0 0 24 24" fill="none" stroke="currentColor" strokeWidth="2" strokeLinecap="round" strokeLinejoin="round"> |
| <line x1="12" y1="5" x2="12" y2="19"/><polyline points="19 12 12 19 5 12"/> |
| </svg> |
| </button> |
| </div> |
| ) |
| }) |
|
|
| export default ChatArea |
|
|