File size: 6,713 Bytes
1f7ead8
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
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
import React, { useState, useEffect, useRef } from 'react';
import ReactMarkdown from 'react-markdown';
import remarkMath from 'remark-math';
import rehypeKatex from 'rehype-katex';
import { Send, Bot, User, Sparkles } from 'lucide-react';
import { querySystem } from '../api';
import { motion } from 'framer-motion';

const Notebook = ({ messages, setMessages, onCitationClick, notebookId }) => { // Accept notebookId
    //   const [messages, setMessages] = useState([ ... ]); // Removed internal state
    const [input, setInput] = useState('');
    const [loading, setLoading] = useState(false);
    const scrollRef = useRef(null);

    useEffect(() => {
        if (scrollRef.current) {
            scrollRef.current.scrollTop = scrollRef.current.scrollHeight;
        }
    }, [messages]);

    const handleSend = async () => {
        if (!input.trim()) return;

        const userMsg = { role: 'user', content: input };
        setMessages(prev => [...prev, userMsg]);
        setInput('');
        setLoading(true);

        try {
            // Pass notebookId to querySystem
            const response = await querySystem(userMsg.content, notebookId);
            const botMsg = {
                role: 'assistant',
                content: response.answer,
                citations: response.citations
            };
            setMessages(prev => [...prev, botMsg]);
        } catch (err) {
            setMessages(prev => [...prev, { role: 'assistant', content: 'Connection error. Please ensure background services are running.' }]);
        }
        setLoading(false);
    };



    return (
        <div className="flex flex-col h-full bg-white relative">

            {/* Scrollable Notebook Area */}

            <div className="flex-1 overflow-y-auto px-12 py-8" ref={scrollRef}>

                <div className="max-w-3xl mx-auto space-y-10 pb-32">

                    {messages.map((msg, idx) => (

                        <motion.div

                            initial={{ opacity: 0, y: 10 }}

                            animate={{ opacity: 1, y: 0 }}

                            key={idx}

                            className={`group ${msg.role === 'user' ? '' : ''}`}

                        >

                            {msg.role === 'user' ? (

                                <h3 className="text-lg font-semibold text-slate-900 mb-4">{msg.content}</h3>

                            ) : (

                                <div className="bg-white">

                                    <div className="prose prose-slate max-w-none prose-headings:font-bold prose-h1:text-2xl prose-h2:text-xl prose-p:text-slate-700 prose-li:text-slate-700">

                                        <ReactMarkdown

                                            remarkPlugins={[remarkMath]}

                                            rehypePlugins={[rehypeKatex]}

                                        >

                                            {msg.content}

                                        </ReactMarkdown>

                                    </div>



                                    {/* Citation Chips */}

                                    {msg.citations && msg.citations.length > 0 && (

                                        <div className="flex flex-wrap gap-2 mt-6 pt-4 border-t border-slate-100">

                                            {msg.citations.map((cit, i) => (

                                                <button

                                                    key={i}

                                                    onClick={() => onCitationClick(cit)} // Prop to open right panel

                                                    className="flex items-center gap-1.5 px-3 py-1.5 rounded-full bg-slate-50 border border-slate-200 text-xs font-medium text-slate-600 hover:bg-blue-50 hover:border-blue-200 hover:text-blue-600 transition-colors"

                                                >

                                                    <span className="w-4 h-4 rounded-full bg-slate-200 text-slate-600 flex items-center justify-center text-[10px] font-bold">{i + 1}</span>

                                                    {cit.paper_id.slice(0, 20)}...

                                                </button>

                                            ))}

                                        </div>

                                    )}

                                </div>

                            )}

                        </motion.div>

                    ))}



                    {loading && (

                        <motion.div

                            initial={{ opacity: 0 }}

                            animate={{ opacity: 1 }}

                            className="flex items-center gap-2 text-slate-400 italic"

                        >

                            <Sparkles size={16} className="animate-spin" />

                            <span>Analyzing documents...</span>

                        </motion.div>

                    )}

                </div>

            </div>



            {/* Input Area - Fixed at bottom center like a floating bar */}

            <div className="absolute bottom-6 left-0 right-0 px-4">

                <div className="max-w-3xl mx-auto">

                    <div className="bg-white p-2 rounded-2xl shadow-xl border border-slate-200 flex items-center gap-2">

                        <input

                            type="text"

                            className="flex-1 bg-transparent border-0 px-4 py-3 text-slate-700 placeholder-slate-400 focus:ring-0 text-base"

                            placeholder="Ask a question about your sources..."

                            value={input}

                            onChange={(e) => setInput(e.target.value)}

                            onKeyDown={(e) => e.key === 'Enter' && handleSend()}

                        />

                        <button

                            onClick={handleSend}

                            disabled={loading || !input.trim()}

                            className="p-3 rounded-xl bg-slate-900 text-white hover:bg-slate-700 disabled:opacity-50 transition-all"

                        >

                            <Send size={18} />

                        </button>

                    </div>

                    <p className="text-center text-xs text-slate-400 mt-3">

                        AI can make mistakes. Check important info.

                    </p>

                </div>

            </div>

        </div>
    );
};

export default Notebook;