Spaces:
Sleeping
Sleeping
File size: 7,047 Bytes
4000a4c |
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 |
'use client';
import { useState } from 'react';
import { X, Send, Loader2, MessageCircle } from 'lucide-react';
import clsx from 'clsx';
import ReactMarkdown from 'react-markdown';
import remarkGfm from 'remark-gfm';
interface AskQuestionModalProps {
isOpen: boolean;
onClose: () => void;
context: string; // The original analyzed content
}
interface Message {
role: 'user' | 'assistant';
content: string;
}
export default function AskQuestionModal({ isOpen, onClose, context }: AskQuestionModalProps) {
const [messages, setMessages] = useState<Message[]>([]);
const [input, setInput] = useState('');
const [loading, setLoading] = useState(false);
const handleSubmit = async (e: React.FormEvent) => {
e.preventDefault();
if (!input.trim() || loading) return;
const userMessage = input.trim();
setInput('');
setMessages(prev => [...prev, { role: 'user', content: userMessage }]);
setLoading(true);
try {
const formData = new FormData();
formData.append('text', `
Based on this content I just learned:
---
${context.substring(0, 2000)}
---
My question: ${userMessage}
Please answer my question in a helpful, educational way. Use simple language and examples if needed.
`);
const res = await fetch('/api/analyze', {
method: 'POST',
body: formData,
});
if (!res.ok) throw new Error('Failed to get response');
const data = await res.json();
const cleanResponse = data.result?.replace(/\[\[TOPICS?:.*?\]\]/gi, '').trim() || 'Sorry, I could not generate a response.';
setMessages(prev => [...prev, { role: 'assistant', content: cleanResponse }]);
} catch (error) {
setMessages(prev => [...prev, { role: 'assistant', content: 'Sorry, something went wrong. Please try again.' }]);
} finally {
setLoading(false);
}
};
if (!isOpen) return null;
return (
<div className="fixed inset-0 z-50 flex items-center justify-center p-4 bg-black/50 backdrop-blur-sm animate-fade-in-up">
<div className="w-full max-w-2xl bg-white dark:bg-neutral-900 rounded-2xl shadow-2xl border border-gray-200 dark:border-neutral-800 flex flex-col max-h-[80vh]">
{/* Header */}
<div className="flex items-center justify-between p-4 border-b border-gray-200 dark:border-neutral-800">
<div className="flex items-center gap-2">
<MessageCircle size={20} className="text-sky-500" />
<h2 className="text-lg font-semibold text-gray-900 dark:text-white">Ask a Question</h2>
</div>
<button
onClick={onClose}
className="p-2 hover:bg-gray-100 dark:hover:bg-neutral-800 rounded-full transition-colors"
>
<X size={20} className="text-gray-500" />
</button>
</div>
{/* Messages */}
<div className="flex-1 overflow-y-auto p-4 space-y-4">
{messages.length === 0 && (
<div className="text-center py-12 text-gray-400">
<MessageCircle size={48} className="mx-auto mb-4 opacity-50" />
<p>Ask any question about the content you just analyzed.</p>
</div>
)}
{messages.map((msg, idx) => (
<div
key={idx}
className={clsx(
"flex",
msg.role === 'user' ? "justify-end" : "justify-start"
)}
>
<div
className={clsx(
"max-w-[80%] rounded-2xl px-4 py-3",
msg.role === 'user'
? "bg-sky-500 text-white rounded-br-md"
: "bg-gray-100 dark:bg-neutral-800 text-gray-800 dark:text-gray-200 rounded-bl-md"
)}
>
{msg.role === 'assistant' ? (
<div className="prose prose-sm dark:prose-invert max-w-none">
<ReactMarkdown remarkPlugins={[remarkGfm]}>
{msg.content}
</ReactMarkdown>
</div>
) : (
<p>{msg.content}</p>
)}
</div>
</div>
))}
{loading && (
<div className="flex justify-start">
<div className="bg-gray-100 dark:bg-neutral-800 rounded-2xl rounded-bl-md px-4 py-3">
<Loader2 size={20} className="animate-spin text-gray-400" />
</div>
</div>
)}
</div>
{/* Input */}
<form onSubmit={handleSubmit} className="p-4 border-t border-gray-200 dark:border-neutral-800">
<div className="flex gap-3">
<input
type="text"
value={input}
onChange={(e) => setInput(e.target.value)}
placeholder="Type your question..."
className="flex-1 px-4 py-3 bg-gray-100 dark:bg-neutral-800 border border-gray-200 dark:border-neutral-700 rounded-xl focus:outline-none focus:ring-2 focus:ring-sky-500 focus:border-transparent text-gray-900 dark:text-white placeholder-gray-400"
disabled={loading}
/>
<button
type="submit"
disabled={loading || !input.trim()}
className={clsx(
"px-4 py-3 rounded-xl font-medium transition-all flex items-center gap-2",
loading || !input.trim()
? "bg-gray-200 dark:bg-neutral-700 text-gray-400 cursor-not-allowed"
: "bg-sky-500 text-white hover:bg-sky-600"
)}
>
{loading ? <Loader2 size={18} className="animate-spin" /> : <Send size={18} />}
</button>
</div>
</form>
</div>
</div>
);
}
|