Spaces:
Sleeping
Sleeping
File size: 4,031 Bytes
f444dc0 f1b7f9a 98d2bd7 f444dc0 98d2bd7 f444dc0 98d2bd7 8d0b379 98d2bd7 f444dc0 8d0b379 98d2bd7 f444dc0 98d2bd7 f444dc0 98d2bd7 f444dc0 98d2bd7 f444dc0 98d2bd7 f444dc0 8d0b379 98d2bd7 8d0b379 98d2bd7 8d0b379 98d2bd7 f444dc0 98d2bd7 f444dc0 98d2bd7 8d0b379 |
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 |
import { useState, useEffect, useRef } from 'react';
import ReactMarkdown from 'react-markdown';
import remarkMath from 'remark-math';
import rehypeKatex from 'rehype-katex';
import rehypeRaw from 'rehype-raw';
import { getChatMarkdownComponents } from '../utils/markdownComponents.jsx';
const SimpleChat = ({ messages, currentChunkIndex, canEdit, onSend, isLoading }) => {
const [input, setInput] = useState('');
const messagesEndRef = useRef(null);
const currentChunkStartRef = useRef(null);
const handleSubmit = (e) => {
e.preventDefault();
if (!input.trim() || isLoading || !canEdit) return;
onSend(input.trim());
setInput('');
};
// Scroll to current chunk's first message when chunk changes
useEffect(() => {
if (currentChunkStartRef.current) {
currentChunkStartRef.current.scrollIntoView({ behavior: 'smooth', block: 'start' });
}
}, [currentChunkIndex]);
// Find the first message of the current chunk
const currentChunkFirstMessageIndex = messages.findIndex(msg => msg.chunkIndex === currentChunkIndex);
return (
<div className="flex flex-col h-full">
{/* Messages */}
<div className="flex-1 overflow-y-auto p-4 space-y-3">
{messages.map((message, idx) => {
const isCurrentChunk = message.chunkIndex === currentChunkIndex;
const isFirstOfCurrentChunk = idx === currentChunkFirstMessageIndex;
return (
<div
key={idx}
ref={isFirstOfCurrentChunk ? currentChunkStartRef : null}
className={`flex ${message.role === 'user' ? 'justify-end' : 'justify-start'}`}
>
<div
className={`max-w-[90%] p-3 rounded-lg transition-opacity ${
message.role === 'user'
? `bg-gray-100 text-white ${isCurrentChunk ? 'opacity-100' : 'opacity-40'}`
: `bg-white text-gray-900 ${isCurrentChunk ? 'opacity-100' : 'opacity-40'}`
}`}
>
<ReactMarkdown
remarkPlugins={[remarkMath]}
rehypePlugins={[rehypeRaw, rehypeKatex]}
components={getChatMarkdownComponents()}
>
{message.content}
</ReactMarkdown>
</div>
</div>
);
})}
{isLoading && (
<div className="flex justify-start">
<div className="bg-gray-100 p-3 rounded-lg">
<div className="flex space-x-1">
<div className="w-2 h-2 bg-gray-400 rounded-full animate-bounce"></div>
<div
className="w-2 h-2 bg-gray-400 rounded-full animate-bounce"
style={{ animationDelay: '0.1s' }}
></div>
<div
className="w-2 h-2 bg-gray-400 rounded-full animate-bounce"
style={{ animationDelay: '0.2s' }}
></div>
</div>
</div>
</div>
)}
</div>
{/* Input */}
<form onSubmit={handleSubmit} className="p-4 border-t">
<div className="flex space-x-2">
<input
type="text"
value={input}
onChange={(e) => setInput(e.target.value)}
placeholder={canEdit ? "Type your message..." : "This chunk is completed - navigation only"}
disabled={isLoading || !canEdit}
className="flex-1 px-3 py-2 border rounded-lg focus:outline-none focus:ring-2 focus:ring-blue-500 disabled:bg-gray-100 disabled:text-gray-500"
/>
<button
type="submit"
disabled={!input.trim() || isLoading || !canEdit}
className="px-4 py-2 bg-blue-500 text-white rounded-lg hover:bg-blue-600 disabled:bg-gray-300 disabled:cursor-not-allowed"
>
{isLoading ? '...' : 'Send'}
</button>
</div>
</form>
</div>
);
};
export default SimpleChat;
|