jasvir-singh1021 commited on
Commit
e7a4605
·
verified ·
1 Parent(s): 7aa8120

Upload src/streamlit_app.py with huggingface_hub

Browse files
Files changed (1) hide show
  1. src/streamlit_app.py +752 -40
src/streamlit_app.py CHANGED
@@ -1,40 +1,752 @@
1
- import altair as alt
2
- import numpy as np
3
- import pandas as pd
4
- import streamlit as st
5
-
6
- """
7
- # Welcome to Streamlit!
8
-
9
- Edit `/streamlit_app.py` to customize this app to your heart's desire :heart:.
10
- If you have any questions, checkout our [documentation](https://docs.streamlit.io) and [community
11
- forums](https://discuss.streamlit.io).
12
-
13
- In the meantime, below is an example of what you can do with just a few lines of code:
14
- """
15
-
16
- num_points = st.slider("Number of points in spiral", 1, 10000, 1100)
17
- num_turns = st.slider("Number of turns in spiral", 1, 300, 31)
18
-
19
- indices = np.linspace(0, 1, num_points)
20
- theta = 2 * np.pi * num_turns * indices
21
- radius = indices
22
-
23
- x = radius * np.cos(theta)
24
- y = radius * np.sin(theta)
25
-
26
- df = pd.DataFrame({
27
- "x": x,
28
- "y": y,
29
- "idx": indices,
30
- "rand": np.random.randn(num_points),
31
- })
32
-
33
- st.altair_chart(alt.Chart(df, height=700, width=700)
34
- .mark_point(filled=True)
35
- .encode(
36
- x=alt.X("x", axis=None),
37
- y=alt.Y("y", axis=None),
38
- color=alt.Color("idx", legend=None, scale=alt.Scale()),
39
- size=alt.Size("rand", legend=None, scale=alt.Scale(range=[1, 150])),
40
- ))
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
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>Document Parser - LlamaIndex & GPT-4</title>
7
+ <link rel="stylesheet" href="https://cdnjs.cloudflare.com/ajax/libs/font-awesome/6.4.0/css/all.min.css">
8
+ <style>
9
+ :root {
10
+ --primary-color: #1a1a2e;
11
+ --secondary-color: #16213e;
12
+ --accent-color: #0f3460;
13
+ --highlight-color: #e94560;
14
+ --text-primary: #ffffff;
15
+ --text-secondary: #b3b3b3;
16
+ --bg-primary: #0a0a0a;
17
+ --bg-secondary: #1a1a1a;
18
+ --bg-tertiary: #2a2a2a;
19
+ --success-color: #4ade80;
20
+ --warning-color: #fbbf24;
21
+ --error-color: #ef4444;
22
+ --border-radius: 12px;
23
+ --shadow-sm: 0 2px 4px rgba(0, 0, 0, 0.1);
24
+ --shadow-md: 0 4px 6px rgba(0, 0, 0, 0.1);
25
+ --shadow-lg: 0 10px 15px rgba(0, 0, 0, 0.2);
26
+ --transition: all 0.3s ease;
27
+ }
28
+
29
+ * {
30
+ margin: 0;
31
+ padding: 0;
32
+ box-sizing: border-box;
33
+ }
34
+
35
+ body {
36
+ font-family: -apple-system, BlinkMacSystemFont, 'Segoe UI', Roboto, 'Helvetica Neue', Arial, sans-serif;
37
+ background: var(--bg-primary);
38
+ color: var(--text-primary);
39
+ line-height: 1.6;
40
+ min-height: 100vh;
41
+ overflow-x: hidden;
42
+ }
43
+
44
+ .container {
45
+ max-width: 1200px;
46
+ margin: 0 auto;
47
+ padding: 2rem;
48
+ }
49
+
50
+ .header {
51
+ text-align: center;
52
+ margin-bottom: 3rem;
53
+ animation: fadeInDown 0.6s ease;
54
+ }
55
+
56
+ .header h1 {
57
+ font-size: 2.5rem;
58
+ font-weight: 700;
59
+ background: linear-gradient(135deg, var(--highlight-color), #ff6b6b);
60
+ -webkit-background-clip: text;
61
+ -webkit-text-fill-color: transparent;
62
+ margin-bottom: 0.5rem;
63
+ }
64
+
65
+ .header p {
66
+ color: var(--text-secondary);
67
+ font-size: 1.2rem;
68
+ }
69
+
70
+ .card {
71
+ background: var(--bg-secondary);
72
+ border-radius: var(--border-radius);
73
+ padding: 2rem;
74
+ box-shadow: var(--shadow-lg);
75
+ border: 1px solid #333;
76
+ margin-bottom: 2rem;
77
+ transition: var(--transition);
78
+ }
79
+
80
+ .card:hover {
81
+ transform: translateY(-2px);
82
+ box-shadow: 0 15px 25px rgba(0, 0, 0, 0.3);
83
+ }
84
+
85
+ .input-group {
86
+ margin-bottom: 1.5rem;
87
+ }
88
+
89
+ .input-label {
90
+ display: block;
91
+ margin-bottom: 0.5rem;
92
+ font-weight: 600;
93
+ color: var(--text-primary);
94
+ }
95
+
96
+ .input-field {
97
+ width: 100%;
98
+ padding: 0.75rem 1rem;
99
+ background: var(--bg-tertiary);
100
+ border: 1px solid #444;
101
+ border-radius: var(--border-radius);
102
+ color: var(--text-primary);
103
+ font-size: 1rem;
104
+ transition: var(--transition);
105
+ }
106
+
107
+ .input-field:focus {
108
+ outline: none;
109
+ border-color: var(--highlight-color);
110
+ box-shadow: 0 0 0 3px rgba(233, 69, 96, 0.2);
111
+ }
112
+
113
+ .slider-container {
114
+ margin-bottom: 2rem;
115
+ }
116
+
117
+ .slider {
118
+ width: 100%;
119
+ height: 6px;
120
+ background: var(--bg-tertiary);
121
+ border-radius: 3px;
122
+ outline: none;
123
+ -webkit-appearance: none;
124
+ cursor: pointer;
125
+ }
126
+
127
+ .slider::-webkit-slider-thumb {
128
+ -webkit-appearance: none;
129
+ appearance: none;
130
+ width: 20px;
131
+ height: 20px;
132
+ background: var(--highlight-color);
133
+ border-radius: 50%;
134
+ cursor: pointer;
135
+ transition: var(--transition);
136
+ }
137
+
138
+ .slider::-webkit-slider-thumb:hover {
139
+ transform: scale(1.2);
140
+ }
141
+
142
+ .file-upload {
143
+ position: relative;
144
+ display: inline-block;
145
+ width: 100%;
146
+ }
147
+
148
+ .file-upload-label {
149
+ display: flex;
150
+ align-items: center;
151
+ justify-content: center;
152
+ padding: 2rem;
153
+ border: 2px dashed #555;
154
+ border-radius: var(--border-radius);
155
+ cursor: pointer;
156
+ transition: var(--transition);
157
+ background: var(--bg-tertiary);
158
+ }
159
+
160
+ .file-upload-label:hover {
161
+ border-color: var(--highlight-color);
162
+ background: var(--bg-secondary);
163
+ }
164
+
165
+ .file-upload-input {
166
+ display: none;
167
+ }
168
+
169
+ .upload-icon {
170
+ font-size: 3rem;
171
+ color: var(--highlight-color);
172
+ margin-bottom: 1rem;
173
+ }
174
+
175
+ .btn {
176
+ display: inline-flex;
177
+ align-items: center;
178
+ justify-content: center;
179
+ padding: 0.75rem 1.5rem;
180
+ background: var(--highlight-color);
181
+ color: white;
182
+ border: none;
183
+ border-radius: var(--border-radius);
184
+ font-size: 1rem;
185
+ font-weight: 600;
186
+ cursor: pointer;
187
+ transition: var(--transition);
188
+ text-decoration: none;
189
+ gap: 0.5rem;
190
+ }
191
+
192
+ .btn:hover {
193
+ background: #ff5252;
194
+ transform: translateY(-1px);
195
+ box-shadow: var(--shadow-md);
196
+ }
197
+
198
+ .btn-secondary {
199
+ background: var(--bg-tertiary);
200
+ color: var(--text-primary);
201
+ border: 1px solid #555;
202
+ }
203
+
204
+ .btn-secondary:hover {
205
+ background: #444;
206
+ }
207
+
208
+ .btn-danger {
209
+ background: var(--error-color);
210
+ }
211
+
212
+ .btn-danger:hover {
213
+ background: #dc2626;
214
+ }
215
+
216
+ .chat-container {
217
+ max-height: 600px;
218
+ overflow-y: auto;
219
+ padding: 1rem;
220
+ background: var(--bg-secondary);
221
+ border-radius: var(--border-radius);
222
+ margin-bottom: 1rem;
223
+ }
224
+
225
+ .chat-message {
226
+ display: flex;
227
+ margin-bottom: 1rem;
228
+ animation: fadeInUp 0.3s ease;
229
+ }
230
+
231
+ .chat-message.user {
232
+ justify-content: flex-end;
233
+ }
234
+
235
+ .chat-message.assistant {
236
+ justify-content: flex-start;
237
+ }
238
+
239
+ .chat-bubble {
240
+ max-width: 70%;
241
+ padding: 1rem;
242
+ border-radius: var(--border-radius);
243
+ word-wrap: break-word;
244
+ position: relative;
245
+ }
246
+
247
+ .chat-message.user .chat-bubble {
248
+ background: linear-gradient(135deg, var(--highlight-color), #ff6b6b);
249
+ color: white;
250
+ border-bottom-right-radius: 4px;
251
+ }
252
+
253
+ .chat-message.assistant .chat-bubble {
254
+ background: var(--bg-tertiary);
255
+ color: var(--text-primary);
256
+ border-bottom-left-radius: 4px;
257
+ }
258
+
259
+ .loading {
260
+ display: inline-block;
261
+ position: relative;
262
+ width: 20px;
263
+ height: 20px;
264
+ }
265
+
266
+ .loading div {
267
+ position: absolute;
268
+ top: 8px;
269
+ width: 6px;
270
+ height: 6px;
271
+ border-radius: 50%;
272
+ background: var(--text-secondary);
273
+ animation-timing-function: cubic-bezier(0, 1, 1, 0);
274
+ }
275
+
276
+ .loading div:nth-child(1) {
277
+ left: 0;
278
+ animation: loading1 0.6s infinite;
279
+ }
280
+
281
+ .loading div:nth-child(2) {
282
+ left: 6px;
283
+ animation: loading2 0.6s infinite;
284
+ }
285
+
286
+ .loading div:nth-child(3) {
287
+ left: 12px;
288
+ animation: loading2 0.6s infinite;
289
+ }
290
+
291
+ .loading div:nth-child(4) {
292
+ left: 18px;
293
+ animation: loading3 0.6s infinite;
294
+ }
295
+
296
+ @keyframes loading1 {
297
+ 0% { transform: scale(0); }
298
+ 100% { transform: scale(1); }
299
+ }
300
+
301
+ @keyframes loading3 {
302
+ 0% { transform: scale(1); }
303
+ 100% { transform: scale(0); }
304
+ }
305
+
306
+ @keyframes loading2 {
307
+ 0% { transform: translate(0, 0); }
308
+ 100% { transform: translate(6px, 0); }
309
+ }
310
+
311
+ @keyframes fadeInDown {
312
+ from {
313
+ opacity: 0;
314
+ transform: translateY(-20px);
315
+ }
316
+ to {
317
+ opacity: 1;
318
+ transform: translateY(0);
319
+ }
320
+ }
321
+
322
+ @keyframes fadeInUp {
323
+ from {
324
+ opacity: 0;
325
+ transform: translateY(20px);
326
+ }
327
+ to {
328
+ opacity: 1;
329
+ transform: translateY(0);
330
+ }
331
+ }
332
+
333
+ .grid {
334
+ display: grid;
335
+ grid-template-columns: 1fr 1fr;
336
+ gap: 1rem;
337
+ }
338
+
339
+ .alert {
340
+ padding: 1rem;
341
+ border-radius: var(--border-radius);
342
+ margin-bottom: 1rem;
343
+ animation: fadeInUp 0.3s ease;
344
+ }
345
+
346
+ .alert-info {
347
+ background: rgba(59, 130, 246, 0.1);
348
+ border: 1px solid rgba(59, 130, 246, 0.3);
349
+ color: #93c5fd;
350
+ }
351
+
352
+ .alert-warning {
353
+ background: rgba(245, 158, 11, 0.1);
354
+ border: 1px solid rgba(245, 158, 11, 0.3);
355
+ color: #fbbf24;
356
+ }
357
+
358
+ .alert-error {
359
+ background: rgba(239, 68, 68, 0.1);
360
+ border: 1px solid rgba(239, 68, 68, 0.3);
361
+ color: #f87171;
362
+ }
363
+
364
+ .file-list {
365
+ margin-top: 1rem;
366
+ }
367
+
368
+ .file-item {
369
+ display: flex;
370
+ align-items: center;
371
+ justify-content: space-between;
372
+ padding: 0.5rem 1rem;
373
+ background: var(--bg-tertiary);
374
+ border-radius: var(--border-radius);
375
+ margin-bottom: 0.5rem;
376
+ animation: fadeInUp 0.3s ease;
377
+ }
378
+
379
+ .file-name {
380
+ display: flex;
381
+ align-items: center;
382
+ gap: 0.5rem;
383
+ color: var(--text-primary);
384
+ }
385
+
386
+ .file-icon {
387
+ color: var(--highlight-color);
388
+ }
389
+
390
+ .remove-file {
391
+ background: none;
392
+ border: none;
393
+ color: var(--text-secondary);
394
+ cursor: pointer;
395
+ padding: 0.25rem;
396
+ transition: var(--transition);
397
+ }
398
+
399
+ .remove-file:hover {
400
+ color: var(--error-color);
401
+ transform: scale(1.1);
402
+ }
403
+
404
+ .references {
405
+ margin-top: 1rem;
406
+ padding: 1rem;
407
+ background: var(--bg-tertiary);
408
+ border-radius: var(--border-radius);
409
+ border-left: 3px solid var(--highlight-color);
410
+ }
411
+
412
+ .references summary {
413
+ cursor: pointer;
414
+ font-weight: 600;
415
+ color: var(--highlight-color);
416
+ }
417
+
418
+ .references details {
419
+ margin-top: 0.5rem;
420
+ }
421
+
422
+ @media (max-width: 768px) {
423
+ .container {
424
+ padding: 1rem;
425
+ }
426
+
427
+ .header h1 {
428
+ font-size: 2rem;
429
+ }
430
+
431
+ .grid {
432
+ grid-template-columns: 1fr;
433
+ }
434
+
435
+ .chat-bubble {
436
+ max-width: 85%;
437
+ }
438
+ }
439
+
440
+ .spinner {
441
+ display: inline-block;
442
+ width: 20px;
443
+ height: 20px;
444
+ border: 3px solid rgba(233, 69, 96, 0.3);
445
+ border-radius: 50%;
446
+ border-top-color: var(--highlight-color);
447
+ animation: spin 1s ease-in-out infinite;
448
+ }
449
+
450
+ @keyframes spin {
451
+ to { transform: rotate(360deg); }
452
+ }
453
+ </style>
454
+ </head>
455
+ <body>
456
+ <div class="container">
457
+ <div class="header">
458
+ <h1><i class="fas fa-file-alt"></i> Document Parser</h1>
459
+ <p>Upload documents and chat with GPT-4 powered AI</p>
460
+ </div>
461
+
462
+ <div class="card">
463
+ <div class="input-group">
464
+ <label class="input-label" for="apiKey">
465
+ <i class="fas fa-key"></i> OpenAI API Key
466
+ </label>
467
+ <input type="password" id="apiKey" class="input-field" placeholder="Enter your OpenAI API key">
468
+ </div>
469
+
470
+ <div class="slider-container">
471
+ <label class="input-label">
472
+ <i class="fas fa-thermometer-half"></i> Model Temperature
473
+ <span id="tempValue">0.0</span>
474
+ </label>
475
+ <input type="range" id="temperature" class="slider" min="0" max="1" step="0.1" value="0">
476
+ </div>
477
+
478
+ <div class="input-group">
479
+ <label class="input-label">
480
+ <i class="fas fa-cloud-upload-alt"></i> Upload Documents
481
+ </label>
482
+ <div class="file-upload">
483
+ <label class="file-upload-label" for="fileInput">
484
+ <div>
485
+ <i class="fas fa-cloud-upload-alt upload-icon"></i>
486
+ <p>Click to upload or drag files here</p>
487
+ <small>Supported formats: PDF, DOCX, DOC, TXT, RTF, HTML</small>
488
+ </div>
489
+ </label>
490
+ <input type="file" id="fileInput" class="file-upload-input" multiple accept=".pdf,.docx,.doc,.txt,.rtf,.html">
491
+ </div>
492
+ <div id="fileList" class="file-list"></div>
493
+ </div>
494
+
495
+ <div style="text-align: right; margin-top: 1rem;">
496
+ <button class="btn btn-danger" onclick="clearConversation()">
497
+ <i class="fas fa-trash"></i> Clear History
498
+ </button>
499
+ </div>
500
+ </div>
501
+
502
+ <div id="chatSection" style="display: none;">
503
+ <div class="card">
504
+ <div class="input-group">
505
+ <label class="input-label">
506
+ <i class="fas fa-question-circle"></i> Ask a question
507
+ </label>
508
+ <div style="display: flex; gap: 0.5rem;">
509
+ <input type="text" id="questionInput" class="input-field" placeholder="Ask about your documents...">
510
+ <button class="btn" onclick="askQuestion()">
511
+ <i class="fas fa-paper-plane"></i>
512
+ </button>
513
+ </div>
514
+ </div>
515
+
516
+ <div id="chatContainer" class="chat-container">
517
+ <div id="chatMessages"></div>
518
+ </div>
519
+
520
+ <div class="grid">
521
+ <button class="btn btn-secondary" onclick="downloadConversation('txt')">
522
+ <i class="fas fa-download"></i> Download TXT
523
+ </button>
524
+ <button class="btn btn-secondary" onclick="downloadConversation('json')">
525
+ <i class="fas fa-download"></i> Download JSON
526
+ </button>
527
+ </div>
528
+ </div>
529
+ </div>
530
+ </div>
531
+
532
+ <script>
533
+ let uploadedFiles = [];
534
+ let conversationHistory = [];
535
+ let isProcessing = false;
536
+
537
+ // Temperature slider
538
+ const tempSlider = document.getElementById('temperature');
539
+ const tempValue = document.getElementById('tempValue');
540
+ tempSlider.addEventListener('input', (e) => {
541
+ tempValue.textContent = e.target.value;
542
+ });
543
+
544
+ // File upload handling
545
+ const fileInput = document.getElementById('fileInput');
546
+ const fileList = document.getElementById('fileList');
547
+
548
+ fileInput.addEventListener('change', (e) => {
549
+ const files = Array.from(e.target.files);
550
+ uploadedFiles = [...uploadedFiles, ...files];
551
+ renderFileList();
552
+ document.getElementById('chatSection').style.display = 'block';
553
+ });
554
+
555
+ function renderFileList() {
556
+ fileList.innerHTML = '';
557
+ uploadedFiles.forEach((file, index) => {
558
+ const fileItem = document.createElement('div');
559
+ fileItem.className = 'file-item';
560
+ fileItem.innerHTML = `
561
+ <div class="file-name">
562
+ <i class="fas fa-file file-icon"></i>
563
+ <span>${file.name}</span>
564
+ </div>
565
+ <button class="remove-file" onclick="removeFile(${index})">
566
+ <i class="fas fa-times"></i>
567
+ </button>
568
+ `;
569
+ fileList.appendChild(fileItem);
570
+ });
571
+ }
572
+
573
+ function removeFile(index) {
574
+ uploadedFiles.splice(index, 1);
575
+ renderFileList();
576
+ if (uploadedFiles.length === 0) {
577
+ document.getElementById('chatSection').style.display = 'none';
578
+ }
579
+ }
580
+
581
+ async function askQuestion() {
582
+ const apiKey = document.getElementById('apiKey').value;
583
+ const question = document.getElementById('questionInput').value;
584
+ const temperature = parseFloat(document.getElementById('temperature').value);
585
+
586
+ if (!apiKey) {
587
+ showAlert('Please enter your OpenAI API key', 'error');
588
+ return;
589
+ }
590
+
591
+ if (!question.trim()) {
592
+ showAlert('Please enter a question', 'warning');
593
+ return;
594
+ }
595
+
596
+ if (uploadedFiles.length === 0) {
597
+ showAlert('Please upload some documents first', 'warning');
598
+ return;
599
+ }
600
+
601
+ if (isProcessing) return;
602
+
603
+ isProcessing = true;
604
+ addMessage('user', question);
605
+ document.getElementById('questionInput').value = '';
606
+
607
+ const formData = new FormData();
608
+ formData.append('apiKey', apiKey);
609
+ formData.append('question', question);
610
+ formData.append('temperature', temperature);
611
+ uploadedFiles.forEach(file => {
612
+ formData.append('documents', file);
613
+ });
614
+
615
+ addMessage('assistant', '<div class="spinner"></div> Processing...');
616
+
617
+ try {
618
+ // Simulate processing delay
619
+ await new Promise(resolve => setTimeout(resolve, 2000));
620
+
621
+ // Mock response
622
+ const mockResponse = {
623
+ answer: `Based on the uploaded documents, here's what I found: ${question}`,
624
+ sources: [
625
+ { page: 1, text: "This is a preview of the source text..." }
626
+ ]
627
+ };
628
+
629
+ updateLastMessage(mockResponse.answer);
630
+ } catch (error) {
631
+ updateLastMessage('Error processing your request. Please try again.');
632
+ showAlert('Error processing request', 'error');
633
+ }
634
+
635
+ isProcessing = false;
636
+ }
637
+
638
+ function addMessage(role, content) {
639
+ const message = { role, content };
640
+ conversationHistory.push(message);
641
+
642
+ const chatMessages = document.getElementById('chatMessages');
643
+ const messageDiv = document.createElement('div');
644
+ messageDiv.className = `chat-message ${role}`;
645
+ messageDiv.innerHTML = `
646
+ <div class="chat-bubble">
647
+ ${content}
648
+ </div>
649
+ `;
650
+ chatMessages.appendChild(messageDiv);
651
+ chatMessages.scrollTop = chatMessages.scrollHeight;
652
+ }
653
+
654
+ function updateLastMessage(content) {
655
+ const chatMessages = document.getElementById('chatMessages');
656
+ const lastMessage = chatMessages.lastElementChild;
657
+ if (lastMessage) {
658
+ lastMessage.querySelector('.chat-bubble').innerHTML = content;
659
+ }
660
+
661
+ if (conversationHistory.length > 0) {
662
+ conversationHistory[conversationHistory.length - 1].content = content;
663
+ }
664
+ }
665
+
666
+ function clearConversation() {
667
+ conversationHistory = [];
668
+ document.getElementById('chatMessages').innerHTML = '';
669
+ document.getElementById('questionInput').value = '';
670
+ }
671
+
672
+ function downloadConversation(format) {
673
+ let content, filename, mimeType;
674
+
675
+ if (format === 'txt') {
676
+ content = conversationHistory.map(msg => `${msg.role.toUpperCase()}:\n${msg.content}\n`).join('\n');
677
+ filename = 'conversation.txt';
678
+ mimeType = 'text/plain';
679
+ } else {
680
+ content = JSON.stringify(conversationHistory, null, 2);
681
+ filename = 'conversation.json';
682
+ mimeType = 'application/json';
683
+ }
684
+
685
+ const blob = new Blob([content], { type: mimeType });
686
+ const url = URL.createObjectURL(blob);
687
+ const a = document.createElement('a');
688
+ a.href = url;
689
+ a.download = filename;
690
+ a.click();
691
+ URL.revokeObjectURL(url);
692
+ }
693
+
694
+ function showAlert(message, type = 'info') {
695
+ const alertDiv = document.createElement('div');
696
+ alertDiv.className = `alert alert-${type}`;
697
+ alertDiv.textContent = message;
698
+ document.body.insertBefore(alertDiv, document.body.firstChild);
699
+
700
+ setTimeout(() => {
701
+ alertDiv.remove();
702
+ }, 5000);
703
+ }
704
+
705
+ // Drag and drop functionality
706
+ const uploadLabel = document.querySelector('.file-upload-label');
707
+
708
+ ['dragenter', 'dragover', 'dragleave', 'drop'].forEach(eventName => {
709
+ uploadLabel.addEventListener(eventName, preventDefaults, false);
710
+ });
711
+
712
+ function preventDefaults(e) {
713
+ e.preventDefault();
714
+ e.stopPropagation();
715
+ }
716
+
717
+ ['dragenter', 'dragover'].forEach(eventName => {
718
+ uploadLabel.addEventListener(eventName, highlight);
719
+ });
720
+
721
+ ['dragleave', 'drop'].forEach(eventName => {
722
+ uploadLabel.addEventListener(eventName, unhighlight);
723
+ });
724
+
725
+ function highlight() {
726
+ uploadLabel.style.borderColor = '#e94560';
727
+ uploadLabel.style.background = '#111';
728
+ }
729
+
730
+ function unhighlight() {
731
+ uploadLabel.style.borderColor = '#555';
732
+ uploadLabel.style.background = '#2a2a2a';
733
+ }
734
+
735
+ uploadLabel.addEventListener('drop', handleDrop);
736
+
737
+ function handleDrop(e) {
738
+ const files = Array.from(e.dataTransfer.files);
739
+ uploadedFiles = [...uploadedFiles, ...files];
740
+ renderFileList();
741
+ document.getElementById('chatSection').style.display = 'block';
742
+ }
743
+
744
+ // Enter key to send question
745
+ document.getElementById('questionInput').addEventListener('keypress', (e) => {
746
+ if (e.key === 'Enter') {
747
+ askQuestion();
748
+ }
749
+ });
750
+ </script>
751
+ </body>
752
+ </html>