Spaces:
Running
Running
File size: 3,763 Bytes
c3a0082 b10d53b c3a0082 aaf2031 c3a0082 aaf2031 c3a0082 aaf2031 c3a0082 b10d53b c3a0082 | 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 | // components/ChatApp.jsx
import React, { useState, useRef, useEffect } from 'react';
import MessageBubble from './MessageBubble';
import { Button , Typography, Box} from '@mui/material';
import { ChatBubbleOutline } from '@mui/icons-material';
import PipelineLoader from './PipelineLoader';
export default function ChatApp() {
const [message, setMessage] = useState('');
const [history, setHistory] = useState([]);
const [loading, setLoading] = useState(false);
const chatEndRef = useRef(null);
const examples = [
"What is Python?",
"Write a JavaScript function to reverse a string.",
"Explain how transformers work.",
];
const scrollToBottom = () => {
chatEndRef.current?.scrollIntoView({ behavior: 'smooth' });
};
useEffect(scrollToBottom, [history]);
const token = process.env.REACT_APP_HF_TOKEN;
const sendMessage = async () => {
if (!message.trim()) return;
const time = new Date().toLocaleTimeString([], { hour: '2-digit', minute: '2-digit' });
const newHistory = [...history, { role: 'user', content: message, time }];
setHistory(newHistory);
setMessage('');
setLoading(true);
const response = await fetch('https://fredericksundeep-chatmateapi.hf.space/chat-stream', {
method: 'POST',
headers: { Authorization: `Bearer ${token}`, 'Content-Type': 'application/json' },
body: JSON.stringify({ message, history: newHistory }),
});
const reader = response.body.getReader();
const decoder = new TextDecoder();
let done = false;
let content = '';
while (!done) {
const { value, done: isDone } = await reader.read();
if (value) {
const chunk = decoder.decode(value);
content += chunk;
const currentContent = content; // capture safe reference
setHistory(h =>
h.map((msg, i) =>
i === newHistory.length
? { ...msg, content: currentContent }
: msg
)
);
}
done = isDone;
}
setHistory(h => [...h, { role: 'assistant', content, time }]);
setLoading(false);
};
return (
<div className="chat-container">
<div className="header-area">
<Box display="flex" alignItems="center" gap={1} mb={2}>
<ChatBubbleOutline color="primary" />
<Typography variant="h5" component="h2">
Chat Mate
</Typography>
</Box>
</div>
<div className="chat-box">
{history.length === 0 && !loading && (
<Box
display="flex"
flexDirection="column"
alignItems="center"
gap={2}
pt={8}
>
{examples.map((example, i) => (
<Button
key={i}
variant="outlined"
onClick={() => {
const selected = example;
setMessage(selected);
setTimeout(() => {
sendMessage(selected);
}, 50);
}}
sx={{
color: '#fff',
borderColor: '#444',
backgroundColor: '#1e1e2f',
borderRadius: '12px',
width: '70%',
fontSize: '1rem',
fontWeight: 400,
textTransform: 'none',
'&:hover': {
backgroundColor: '#2c2c3e',
borderColor: '#666',
},
}}
>
{example}
</Button>
))}
</Box>
)}
{history.map((msg, i) => (
<MessageBubble key={i} {...msg} />
))}
{loading && <PipelineLoader />}
<div ref={chatEndRef} />
</div>
<div className="input-area">
<textarea
value={message}
rows={2}
onChange={(e) => setMessage(e.target.value)}
placeholder="Ask something..."
/>
<Button variant="contained"
color="primary"
sx={{ ml: 1, px: 2, py: 1 }} disabled={!message.trim() || loading} onClick={sendMessage}>Send</Button>
</div>
</div>
);
}
|