Alleinzellgaenger commited on
Commit
8066d07
·
1 Parent(s): 4a6c290

Remove chunk texts and add button to understand and skip in the upper right corner to save vertical space.

Browse files
frontend/src/components/ChunkPanel.jsx CHANGED
@@ -9,41 +9,31 @@ import React, { useState, useEffect } from 'react';
9
  const ChunkPanel = ({
10
  documentData,
11
  currentChunkIndex,
12
- chunkExpanded,
13
- setChunkExpanded,
14
- chunkStates,
15
- skipChunk,
16
- markChunkUnderstood,
17
- startInteractiveLesson,
18
  showChat,
19
- setShowChat,
20
- setChunkAsInteractive,
21
  updateChunkChatHistory,
22
- getCurrentChunkChatHistory
 
 
 
23
  }) => {
24
 
25
  const chatMarkdownComponents = getChatMarkdownComponents();
26
  const [isLoading, setIsLoading] = useState(false);
27
- const currentChunk = documentData?.chunks?.[currentChunkIndex] ?? null;
28
- const chunkText = currentChunk?.text || '';
29
-
30
 
31
-
32
- // Generate greeting when chat opens and no messages
33
  useEffect(() => {
34
- if (showChat && (getCurrentChunkChatHistory()?.length ?? 0) === 0) {
 
 
35
  generateGreeting();
36
  }
37
- }, [showChat]);
38
- const handleChatToggle = () => {
39
- if (!showChat) {
40
- setChunkAsInteractive();
41
- }
42
- setShowChat(!showChat);
43
- };
44
 
45
  const generateGreeting = async () => {
46
  setIsLoading(true);
 
 
 
47
  try {
48
  const response = await fetch('/api/chat', {
49
  method: 'POST',
@@ -73,6 +63,9 @@ const ChunkPanel = ({
73
  ]);
74
  } finally {
75
  setIsLoading(false);
 
 
 
76
  }
77
  };
78
 
@@ -112,12 +105,7 @@ const ChunkPanel = ({
112
  return (
113
  <>
114
  {/* Chunk Header */}
115
- <div className="px-6 py-4 flex-shrink-0 bg-white rounded-t-lg border-b border-gray-200 z-10">
116
- <div className="flex items-center justify-between">
117
- <button
118
- onClick={() => setChunkExpanded(!chunkExpanded)}
119
- className="flex items-center hover:bg-gray-50 py-2 px-3 rounded-lg transition-all -ml-3"
120
- >
121
  <div className="font-semibold text-gray-900 text-left flex-1">
122
  <ReactMarkdown
123
  remarkPlugins={[remarkMath]}
@@ -130,114 +118,61 @@ const ChunkPanel = ({
130
  {documentData?.chunks?.[currentChunkIndex]?.topic || "Loading..."}
131
  </ReactMarkdown>
132
  </div>
133
- <span className="text-gray-400 ml-3">
134
- {chunkExpanded ? '▲' : '▼'}
135
- </span>
136
- </button>
137
-
138
- </div>
139
-
140
- {/* Expandable Chunk Content */}
141
- {chunkExpanded && documentData?.chunks?.[currentChunkIndex] && (
142
- <>
143
- <div className="prose prose-sm max-w-none">
144
- <ReactMarkdown
145
- remarkPlugins={[remarkMath]}
146
- rehypePlugins={[rehypeRaw, rehypeKatex]}
147
- components={chatMarkdownComponents}
148
- >
149
- {documentData.chunks[currentChunkIndex].text}
150
- </ReactMarkdown>
151
- </div>
152
 
153
- {/* Action Buttons */}
154
- <div className="flex items-center justify-center gap-4 mt-4 pt-4 border-gray-200">
155
- <button
156
- onClick={skipChunk}
157
- className={`py-2 px-4 border rounded-lg transition-all text-sm ${
158
- chunkStates[currentChunkIndex] === 'skipped'
159
- ? 'bg-red-500 text-white border-red-500 hover:bg-red-600'
160
- : 'bg-white hover:bg-gray-50 border-gray-300'
161
- }`}
162
- >
163
- Skip
164
- </button>
165
  <button
166
- onClick={handleChatToggle}
167
- className={`py-2 px-4 border rounded-lg transition-all text-sm flex items-center gap-1 ${
168
- chunkStates[currentChunkIndex] === 'interactive'
169
- ? 'bg-blue-500 text-white border-blue-500 hover:bg-blue-600'
170
- : 'bg-white hover:bg-gray-50 border-gray-300'
171
- }`}
172
  >
173
- <svg width="16" height="16" viewBox="0 0 24 24" fill="none" stroke="currentColor" strokeWidth="1.5">
174
- <path d="M21 15a2 2 0 0 1-2 2H7l-4 4V5a2 2 0 0 1 2-2h14a2 2 0 0 1 2 2z"/>
 
 
 
 
 
 
 
 
 
 
 
 
 
175
  </svg>
176
- Chat
177
  </button>
178
- <button
 
 
179
  onClick={markChunkUnderstood}
180
- className={`py-2 px-4 border rounded-lg transition-all text-sm ${
181
- chunkStates[currentChunkIndex] === 'understood'
182
- ? 'bg-green-500 text-white border-green-500 hover:bg-green-600'
183
- : 'bg-white hover:bg-gray-50 border-gray-300'
184
- }`}
185
  >
186
- Understood
 
 
 
 
 
 
 
 
 
 
187
  </button>
188
  </div>
189
- </>
190
- )}
191
-
192
- {/* Show buttons even when chunk is collapsed */}
193
- {!chunkExpanded && (
194
- <div className="flex items-center justify-center gap-4 mt-4 pt-4 border-t border-gray-200">
195
- <button
196
- onClick={skipChunk}
197
- className={`py-2 px-4 border rounded-lg transition-all text-sm ${
198
- chunkStates[currentChunkIndex] === 'skipped'
199
- ? 'bg-red-500 text-white border-red-500 hover:bg-red-600'
200
- : 'bg-white hover:bg-gray-50 border-gray-300'
201
- }`}
202
- >
203
- Skip
204
- </button>
205
- <button
206
- onClick={handleChatToggle}
207
- className={`py-2 px-4 border rounded-lg transition-all text-sm flex items-center gap-1 ${
208
- chunkStates[currentChunkIndex] === 'interactive'
209
- ? 'bg-blue-500 text-white border-blue-500 hover:bg-blue-600'
210
- : 'bg-white hover:bg-gray-50 border-gray-300'
211
- }`}
212
- >
213
- <svg width="16" height="16" viewBox="0 0 24 24" fill="none" stroke="currentColor" strokeWidth="1.5">
214
- <path d="M21 15a2 2 0 0 1-2 2H7l-4 4V5a2 2 0 0 1 2-2h14a2 2 0 0 1 2 2z"/>
215
- </svg>
216
- Chat
217
- </button>
218
- <button
219
- onClick={markChunkUnderstood}
220
- className={`py-2 px-4 border rounded-lg transition-all text-sm ${
221
- chunkStates[currentChunkIndex] === 'understood'
222
- ? 'bg-green-500 text-white border-green-500 hover:bg-green-600'
223
- : 'bg-white hover:bg-gray-50 border-gray-300'
224
- }`}
225
- >
226
- Understood
227
- </button>
228
- </div>
229
- )}
230
  </div>
231
 
232
  {/* Chat Interface - Only shown when showChat is true */}
233
  {showChat && (
234
- <div className="flex-1 flex flex-col min-h-0 bg-white rounded-lg m-2 shadow-lg">
235
  <SimpleChat
236
  messages={getCurrentChunkChatHistory()}
237
  onSend={handleSend}
238
  isLoading={isLoading}
239
  />
240
- </div>
241
  )}
242
  </>
243
  );
 
9
  const ChunkPanel = ({
10
  documentData,
11
  currentChunkIndex,
 
 
 
 
 
 
12
  showChat,
 
 
13
  updateChunkChatHistory,
14
+ getCurrentChunkChatHistory,
15
+ setWaitingForFirstResponse,
16
+ markChunkUnderstood,
17
+ skipChunk
18
  }) => {
19
 
20
  const chatMarkdownComponents = getChatMarkdownComponents();
21
  const [isLoading, setIsLoading] = useState(false);
 
 
 
22
 
23
+ // Generate greeting only when navigating to a chunk that has no chat history
 
24
  useEffect(() => {
25
+ const hasNoMessages = (getCurrentChunkChatHistory()?.length ?? 0) === 0;
26
+
27
+ if (documentData && showChat && hasNoMessages) {
28
  generateGreeting();
29
  }
30
+ }, [currentChunkIndex]); // Only trigger on chunk navigation
 
 
 
 
 
 
31
 
32
  const generateGreeting = async () => {
33
  setIsLoading(true);
34
+ if (setWaitingForFirstResponse) {
35
+ setWaitingForFirstResponse(true);
36
+ }
37
  try {
38
  const response = await fetch('/api/chat', {
39
  method: 'POST',
 
63
  ]);
64
  } finally {
65
  setIsLoading(false);
66
+ if (setWaitingForFirstResponse) {
67
+ setWaitingForFirstResponse(false);
68
+ }
69
  }
70
  };
71
 
 
105
  return (
106
  <>
107
  {/* Chunk Header */}
108
+ <div className="px-6 py-4 flex-shrink-0 bg-white rounded-t-lg border-b border-gray-200 z-10 flex items-center justify-between">
 
 
 
 
 
109
  <div className="font-semibold text-gray-900 text-left flex-1">
110
  <ReactMarkdown
111
  remarkPlugins={[remarkMath]}
 
118
  {documentData?.chunks?.[currentChunkIndex]?.topic || "Loading..."}
119
  </ReactMarkdown>
120
  </div>
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
121
 
122
+ <div className="flex items-center gap-2">
123
+ {/* Skip Button */}
 
 
 
 
 
 
 
 
 
 
124
  <button
125
+ onClick={skipChunk}
126
+ className="p-2 rounded-full bg-red-100 hover:bg-red-200 text-red-600 transition-colors duration-200"
127
+ title="Skip this chunk"
 
 
 
128
  >
129
+ <svg
130
+ className="w-5 h-5"
131
+ fill="currentColor"
132
+ viewBox="0 0 20 20"
133
+ >
134
+ <path
135
+ fillRule="evenodd"
136
+ d="M7.293 14.707a1 1 0 010-1.414L10.586 10 7.293 6.707a1 1 0 011.414-1.414l4 4a1 1 0 010 1.414l-4 4a1 1 0 01-1.414 0z"
137
+ clipRule="evenodd"
138
+ />
139
+ <path
140
+ fillRule="evenodd"
141
+ d="M12.293 14.707a1 1 0 010-1.414L15.586 10l-3.293-3.293a1 1 0 011.414-1.414l4 4a1 1 0 010 1.414l-4 4a1 1 0 01-1.414 0z"
142
+ clipRule="evenodd"
143
+ />
144
  </svg>
 
145
  </button>
146
+
147
+ {/* Understood Button */}
148
+ <button
149
  onClick={markChunkUnderstood}
150
+ className="p-2 rounded-full bg-green-100 hover:bg-green-200 text-green-600 transition-colors duration-200"
151
+ title="Mark chunk as understood"
 
 
 
152
  >
153
+ <svg
154
+ className="w-5 h-5"
155
+ fill="currentColor"
156
+ viewBox="0 0 20 20"
157
+ >
158
+ <path
159
+ fillRule="evenodd"
160
+ d="M16.707 5.293a1 1 0 010 1.414l-8 8a1 1 0 01-1.414 0l-4-4a1 1 0 011.414-1.414L8 12.586l7.293-7.293a1 1 0 011.414 0z"
161
+ clipRule="evenodd"
162
+ />
163
+ </svg>
164
  </button>
165
  </div>
166
+
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
167
  </div>
168
 
169
  {/* Chat Interface - Only shown when showChat is true */}
170
  {showChat && (
 
171
  <SimpleChat
172
  messages={getCurrentChunkChatHistory()}
173
  onSend={handleSend}
174
  isLoading={isLoading}
175
  />
 
176
  )}
177
  </>
178
  );
frontend/src/components/DocumentProcessor.jsx CHANGED
@@ -15,6 +15,8 @@ import ChunkPanel from './ChunkPanel';
15
  function DocumentProcessor() {
16
  // State for PDF navigation
17
  const [pdfNavigation, setPdfNavigation] = useState(null);
 
 
18
 
19
  // Custom hooks
20
  const {
@@ -88,7 +90,7 @@ function DocumentProcessor() {
88
  );
89
  }
90
 
91
- if (processing) {
92
  return <LoadingAnimation uploadProgress={uploadProgress} />;
93
  }
94
 
@@ -163,17 +165,12 @@ function DocumentProcessor() {
163
  <ChunkPanel
164
  documentData={documentData}
165
  currentChunkIndex={currentChunkIndex}
166
- chunkExpanded={chunkExpanded}
167
- setChunkExpanded={setChunkExpanded}
168
- chunkStates={chunkStates}
169
- skipChunk={skipChunk}
170
- markChunkUnderstood={markChunkUnderstood}
171
- startInteractiveLesson={handleStartInteractiveLesson}
172
  showChat={showChat}
173
- setShowChat={setShowChat}
174
- setChunkAsInteractive={setChunkAsInteractive}
175
  updateChunkChatHistory={updateChunkChatHistory}
176
  getCurrentChunkChatHistory={getCurrentChunkChatHistory}
 
 
 
177
  />
178
  </div>
179
  </div>
 
15
  function DocumentProcessor() {
16
  // State for PDF navigation
17
  const [pdfNavigation, setPdfNavigation] = useState(null);
18
+ // State for first LLM response loading
19
+ const [waitingForFirstResponse, setWaitingForFirstResponse] = useState(false);
20
 
21
  // Custom hooks
22
  const {
 
90
  );
91
  }
92
 
93
+ if (processing || waitingForFirstResponse) {
94
  return <LoadingAnimation uploadProgress={uploadProgress} />;
95
  }
96
 
 
165
  <ChunkPanel
166
  documentData={documentData}
167
  currentChunkIndex={currentChunkIndex}
 
 
 
 
 
 
168
  showChat={showChat}
 
 
169
  updateChunkChatHistory={updateChunkChatHistory}
170
  getCurrentChunkChatHistory={getCurrentChunkChatHistory}
171
+ setWaitingForFirstResponse={setWaitingForFirstResponse}
172
+ markChunkUnderstood={markChunkUnderstood}
173
+ skipChunk={skipChunk}
174
  />
175
  </div>
176
  </div>
frontend/src/hooks/useChunkNavigation.js CHANGED
@@ -5,7 +5,7 @@ export const useChunkNavigation = (documentData, clearTypingAnimation) => {
5
  const [currentChunkIndex, setCurrentChunkIndex] = useState(0);
6
  const [chunkExpanded, setChunkExpanded] = useState(true);
7
  const [chunkChatHistories, setChunkChatHistories] = useState({});
8
- const [showChat, setShowChat] = useState(false);
9
 
10
  const goToNextChunk = () => {
11
  if (documentData && currentChunkIndex < documentData.chunks.length - 1) {
@@ -14,7 +14,6 @@ export const useChunkNavigation = (documentData, clearTypingAnimation) => {
14
  }
15
  setCurrentChunkIndex(currentChunkIndex + 1);
16
  setChunkExpanded(true);
17
- setShowChat(false);
18
  }
19
  };
20
 
@@ -25,7 +24,6 @@ export const useChunkNavigation = (documentData, clearTypingAnimation) => {
25
  }
26
  setCurrentChunkIndex(currentChunkIndex - 1);
27
  setChunkExpanded(true);
28
- setShowChat(false);
29
  }
30
  };
31
 
@@ -42,7 +40,6 @@ export const useChunkNavigation = (documentData, clearTypingAnimation) => {
42
  }
43
  setCurrentChunkIndex(currentChunkIndex + 1);
44
  setChunkExpanded(true);
45
- setShowChat(false);
46
  }, 100); // Small delay to allow state update to complete
47
  }
48
 
@@ -66,7 +63,6 @@ export const useChunkNavigation = (documentData, clearTypingAnimation) => {
66
  }
67
  setCurrentChunkIndex(currentChunkIndex + 1);
68
  setChunkExpanded(true);
69
- setShowChat(false);
70
  }, 100); // Small delay to allow state update to complete
71
  }
72
 
 
5
  const [currentChunkIndex, setCurrentChunkIndex] = useState(0);
6
  const [chunkExpanded, setChunkExpanded] = useState(true);
7
  const [chunkChatHistories, setChunkChatHistories] = useState({});
8
+ const [showChat, setShowChat] = useState(true);
9
 
10
  const goToNextChunk = () => {
11
  if (documentData && currentChunkIndex < documentData.chunks.length - 1) {
 
14
  }
15
  setCurrentChunkIndex(currentChunkIndex + 1);
16
  setChunkExpanded(true);
 
17
  }
18
  };
19
 
 
24
  }
25
  setCurrentChunkIndex(currentChunkIndex - 1);
26
  setChunkExpanded(true);
 
27
  }
28
  };
29
 
 
40
  }
41
  setCurrentChunkIndex(currentChunkIndex + 1);
42
  setChunkExpanded(true);
 
43
  }, 100); // Small delay to allow state update to complete
44
  }
45
 
 
63
  }
64
  setCurrentChunkIndex(currentChunkIndex + 1);
65
  setChunkExpanded(true);
 
66
  }, 100); // Small delay to allow state update to complete
67
  }
68
 
frontend/src/main.jsx CHANGED
@@ -4,7 +4,5 @@ import './index.css'
4
  import App from './App.jsx'
5
 
6
  createRoot(document.getElementById('root')).render(
7
- <StrictMode>
8
  <App />
9
- </StrictMode>,
10
  )
 
4
  import App from './App.jsx'
5
 
6
  createRoot(document.getElementById('root')).render(
 
7
  <App />
 
8
  )