YashB1 commited on
Commit
f94b3d9
Β·
verified Β·
1 Parent(s): 239ffee

Update index.html

Browse files
Files changed (1) hide show
  1. index.html +1464 -19
index.html CHANGED
@@ -1,19 +1,1464 @@
1
- <!doctype html>
2
- <html>
3
- <head>
4
- <meta charset="utf-8" />
5
- <meta name="viewport" content="width=device-width" />
6
- <title>My static Space</title>
7
- <link rel="stylesheet" href="style.css" />
8
- </head>
9
- <body>
10
- <div class="card">
11
- <h1>Welcome to your static Space!</h1>
12
- <p>You can modify this app directly by editing <i>index.html</i> in the Files and versions tab.</p>
13
- <p>
14
- Also don't forget to check the
15
- <a href="https://huggingface.co/docs/hub/spaces" target="_blank">Spaces documentation</a>.
16
- </p>
17
- </div>
18
- </body>
19
- </html>
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ <!DOCTYPE html>
2
+ <html lang="en">
3
+ <head>
4
+ <meta charset="UTF-8">
5
+ <meta name="viewport" content="width=device-width, initial-scale=1.0">
6
+ <title>Research Paper Chatbot</title>
7
+ <script src="https://cdnjs.cloudflare.com/ajax/libs/three.js/r128/three.min.js"></script>
8
+ <!-- βœ… Add Markdown parsing library -->
9
+ <script src="https://cdn.jsdelivr.net/npm/marked/marked.min.js"></script>
10
+ <style>
11
+ * {
12
+ margin: 0;
13
+ padding: 0;
14
+ box-sizing: border-box;
15
+ }
16
+
17
+ body {
18
+ font-family: 'Segoe UI', Tahoma, Geneva, Verdana, sans-serif;
19
+ background: linear-gradient(135deg, #667eea 0%, #764ba2 100%);
20
+ min-height: 100vh;
21
+ overflow-x: hidden;
22
+ }
23
+
24
+ .bg-animation {
25
+ position: fixed;
26
+ top: 0;
27
+ left: 0;
28
+ width: 100%;
29
+ height: 100%;
30
+ z-index: -1;
31
+ opacity: 0.3;
32
+ }
33
+
34
+ /* βœ… Login Modal Styles */
35
+ .login-modal {
36
+ position: fixed;
37
+ top: 0;
38
+ left: 0;
39
+ width: 100%;
40
+ height: 100%;
41
+ background: rgba(0, 0, 0, 0.8);
42
+ display: flex;
43
+ justify-content: center;
44
+ align-items: center;
45
+ z-index: 1001;
46
+ }
47
+
48
+ .login-form {
49
+ background: white;
50
+ padding: 40px;
51
+ border-radius: 20px;
52
+ box-shadow: 0 20px 60px rgba(0, 0, 0, 0.3);
53
+ max-width: 500px;
54
+ width: 90%;
55
+ text-align: center;
56
+ }
57
+
58
+ .login-form h2 {
59
+ color: #333;
60
+ margin-bottom: 20px;
61
+ font-size: 1.8rem;
62
+ }
63
+
64
+ .login-form p {
65
+ color: #666;
66
+ margin-bottom: 30px;
67
+ line-height: 1.5;
68
+ }
69
+
70
+ .login-input-group {
71
+ position: relative;
72
+ margin-bottom: 20px;
73
+ }
74
+
75
+ .login-input {
76
+ width: 100%;
77
+ padding: 15px 50px 15px 15px;
78
+ border: 2px solid #e0e0e0;
79
+ border-radius: 10px;
80
+ font-size: 16px;
81
+ transition: border-color 0.3s ease;
82
+ }
83
+
84
+ .login-input:focus {
85
+ outline: none;
86
+ border-color: #667eea;
87
+ box-shadow: 0 0 0 3px rgba(102, 126, 234, 0.1);
88
+ }
89
+
90
+ .login-submit {
91
+ background: linear-gradient(135deg, #667eea, #764ba2);
92
+ color: white;
93
+ border: none;
94
+ padding: 15px 30px;
95
+ border-radius: 10px;
96
+ cursor: pointer;
97
+ font-weight: 600;
98
+ font-size: 16px;
99
+ transition: all 0.3s ease;
100
+ width: 100%;
101
+ margin-bottom: 15px;
102
+ }
103
+
104
+ .login-submit:hover {
105
+ transform: translateY(-2px);
106
+ box-shadow: 0 10px 20px rgba(102, 126, 234, 0.3);
107
+ }
108
+
109
+ .login-submit:disabled {
110
+ opacity: 0.6;
111
+ cursor: not-allowed;
112
+ transform: none;
113
+ }
114
+
115
+ .login-error {
116
+ background: rgba(220, 53, 69, 0.1);
117
+ color: #dc3545;
118
+ padding: 10px;
119
+ border-radius: 8px;
120
+ margin-bottom: 15px;
121
+ border: 1px solid rgba(220, 53, 69, 0.2);
122
+ display: none;
123
+ }
124
+
125
+ /* βœ… API Key Modal Styles */
126
+ .api-key-modal {
127
+ position: fixed;
128
+ top: 0;
129
+ left: 0;
130
+ width: 100%;
131
+ height: 100%;
132
+ background: rgba(0, 0, 0, 0.8);
133
+ display: flex;
134
+ justify-content: center;
135
+ align-items: center;
136
+ z-index: 1000;
137
+ }
138
+
139
+ .api-key-form {
140
+ background: white;
141
+ padding: 40px;
142
+ border-radius: 20px;
143
+ box-shadow: 0 20px 60px rgba(0, 0, 0, 0.3);
144
+ max-width: 500px;
145
+ width: 90%;
146
+ text-align: center;
147
+ }
148
+
149
+ .api-key-form h2 {
150
+ color: #333;
151
+ margin-bottom: 20px;
152
+ font-size: 1.8rem;
153
+ }
154
+
155
+ .api-key-form p {
156
+ color: #666;
157
+ margin-bottom: 30px;
158
+ line-height: 1.5;
159
+ }
160
+
161
+ .api-key-input-group {
162
+ position: relative;
163
+ margin-bottom: 20px;
164
+ }
165
+
166
+ .api-key-input {
167
+ width: 100%;
168
+ padding: 15px 50px 15px 15px;
169
+ border: 2px solid #e0e0e0;
170
+ border-radius: 10px;
171
+ font-size: 16px;
172
+ transition: border-color 0.3s ease;
173
+ }
174
+
175
+ .api-key-input:focus {
176
+ outline: none;
177
+ border-color: #667eea;
178
+ box-shadow: 0 0 0 3px rgba(102, 126, 234, 0.1);
179
+ }
180
+
181
+ .toggle-visibility {
182
+ position: absolute;
183
+ right: 15px;
184
+ top: 50%;
185
+ transform: translateY(-50%);
186
+ background: none;
187
+ border: none;
188
+ font-size: 16px;
189
+ cursor: pointer;
190
+ color: #667eea;
191
+ transition: color 0.3s ease;
192
+ }
193
+
194
+ .toggle-visibility:hover {
195
+ color: #764ba2;
196
+ }
197
+
198
+ .api-key-submit {
199
+ background: linear-gradient(135deg, #667eea, #764ba2);
200
+ color: white;
201
+ border: none;
202
+ padding: 15px 30px;
203
+ border-radius: 10px;
204
+ cursor: pointer;
205
+ font-weight: 600;
206
+ font-size: 16px;
207
+ transition: all 0.3s ease;
208
+ }
209
+
210
+ .api-key-submit:hover {
211
+ transform: translateY(-2px);
212
+ box-shadow: 0 10px 20px rgba(102, 126, 234, 0.3);
213
+ }
214
+
215
+ .api-key-submit:disabled {
216
+ opacity: 0.6;
217
+ cursor: not-allowed;
218
+ transform: none;
219
+ }
220
+
221
+ .container {
222
+ max-width: 1400px;
223
+ margin: 0 auto;
224
+ padding: 20px;
225
+ position: relative;
226
+ z-index: 1;
227
+ }
228
+
229
+ .header {
230
+ text-align: center;
231
+ margin-bottom: 40px;
232
+ color: white;
233
+ }
234
+
235
+ .header h1 {
236
+ font-size: 3rem;
237
+ font-weight: 700;
238
+ margin-bottom: 10px;
239
+ text-shadow: 2px 2px 4px rgba(0,0,0,0.3);
240
+ background: linear-gradient(45deg, #fff, #e0e0e0);
241
+ -webkit-background-clip: text;
242
+ -webkit-text-fill-color: transparent;
243
+ background-clip: text;
244
+ }
245
+
246
+ .header p {
247
+ font-size: 1.2rem;
248
+ opacity: 0.9;
249
+ }
250
+
251
+ /* βœ… User Status in header */
252
+ .user-status {
253
+ display: inline-flex;
254
+ align-items: center;
255
+ gap: 15px;
256
+ background: rgba(255, 255, 255, 0.2);
257
+ padding: 8px 15px;
258
+ border-radius: 20px;
259
+ margin-top: 10px;
260
+ font-size: 14px;
261
+ }
262
+
263
+ .user-info {
264
+ display: flex;
265
+ align-items: center;
266
+ gap: 8px;
267
+ }
268
+
269
+ .api-key-status {
270
+ display: flex;
271
+ align-items: center;
272
+ gap: 8px;
273
+ cursor: pointer;
274
+ transition: background 0.3s ease;
275
+ padding: 4px 8px;
276
+ border-radius: 15px;
277
+ }
278
+
279
+ .api-key-status:hover {
280
+ background: rgba(255, 255, 255, 0.1);
281
+ }
282
+
283
+ .logout-btn {
284
+ background: rgba(255, 255, 255, 0.2);
285
+ color: white;
286
+ border: none;
287
+ padding: 4px 12px;
288
+ border-radius: 15px;
289
+ cursor: pointer;
290
+ font-size: 12px;
291
+ transition: background 0.3s ease;
292
+ }
293
+
294
+ .logout-btn:hover {
295
+ background: rgba(255, 255, 255, 0.3);
296
+ }
297
+
298
+ .main-content {
299
+ display: grid;
300
+ grid-template-columns: 350px 1fr;
301
+ gap: 30px;
302
+ height: calc(100vh - 200px);
303
+ }
304
+
305
+ .sidebar {
306
+ background: rgba(255, 255, 255, 0.95);
307
+ backdrop-filter: blur(10px);
308
+ border-radius: 20px;
309
+ padding: 30px;
310
+ box-shadow: 0 20px 40px rgba(0, 0, 0, 0.1);
311
+ overflow-y: auto;
312
+ }
313
+
314
+ .chat-container {
315
+ background: rgba(255, 255, 255, 0.95);
316
+ backdrop-filter: blur(10px);
317
+ border-radius: 20px;
318
+ display: flex;
319
+ flex-direction: column;
320
+ box-shadow: 0 20px 40px rgba(0, 0, 0, 0.1);
321
+ }
322
+
323
+ .section-title {
324
+ font-size: 1.3rem;
325
+ font-weight: 600;
326
+ margin-bottom: 20px;
327
+ color: #333;
328
+ border-bottom: 2px solid #667eea;
329
+ padding-bottom: 10px;
330
+ }
331
+
332
+ .upload-area {
333
+ border: 2px dashed #667eea;
334
+ border-radius: 15px;
335
+ padding: 30px;
336
+ text-align: center;
337
+ margin-bottom: 20px;
338
+ transition: all 0.3s ease;
339
+ cursor: pointer;
340
+ }
341
+
342
+ .upload-area:hover {
343
+ border-color: #764ba2;
344
+ background: rgba(102, 126, 234, 0.05);
345
+ transform: translateY(-2px);
346
+ }
347
+
348
+ .upload-area.dragover {
349
+ border-color: #764ba2;
350
+ background: rgba(102, 126, 234, 0.1);
351
+ transform: scale(1.02);
352
+ }
353
+
354
+ .upload-icon {
355
+ font-size: 3rem;
356
+ color: #667eea;
357
+ margin-bottom: 15px;
358
+ }
359
+
360
+ .input-group {
361
+ margin-bottom: 20px;
362
+ }
363
+
364
+ .input-group label {
365
+ display: block;
366
+ margin-bottom: 8px;
367
+ font-weight: 600;
368
+ color: #333;
369
+ }
370
+
371
+ .input-group input {
372
+ width: 100%;
373
+ padding: 12px;
374
+ border: 2px solid #e0e0e0;
375
+ border-radius: 10px;
376
+ font-size: 14px;
377
+ transition: border-color 0.3s ease;
378
+ }
379
+
380
+ .input-group input:focus {
381
+ outline: none;
382
+ border-color: #667eea;
383
+ box-shadow: 0 0 0 3px rgba(102, 126, 234, 0.1);
384
+ }
385
+
386
+ .btn {
387
+ background: linear-gradient(135deg, #667eea, #764ba2);
388
+ color: white;
389
+ border: none;
390
+ padding: 12px 24px;
391
+ border-radius: 10px;
392
+ cursor: pointer;
393
+ font-weight: 600;
394
+ transition: all 0.3s ease;
395
+ width: 100%;
396
+ margin-bottom: 10px;
397
+ }
398
+
399
+ .btn:hover {
400
+ transform: translateY(-2px);
401
+ box-shadow: 0 10px 20px rgba(102, 126, 234, 0.3);
402
+ }
403
+
404
+ .btn:disabled {
405
+ opacity: 0.6;
406
+ cursor: not-allowed;
407
+ transform: none;
408
+ }
409
+
410
+ .papers-list {
411
+ margin-top: 30px;
412
+ }
413
+
414
+ .paper-item {
415
+ background: linear-gradient(135deg, #f8f9ff, #f0f2ff);
416
+ padding: 15px;
417
+ border-radius: 12px;
418
+ margin-bottom: 15px;
419
+ border-left: 4px solid #667eea;
420
+ transition: all 0.3s ease;
421
+ }
422
+
423
+ .paper-item:hover {
424
+ transform: translateX(5px);
425
+ box-shadow: 0 5px 15px rgba(102, 126, 234, 0.2);
426
+ }
427
+
428
+ .paper-title {
429
+ font-weight: 600;
430
+ color: #333;
431
+ margin-bottom: 8px;
432
+ font-size: 14px;
433
+ line-height: 1.4;
434
+ }
435
+
436
+ .paper-actions {
437
+ display: flex;
438
+ gap: 8px;
439
+ }
440
+
441
+ .btn-small {
442
+ background: linear-gradient(135deg, #667eea, #764ba2);
443
+ color: white;
444
+ border: none;
445
+ padding: 6px 12px;
446
+ border-radius: 6px;
447
+ cursor: pointer;
448
+ font-size: 12px;
449
+ font-weight: 500;
450
+ transition: all 0.3s ease;
451
+ }
452
+
453
+ .btn-small:hover {
454
+ transform: translateY(-1px);
455
+ box-shadow: 0 5px 10px rgba(102, 126, 234, 0.3);
456
+ }
457
+
458
+ .chat-header {
459
+ padding: 25px 30px;
460
+ border-bottom: 1px solid rgba(0, 0, 0, 0.1);
461
+ background: linear-gradient(135deg, #667eea, #764ba2);
462
+ color: white;
463
+ border-radius: 20px 20px 0 0;
464
+ }
465
+
466
+ .chat-header h2 {
467
+ font-size: 1.5rem;
468
+ font-weight: 600;
469
+ }
470
+
471
+ .chat-messages {
472
+ flex: 1;
473
+ padding: 20px;
474
+ overflow-y: auto;
475
+ max-height: calc(100vh - 400px);
476
+ }
477
+
478
+ .message {
479
+ margin-bottom: 20px;
480
+ animation: fadeInUp 0.5s ease;
481
+ }
482
+
483
+ @keyframes fadeInUp {
484
+ from {
485
+ opacity: 0;
486
+ transform: translateY(20px);
487
+ }
488
+ to {
489
+ opacity: 1;
490
+ transform: translateY(0);
491
+ }
492
+ }
493
+
494
+ .message.user {
495
+ text-align: right;
496
+ }
497
+
498
+ .message.bot {
499
+ text-align: left;
500
+ }
501
+
502
+ .message-bubble {
503
+ display: inline-block;
504
+ max-width: 70%;
505
+ padding: 15px 20px;
506
+ border-radius: 20px;
507
+ font-size: 14px;
508
+ line-height: 1.5;
509
+ }
510
+
511
+ .message.user .message-bubble {
512
+ background: linear-gradient(135deg, #667eea, #764ba2);
513
+ color: white;
514
+ border-bottom-right-radius: 5px;
515
+ }
516
+
517
+ .message.bot .message-bubble {
518
+ background: #f8f9fa;
519
+ color: #333;
520
+ border-bottom-left-radius: 5px;
521
+ border: 1px solid #e9ecef;
522
+ }
523
+
524
+ /* Enhanced styles for Markdown content in bot messages */
525
+ .message.bot .message-bubble h1,
526
+ .message.bot .message-bubble h2,
527
+ .message.bot .message-bubble h3 {
528
+ color: #667eea;
529
+ margin: 15px 0 10px 0;
530
+ font-weight: 600;
531
+ }
532
+
533
+ .message.bot .message-bubble h1 { font-size: 18px; }
534
+ .message.bot .message-bubble h2 { font-size: 16px; }
535
+ .message.bot .message-bubble h3 { font-size: 14px; }
536
+
537
+ .message.bot .message-bubble strong {
538
+ color: #495057;
539
+ font-weight: 600;
540
+ }
541
+
542
+ .message.bot .message-bubble table {
543
+ width: 100%;
544
+ border-collapse: collapse;
545
+ margin: 15px 0;
546
+ font-size: 12px;
547
+ }
548
+
549
+ .message.bot .message-bubble table th,
550
+ .message.bot .message-bubble table td {
551
+ padding: 8px 12px;
552
+ text-align: left;
553
+ border: 1px solid #dee2e6;
554
+ }
555
+
556
+ .message.bot .message-bubble table th {
557
+ background: linear-gradient(135deg, #667eea, #764ba2);
558
+ color: white;
559
+ font-weight: 600;
560
+ }
561
+
562
+ .message.bot .message-bubble table tr:nth-child(even) {
563
+ background-color: rgba(102, 126, 234, 0.05);
564
+ }
565
+
566
+ .message.bot .message-bubble ul,
567
+ .message.bot .message-bubble ol {
568
+ margin: 10px 0;
569
+ padding-left: 20px;
570
+ }
571
+
572
+ .message.bot .message-bubble li {
573
+ margin-bottom: 5px;
574
+ }
575
+
576
+ .message.bot .message-bubble code {
577
+ background: rgba(102, 126, 234, 0.1);
578
+ padding: 2px 6px;
579
+ border-radius: 4px;
580
+ font-family: 'Courier New', monospace;
581
+ font-size: 12px;
582
+ }
583
+
584
+ .message.bot .message-bubble pre {
585
+ background: rgba(102, 126, 234, 0.1);
586
+ padding: 15px;
587
+ border-radius: 8px;
588
+ overflow-x: auto;
589
+ margin: 10px 0;
590
+ }
591
+
592
+ .message.bot .message-bubble pre code {
593
+ background: none;
594
+ padding: 0;
595
+ }
596
+
597
+ .message.bot .message-bubble blockquote {
598
+ border-left: 4px solid #667eea;
599
+ padding-left: 15px;
600
+ margin: 15px 0;
601
+ font-style: italic;
602
+ color: #6c757d;
603
+ }
604
+
605
+ .chat-input {
606
+ padding: 20px;
607
+ border-top: 1px solid rgba(0, 0, 0, 0.1);
608
+ background: rgba(248, 249, 250, 0.5);
609
+ border-radius: 0 0 20px 20px;
610
+ }
611
+
612
+ .input-container {
613
+ display: flex;
614
+ gap: 10px;
615
+ }
616
+
617
+ .chat-input input {
618
+ flex: 1;
619
+ padding: 15px 20px;
620
+ border: 2px solid #e0e0e0;
621
+ border-radius: 25px;
622
+ font-size: 14px;
623
+ transition: border-color 0.3s ease;
624
+ }
625
+
626
+ .chat-input input:focus {
627
+ outline: none;
628
+ border-color: #667eea;
629
+ box-shadow: 0 0 0 3px rgba(102, 126, 234, 0.1);
630
+ }
631
+
632
+ .send-btn {
633
+ background: linear-gradient(135deg, #667eea, #764ba2);
634
+ color: white;
635
+ border: none;
636
+ padding: 15px 25px;
637
+ border-radius: 25px;
638
+ cursor: pointer;
639
+ font-weight: 600;
640
+ transition: all 0.3s ease;
641
+ }
642
+
643
+ .send-btn:hover {
644
+ transform: scale(1.05);
645
+ box-shadow: 0 5px 15px rgba(102, 126, 234, 0.3);
646
+ }
647
+
648
+ .loading {
649
+ display: inline-block;
650
+ width: 20px;
651
+ height: 20px;
652
+ border: 3px solid #f3f3f3;
653
+ border-top: 3px solid #667eea;
654
+ border-radius: 50%;
655
+ animation: spin 1s linear infinite;
656
+ }
657
+
658
+ @keyframes spin {
659
+ 0% { transform: rotate(0deg); }
660
+ 100% { transform: rotate(360deg); }
661
+ }
662
+
663
+ .status-indicator {
664
+ padding: 8px 12px;
665
+ border-radius: 20px;
666
+ font-size: 12px;
667
+ font-weight: 500;
668
+ margin-bottom: 15px;
669
+ text-align: center;
670
+ }
671
+
672
+ .status-ready {
673
+ background: rgba(40, 167, 69, 0.1);
674
+ color: #28a745;
675
+ border: 1px solid rgba(40, 167, 69, 0.2);
676
+ }
677
+
678
+ .status-processing {
679
+ background: rgba(255, 193, 7, 0.1);
680
+ color: #ffc107;
681
+ border: 1px solid rgba(255, 193, 7, 0.2);
682
+ }
683
+
684
+ .status-empty {
685
+ background: rgba(108, 117, 125, 0.1);
686
+ color: #6c757d;
687
+ border: 1px solid rgba(108, 117, 125, 0.2);
688
+ }
689
+
690
+ @media (max-width: 1024px) {
691
+ .main-content {
692
+ grid-template-columns: 1fr;
693
+ gap: 20px;
694
+ }
695
+
696
+ .sidebar {
697
+ order: 2;
698
+ }
699
+
700
+ .chat-container {
701
+ order: 1;
702
+ height: 60vh;
703
+ }
704
+ }
705
+
706
+ @media (max-width: 768px) {
707
+ .container {
708
+ padding: 10px;
709
+ }
710
+
711
+ .header h1 {
712
+ font-size: 2rem;
713
+ }
714
+
715
+ .main-content {
716
+ height: auto;
717
+ }
718
+
719
+ .message-bubble {
720
+ max-width: 85%;
721
+ }
722
+
723
+ .message.bot .message-bubble table {
724
+ font-size: 10px;
725
+ }
726
+
727
+ .message.bot .message-bubble table th,
728
+ .message.bot .message-bubble table td {
729
+ padding: 4px 6px;
730
+ }
731
+ }
732
+ </style>
733
+ </head>
734
+ <body>
735
+ <canvas class="bg-animation"></canvas>
736
+
737
+ <!-- βœ… Login Modal -->
738
+ <div id="loginModal" class="login-modal">
739
+ <div class="login-form">
740
+ <h2 id="authModalTitle">πŸ” Login</h2>
741
+ <p id="authModalDesc">Please enter your credentials to access the Research Paper Chatbot.</p>
742
+ <div class="login-error" id="loginError">Invalid username or password</div>
743
+ <div class="login-input-group">
744
+ <input type="text" id="usernameInput" class="login-input" placeholder="Username">
745
+ </div>
746
+ <div class="login-input-group">
747
+ <input type="password" id="passwordInput" class="login-input" placeholder="Password">
748
+ </div>
749
+ <button id="authSubmit" class="login-submit">Login</button>
750
+ <div style="text-align: center; margin-top: 15px;">
751
+ <span style="color: #666;">Don't have an account? </span>
752
+ <button type="button" id="toggleAuth" style="background: none; border: none; color: #667eea; cursor: pointer; text-decoration: underline; font-weight: 600;">Register here</button>
753
+ </div>
754
+ </div>
755
+ </div>
756
+
757
+ <!-- βœ… API Key Modal -->
758
+ <div id="apiKeyModal" class="api-key-modal" style="display: none;">
759
+ <div class="api-key-form">
760
+ <h2>πŸ”‘ Enter GROQ API Key</h2>
761
+ <p>Please enter your GROQ API key to use the chatbot. Your key will be stored securely in your session.</p>
762
+ <div class="api-key-input-group">
763
+ <input type="password" id="apiKeyInput" class="api-key-input" placeholder="Enter your GROQ API key...">
764
+ <button type="button" class="toggle-visibility" onclick="toggleApiKeyVisibility()">πŸ‘οΈ</button>
765
+ </div>
766
+ <button id="apiKeySubmit" class="api-key-submit">Connect</button>
767
+ </div>
768
+ </div>
769
+
770
+ <div class="container">
771
+ <div class="header">
772
+ <h1>🧠 Research Paper Chatbot</h1>
773
+ <p>Upload papers, explore references, and chat with your research collection</p>
774
+ <!-- βœ… User Status -->
775
+ <div class="user-status">
776
+ <div class="user-info">
777
+ <span>πŸ‘€</span>
778
+ <span id="currentUser">Not logged in</span>
779
+ </div>
780
+ <div class="api-key-status" onclick="showApiKeyModal()">
781
+ <span>πŸ”‘</span>
782
+ <span id="apiKeyStatusText">API Key: Not connected</span>
783
+ </div>
784
+ <button class="logout-btn" onclick="logout()">Logout</button>
785
+ </div>
786
+ </div>
787
+
788
+ <div class="main-content">
789
+ <div class="sidebar">
790
+ <div class="section-title">πŸ“„ Add Papers</div>
791
+ <div class="upload-area" id="uploadArea">
792
+ <div class="upload-icon">πŸ“Ž</div>
793
+ <p><strong>Drop PDF files here</strong></p>
794
+ <p>or click to browse</p>
795
+ <input type="file" id="fileInput" accept=".pdf" multiple style="display: none;">
796
+ </div>
797
+ <div class="input-group">
798
+ <label for="arxivUrl">ArXiv URL or ID</label>
799
+ <input type="text" id="arxivUrl" placeholder="https://arxiv.org/abs/2301.12345">
800
+ </div>
801
+ <button class="btn" id="addArxivBtn">Add from ArXiv</button>
802
+ <div id="statusIndicator" class="status-indicator status-empty">
803
+ No papers loaded
804
+ </div>
805
+ <div class="papers-list">
806
+ <div class="section-title">πŸ“š Your Papers</div>
807
+ <div id="papersList"></div>
808
+ </div>
809
+ </div>
810
+
811
+ <div class="chat-container">
812
+ <div class="chat-header"><h2>πŸ’¬ Chat with Papers</h2></div>
813
+ <div class="chat-messages" id="chatMessages">
814
+ <div class="message bot">
815
+ <div class="message-bubble">
816
+ Welcome! Upload some research papers to get started. I can help you understand the content, find connections between papers, and answer questions about your research collection. You can also ask me general questions anytime!
817
+ </div>
818
+ </div>
819
+ </div>
820
+ <div class="chat-input">
821
+ <div class="input-container">
822
+ <input type="text" id="questionInput" placeholder="Ask about your papers...">
823
+ <button class="send-btn" id="sendBtn">Send</button>
824
+ </div>
825
+ </div>
826
+ </div>
827
+ </div>
828
+ </div>
829
+
830
+ <script>
831
+ const API_BASE = "https://yashb1-rag-chatbot-backend.hf.space"; // FastAPI backend
832
+
833
+ // App state
834
+ let papers = [];
835
+ let isProcessing = false;
836
+ let isLoggedIn = false;
837
+ let authCredentials = null;
838
+ let isLoginMode = true; // true for login, false for register
839
+
840
+ // DOM elements
841
+ const loginModal = document.getElementById('loginModal');
842
+ const usernameInput = document.getElementById('usernameInput');
843
+ const passwordInput = document.getElementById('passwordInput');
844
+ const authSubmit = document.getElementById('authSubmit');
845
+ const loginError = document.getElementById('loginError');
846
+ const currentUser = document.getElementById('currentUser');
847
+ const authModalTitle = document.getElementById('authModalTitle');
848
+ const authModalDesc = document.getElementById('authModalDesc');
849
+ const toggleAuth = document.getElementById('toggleAuth');
850
+ const apiKeyModal = document.getElementById('apiKeyModal');
851
+ const apiKeyInput = document.getElementById('apiKeyInput');
852
+ const apiKeySubmit = document.getElementById('apiKeySubmit');
853
+ const apiKeyStatusText = document.getElementById('apiKeyStatusText');
854
+ const uploadArea = document.getElementById('uploadArea');
855
+ const fileInput = document.getElementById('fileInput');
856
+ const arxivUrl = document.getElementById('arxivUrl');
857
+ const addArxivBtn = document.getElementById('addArxivBtn');
858
+ const statusIndicator = document.getElementById('statusIndicator');
859
+ const papersList = document.getElementById('papersList');
860
+ const chatMessages = document.getElementById('chatMessages');
861
+ const questionInput = document.getElementById('questionInput');
862
+ const sendBtn = document.getElementById('sendBtn');
863
+
864
+ // Configure Markdown parser for better table rendering
865
+ marked.setOptions({
866
+ breaks: true,
867
+ gfm: true,
868
+ tables: true,
869
+ sanitize: false
870
+ });
871
+
872
+ // βœ… Authentication Functions
873
+ function showLoginModal() {
874
+ loginModal.style.display = 'flex';
875
+ usernameInput.focus();
876
+ loginError.style.display = 'none';
877
+ updateAuthModal();
878
+ }
879
+
880
+ function hideLoginModal() {
881
+ loginModal.style.display = 'none';
882
+ }
883
+
884
+ function toggleAuthMode() {
885
+ isLoginMode = !isLoginMode;
886
+ updateAuthModal();
887
+ loginError.style.display = 'none';
888
+ usernameInput.value = '';
889
+ passwordInput.value = '';
890
+ usernameInput.focus();
891
+ }
892
+
893
+ function updateAuthModal() {
894
+ if (isLoginMode) {
895
+ authModalTitle.textContent = 'πŸ” Login';
896
+ authModalDesc.textContent = 'Please enter your credentials to access the Research Paper Chatbot.';
897
+ authSubmit.textContent = 'Login';
898
+ toggleAuth.innerHTML = '<span style="color: #666;">Don\'t have an account? </span><span style="color: #667eea; text-decoration: underline;">Register here</span>';
899
+ } else {
900
+ authModalTitle.textContent = 'πŸ“ Register';
901
+ authModalDesc.textContent = 'Create a new account to access the Research Paper Chatbot.';
902
+ authSubmit.textContent = 'Register';
903
+ toggleAuth.innerHTML = '<span style="color: #666;">Already have an account? </span><span style="color: #667eea; text-decoration: underline;">Login here</span>';
904
+ }
905
+ }
906
+
907
+ async function login() {
908
+ const username = usernameInput.value.trim();
909
+ const password = passwordInput.value.trim();
910
+
911
+ if (!username || !password) {
912
+ showLoginError('Please enter both username and password');
913
+ return;
914
+ }
915
+
916
+ // Validation for registration
917
+ if (!isLoginMode) {
918
+ if (username.length < 3) {
919
+ showLoginError('Username must be at least 3 characters');
920
+ return;
921
+ }
922
+ if (password.length < 6) {
923
+ showLoginError('Password must be at least 6 characters');
924
+ return;
925
+ }
926
+ }
927
+
928
+ authSubmit.disabled = true;
929
+ authSubmit.textContent = isLoginMode ? 'Logging in...' : 'Creating account...';
930
+
931
+ try {
932
+ if (isLoginMode) {
933
+ // Login flow
934
+ const credentials = btoa(`${username}:${password}`);
935
+
936
+ // Test authentication by making a simple request
937
+ const response = await fetch(`${API_BASE}/ask/?q=test`, {
938
+ method: 'GET',
939
+ headers: {
940
+ 'Authorization': `Basic ${credentials}`
941
+ }
942
+ });
943
+
944
+ if (response.ok) {
945
+ // Login successful
946
+ authCredentials = credentials;
947
+ isLoggedIn = true;
948
+ currentUser.textContent = username;
949
+ hideLoginModal();
950
+ // Clear API key input and status for new login session
951
+ apiKeyInput.value = '';
952
+ apiKeyStatusText.textContent = 'API Key: Not connected';
953
+ showApiKeyModal();
954
+ addBotMessage('βœ… Login successful! Please enter your GROQ API key to continue.');
955
+ } else if (response.status === 401) {
956
+ showLoginError('Invalid username or password');
957
+ } else {
958
+ showLoginError(`Login failed: ${response.statusText}`);
959
+ }
960
+ } else {
961
+ // Registration flow
962
+ const response = await fetch(`${API_BASE}/register/`, {
963
+ method: 'POST',
964
+ headers: {
965
+ 'Content-Type': 'application/json'
966
+ },
967
+ body: JSON.stringify({
968
+ username: username,
969
+ password: password
970
+ })
971
+ });
972
+
973
+ if (response.ok) {
974
+ // Registration successful, switch to login mode
975
+ showLoginError(''); // Clear any errors
976
+ addBotMessage('βœ… Account created successfully! Please login with your new credentials.');
977
+ isLoginMode = true;
978
+ updateAuthModal();
979
+ usernameInput.value = username; // Keep username filled
980
+ passwordInput.value = '';
981
+ passwordInput.focus();
982
+ } else {
983
+ const errorData = await response.json();
984
+ showLoginError(errorData.detail || 'Registration failed');
985
+ }
986
+ }
987
+ } catch (error) {
988
+ console.error('Auth error:', error);
989
+ showLoginError('Failed to connect to server');
990
+ } finally {
991
+ authSubmit.disabled = false;
992
+ authSubmit.textContent = isLoginMode ? 'Login' : 'Register';
993
+ }
994
+ }
995
+
996
+ function showLoginError(message) {
997
+ loginError.textContent = message;
998
+ loginError.style.display = 'block';
999
+ }
1000
+
1001
+ function logout() {
1002
+ isLoggedIn = false;
1003
+ authCredentials = null;
1004
+ isLoginMode = true; // Reset to login mode
1005
+ currentUser.textContent = 'Not logged in';
1006
+ apiKeyStatusText.textContent = 'API Key: Not connected';
1007
+ papers = [];
1008
+ updatePapersList();
1009
+ updateStatus();
1010
+ // Clear input fields
1011
+ usernameInput.value = '';
1012
+ passwordInput.value = '';
1013
+ // Clear API key input
1014
+ apiKeyInput.value = '';
1015
+ // Clear chat messages
1016
+ chatMessages.innerHTML = `
1017
+ <div class="message bot">
1018
+ <div class="message-bubble">
1019
+ Welcome! Upload some research papers to get started. I can help you understand the content, find connections between papers, and answer questions about your research collection. You can also ask me general questions anytime!
1020
+ </div>
1021
+ </div>
1022
+ `;
1023
+ showLoginModal();
1024
+ }
1025
+
1026
+ // βœ… API Key Functions
1027
+ function toggleApiKeyVisibility() {
1028
+ const input = apiKeyInput;
1029
+ const button = document.querySelector('.toggle-visibility');
1030
+ if (input.type === 'password') {
1031
+ input.type = 'text';
1032
+ button.textContent = 'πŸ™ˆ';
1033
+ } else {
1034
+ input.type = 'password';
1035
+ button.textContent = 'πŸ‘οΈ';
1036
+ }
1037
+ }
1038
+
1039
+ function showApiKeyModal() {
1040
+ if (!isLoggedIn) {
1041
+ showLoginModal();
1042
+ return;
1043
+ }
1044
+ // Clear the API key input for new users
1045
+ apiKeyInput.value = '';
1046
+ apiKeyModal.style.display = 'flex';
1047
+ apiKeyInput.focus();
1048
+ }
1049
+
1050
+ function hideApiKeyModal() {
1051
+ apiKeyModal.style.display = 'none';
1052
+ }
1053
+
1054
+ async function setApiKey() {
1055
+ const key = apiKeyInput.value.trim();
1056
+ if (!key) {
1057
+ alert('Please enter a valid API key');
1058
+ return;
1059
+ }
1060
+
1061
+ apiKeySubmit.disabled = true;
1062
+ apiKeySubmit.textContent = 'Connecting...';
1063
+
1064
+ try {
1065
+ // Send API key to backend for ephemeral storage
1066
+ const response = await fetch(`${API_BASE}/set_api_key/`, {
1067
+ method: 'POST',
1068
+ headers: {
1069
+ 'Content-Type': 'application/json',
1070
+ 'Authorization': `Basic ${authCredentials}`
1071
+ },
1072
+ body: JSON.stringify({ api_key: key })
1073
+ });
1074
+
1075
+ if (!response.ok) {
1076
+ throw new Error(`Failed to set API key: ${response.statusText}`);
1077
+ }
1078
+
1079
+ apiKeyStatusText.textContent = `API Key: Connected`;
1080
+ hideApiKeyModal();
1081
+ addBotMessage('βœ… GROQ API key connected successfully!');
1082
+ } catch (error) {
1083
+ console.error('API key setup error:', error);
1084
+ addBotMessage(`❌ Failed to connect API key: ${error.message}`);
1085
+ } finally {
1086
+ apiKeySubmit.disabled = false;
1087
+ apiKeySubmit.textContent = 'Connect';
1088
+ }
1089
+ }
1090
+
1091
+ // βœ… Initialize app - show login modal first
1092
+ function initApp() {
1093
+ showLoginModal();
1094
+ }
1095
+
1096
+ // Background animation
1097
+ function initBackgroundAnimation() {
1098
+ const canvas = document.querySelector('.bg-animation');
1099
+ const ctx = canvas.getContext('2d');
1100
+ let animationId;
1101
+
1102
+ function resizeCanvas() {
1103
+ canvas.width = window.innerWidth;
1104
+ canvas.height = window.innerHeight;
1105
+ }
1106
+
1107
+ resizeCanvas();
1108
+ window.addEventListener('resize', resizeCanvas);
1109
+
1110
+ const particles = [];
1111
+ for (let i = 0; i < 50; i++) {
1112
+ particles.push({
1113
+ x: Math.random() * canvas.width,
1114
+ y: Math.random() * canvas.height,
1115
+ vx: (Math.random() - 0.5) * 0.5,
1116
+ vy: (Math.random() - 0.5) * 0.5,
1117
+ size: Math.random() * 2 + 1
1118
+ });
1119
+ }
1120
+
1121
+ function animate() {
1122
+ ctx.clearRect(0, 0, canvas.width, canvas.height);
1123
+ ctx.fillStyle = 'rgba(255, 255, 255, 0.1)';
1124
+
1125
+ particles.forEach(particle => {
1126
+ particle.x += particle.vx;
1127
+ particle.y += particle.vy;
1128
+
1129
+ if (particle.x < 0 || particle.x > canvas.width) particle.vx *= -1;
1130
+ if (particle.y < 0 || particle.y > canvas.height) particle.vy *= -1;
1131
+
1132
+ ctx.beginPath();
1133
+ ctx.arc(particle.x, particle.y, particle.size, 0, Math.PI * 2);
1134
+ ctx.fill();
1135
+ });
1136
+
1137
+ animationId = requestAnimationFrame(animate);
1138
+ }
1139
+
1140
+ animate();
1141
+ }
1142
+
1143
+ // Event Listeners
1144
+ authSubmit.addEventListener('click', login);
1145
+ toggleAuth.addEventListener('click', toggleAuthMode);
1146
+ usernameInput.addEventListener('keypress', (e) => {
1147
+ if (e.key === 'Enter') passwordInput.focus();
1148
+ });
1149
+ passwordInput.addEventListener('keypress', (e) => {
1150
+ if (e.key === 'Enter') login();
1151
+ });
1152
+
1153
+ apiKeySubmit.addEventListener('click', setApiKey);
1154
+ apiKeyInput.addEventListener('keypress', (e) => {
1155
+ if (e.key === 'Enter') setApiKey();
1156
+ });
1157
+
1158
+ // File upload handlers
1159
+ uploadArea.addEventListener('click', () => {
1160
+ if (!isLoggedIn) {
1161
+ addBotMessage('Please login first to upload papers');
1162
+ return;
1163
+ }
1164
+ fileInput.click();
1165
+ });
1166
+ uploadArea.addEventListener('dragover', e => { e.preventDefault(); if (isLoggedIn) uploadArea.classList.add('dragover'); });
1167
+ uploadArea.addEventListener('dragleave', () => uploadArea.classList.remove('dragover'));
1168
+ uploadArea.addEventListener('drop', e => {
1169
+ e.preventDefault();
1170
+ uploadArea.classList.remove('dragover');
1171
+ if (isLoggedIn) handleFiles(e.dataTransfer.files);
1172
+ else addBotMessage('Please login first to upload papers');
1173
+ });
1174
+ fileInput.addEventListener('change', e => handleFiles(e.target.files));
1175
+
1176
+ // ArXiv handler
1177
+ addArxivBtn.addEventListener('click', () => {
1178
+ if (!isLoggedIn) {
1179
+ addBotMessage('Please login first to add papers');
1180
+ return;
1181
+ }
1182
+ const url = arxivUrl.value.trim();
1183
+ if (url) { addPaperFromArxiv(url); arxivUrl.value = ''; }
1184
+ });
1185
+
1186
+ // Chat handlers
1187
+ questionInput.addEventListener('keypress', e => {
1188
+ if (e.key === 'Enter') {
1189
+ if (!isLoggedIn) {
1190
+ addBotMessage('Please login first to ask questions');
1191
+ return;
1192
+ }
1193
+ sendMessage();
1194
+ }
1195
+ });
1196
+ sendBtn.addEventListener('click', () => {
1197
+ if (!isLoggedIn) {
1198
+ addBotMessage('Please login first to ask questions');
1199
+ return;
1200
+ }
1201
+ sendMessage();
1202
+ });
1203
+
1204
+ // Helper function to make authenticated requests
1205
+ async function makeAuthenticatedRequest(url, options = {}) {
1206
+ if (!authCredentials) {
1207
+ throw new Error('Not authenticated');
1208
+ }
1209
+
1210
+ const headers = {
1211
+ 'Authorization': `Basic ${authCredentials}`,
1212
+ ...options.headers
1213
+ };
1214
+
1215
+ return fetch(url, {
1216
+ ...options,
1217
+ headers
1218
+ });
1219
+ }
1220
+
1221
+ // Functions (updated to use authentication)
1222
+ function handleFiles(files) {
1223
+ Array.from(files).forEach(file => {
1224
+ if (file.type === 'application/pdf') addPaperFromPDF(file);
1225
+ });
1226
+ }
1227
+
1228
+ async function addPaperFromPDF(file) {
1229
+ setStatus('processing', 'Processing PDF...');
1230
+ const formData = new FormData();
1231
+ formData.append("file", file);
1232
+ try {
1233
+ const res = await makeAuthenticatedRequest(`${API_BASE}/upload_pdf/`, {
1234
+ method: "POST",
1235
+ body: formData
1236
+ });
1237
+ if (!res.ok) {
1238
+ throw new Error(`HTTP ${res.status}: ${res.statusText}`);
1239
+ }
1240
+ const data = await res.json();
1241
+ const paper = {
1242
+ id: Date.now(),
1243
+ title: data.context_papers.slice(-1)[0] || file.name,
1244
+ type: 'pdf',
1245
+ hasReferences: false,
1246
+ referencesLoaded: false
1247
+ };
1248
+ papers.push(paper);
1249
+ updatePapersList();
1250
+ isProcessing = false;
1251
+ updateStatus();
1252
+ addBotMessage(`βœ… Added: "${paper.title}"`);
1253
+ } catch (err) {
1254
+ console.error('PDF upload error:', err);
1255
+ addBotMessage(`❌ PDF upload failed: ${err.message}`);
1256
+ isProcessing = false;
1257
+ updateStatus();
1258
+ }
1259
+ }
1260
+
1261
+ async function addPaperFromArxiv(url) {
1262
+ setStatus('processing', 'Fetching from ArXiv...');
1263
+ const formData = new FormData();
1264
+ formData.append("arxiv_id", url);
1265
+ try {
1266
+ const res = await makeAuthenticatedRequest(`${API_BASE}/add_arxiv/`, {
1267
+ method: "POST",
1268
+ body: formData
1269
+ });
1270
+ if (!res.ok) {
1271
+ throw new Error(`HTTP ${res.status}: ${res.statusText}`);
1272
+ }
1273
+ const data = await res.json();
1274
+ const paper = {
1275
+ id: Date.now(),
1276
+ title: data.context_papers.slice(-1)[0] || `ArXiv: ${url}`,
1277
+ type: 'arxiv',
1278
+ hasReferences: false,
1279
+ referencesLoaded: false
1280
+ };
1281
+ papers.push(paper);
1282
+ updatePapersList();
1283
+ isProcessing = false;
1284
+ updateStatus();
1285
+ addBotMessage(`βœ… Added: "${paper.title}"`);
1286
+ } catch (err) {
1287
+ console.error('ArXiv fetch error:', err);
1288
+ addBotMessage(`❌ ArXiv fetch failed: ${err.message}`);
1289
+ isProcessing = false;
1290
+ updateStatus();
1291
+ }
1292
+ }
1293
+
1294
+ async function addReferences(paperId) {
1295
+ const paperIndex = papers.findIndex(p => p.id === paperId);
1296
+ if (paperIndex === -1) {
1297
+ addBotMessage("❌ Paper not found");
1298
+ return;
1299
+ }
1300
+
1301
+ if (papers[paperIndex].referencesLoaded) {
1302
+ addBotMessage(`οΏ½οΏ½ References for "${papers[paperIndex].title}" already loaded`);
1303
+ return;
1304
+ }
1305
+
1306
+ setStatus('processing', 'Loading references...');
1307
+ const formData = new FormData();
1308
+ formData.append("index", paperIndex.toString());
1309
+
1310
+ try {
1311
+ const res = await makeAuthenticatedRequest(`${API_BASE}/add_references/`, {
1312
+ method: "POST",
1313
+ body: formData
1314
+ });
1315
+ if (!res.ok) {
1316
+ throw new Error(`HTTP ${res.status}: ${res.statusText}`);
1317
+ }
1318
+ const data = await res.json();
1319
+ papers[paperIndex].hasReferences = true;
1320
+ papers[paperIndex].referencesLoaded = true;
1321
+
1322
+ if (data.references && data.references.length > 0) {
1323
+ data.references.forEach(refTitle => {
1324
+ papers.push({
1325
+ id: Date.now() + Math.random(),
1326
+ title: refTitle,
1327
+ type: 'reference',
1328
+ hasReferences: false,
1329
+ referencesLoaded: false,
1330
+ isReference: true
1331
+ });
1332
+ });
1333
+ }
1334
+
1335
+ updatePapersList();
1336
+ isProcessing = false;
1337
+ updateStatus();
1338
+
1339
+ if (data.references && data.references.length > 0) {
1340
+ addBotMessage(`πŸ“š Added ${data.references.length} references for "${papers[paperIndex].title}": ${data.references.join(", ")}`);
1341
+ } else {
1342
+ addBotMessage(`πŸ“š No references found for "${papers[paperIndex].title}"`);
1343
+ }
1344
+ } catch (err) {
1345
+ console.error('Reference fetch error:', err);
1346
+ addBotMessage(`❌ Reference fetch failed for "${papers[paperIndex].title}": ${err.message}`);
1347
+ isProcessing = false;
1348
+ updateStatus();
1349
+ }
1350
+ }
1351
+
1352
+ async function sendMessage() {
1353
+ const question = questionInput.value.trim();
1354
+ if (!question) return;
1355
+
1356
+ if (papers.length === 0) {
1357
+ addBotMessage("Please upload some papers first before asking questions!");
1358
+ return;
1359
+ }
1360
+
1361
+ addUserMessage(question);
1362
+ questionInput.value = '';
1363
+ setStatus('processing', 'Thinking...');
1364
+
1365
+ try {
1366
+ const res = await makeAuthenticatedRequest(`${API_BASE}/ask/?q=${encodeURIComponent(question)}`);
1367
+ if (!res.ok) {
1368
+ throw new Error(`HTTP ${res.status}: ${res.statusText}`);
1369
+ }
1370
+ const data = await res.json();
1371
+ addBotMessage(data.answer || "I couldn't find an answer to your question.");
1372
+ isProcessing = false;
1373
+ updateStatus();
1374
+ } catch (err) {
1375
+ console.error('QA error:', err);
1376
+ addBotMessage(`❌ Failed to get answer: ${err.message}`);
1377
+ isProcessing = false;
1378
+ updateStatus();
1379
+ }
1380
+ }
1381
+
1382
+ function updatePapersList() {
1383
+ papersList.innerHTML = '';
1384
+ papers.forEach(paper => {
1385
+ const div = document.createElement('div');
1386
+ div.className = 'paper-item';
1387
+
1388
+ if (paper.isReference) {
1389
+ div.style.borderLeft = '4px solid #28a745';
1390
+ div.style.backgroundColor = 'rgba(40, 167, 69, 0.05)';
1391
+ }
1392
+
1393
+ div.innerHTML = `
1394
+ <div class="paper-title">
1395
+ ${paper.isReference ? 'πŸ“Ž ' : ''}${paper.title}
1396
+ ${paper.isReference ? ' <small style="color: #6c757d;">(Reference)</small>' : ''}
1397
+ </div>
1398
+ <div class="paper-actions">
1399
+ ${!paper.isReference ? `
1400
+ <button class="btn-small" onclick="addReferences(${paper.id})" ${paper.referencesLoaded ? 'disabled' : ''}>
1401
+ ${paper.referencesLoaded ? 'βœ“ Refs Loaded' : '+ Add References'}
1402
+ </button>
1403
+ ` : '<small style="color: #6c757d;">Reference paper</small>'}
1404
+ </div>`;
1405
+ papersList.appendChild(div);
1406
+ });
1407
+ }
1408
+
1409
+ function setStatus(type, message) {
1410
+ statusIndicator.className = `status-indicator status-${type}`;
1411
+ statusIndicator.textContent = message;
1412
+ isProcessing = (type === 'processing');
1413
+
1414
+ if (type === 'processing') {
1415
+ setTimeout(() => {
1416
+ if (isProcessing) {
1417
+ console.warn('Status stuck in processing, force clearing...');
1418
+ updateStatus();
1419
+ }
1420
+ }, 30000);
1421
+ }
1422
+ }
1423
+
1424
+ function updateStatus() {
1425
+ if (!isProcessing) {
1426
+ if (papers.length === 0) {
1427
+ setStatus('empty', 'No papers loaded');
1428
+ } else {
1429
+ setStatus('ready', `${papers.length} paper${papers.length > 1 ? 's' : ''} loaded`);
1430
+ questionInput.disabled = false;
1431
+ sendBtn.disabled = false;
1432
+ }
1433
+ }
1434
+ }
1435
+
1436
+ function addUserMessage(msg) {
1437
+ const div = document.createElement('div');
1438
+ div.className = 'message user';
1439
+ div.innerHTML = `<div class="message-bubble">${msg}</div>`;
1440
+ chatMessages.appendChild(div);
1441
+ chatMessages.scrollTop = chatMessages.scrollHeight;
1442
+ }
1443
+
1444
+ function addBotMessage(msg) {
1445
+ const div = document.createElement('div');
1446
+ div.className = 'message bot';
1447
+
1448
+ const renderedMessage = marked.parse(msg);
1449
+ div.innerHTML = `<div class="message-bubble">${renderedMessage}</div>`;
1450
+
1451
+ chatMessages.appendChild(div);
1452
+ chatMessages.scrollTop = chatMessages.scrollHeight;
1453
+ }
1454
+
1455
+ // Initialize app
1456
+ initBackgroundAnimation();
1457
+ initApp();
1458
+
1459
+ window.addReferences = addReferences;
1460
+ window.logout = logout;
1461
+ window.toggleAuthMode = toggleAuthMode;
1462
+ </script>
1463
+ </body>
1464
+ </html>