PrernaKu commited on
Commit
1257dba
Β·
verified Β·
1 Parent(s): 033670b

Upload templates/index.html

Browse files
Files changed (1) hide show
  1. templates/index.html +694 -0
templates/index.html ADDED
@@ -0,0 +1,694 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
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>Graph RAG Chatbot</title>
7
+ <script src="https://cdn.jsdelivr.net/npm/chart.js@3.9.1/dist/chart.min.js"></script>
8
+ <style>
9
+ * {
10
+ margin: 0;
11
+ padding: 0;
12
+ box-sizing: border-box;
13
+ }
14
+
15
+ body {
16
+ font-family: 'Segoe UI', Tahoma, Geneva, Verdana, sans-serif;
17
+ background: linear-gradient(135deg, #667eea 0%, #764ba2 100%);
18
+ min-height: 100vh;
19
+ padding: 20px;
20
+ }
21
+
22
+ .container {
23
+ max-width: 1400px;
24
+ margin: 0 auto;
25
+ display: grid;
26
+ grid-template-columns: 1fr 1fr;
27
+ gap: 20px;
28
+ }
29
+
30
+ @media (max-width: 1024px) {
31
+ .container {
32
+ grid-template-columns: 1fr;
33
+ }
34
+ }
35
+
36
+ .card {
37
+ background: white;
38
+ border-radius: 12px;
39
+ box-shadow: 0 10px 30px rgba(0, 0, 0, 0.2);
40
+ padding: 24px;
41
+ display: flex;
42
+ flex-direction: column;
43
+ }
44
+
45
+ .card h2 {
46
+ color: #667eea;
47
+ margin-bottom: 16px;
48
+ font-size: 20px;
49
+ border-bottom: 2px solid #667eea;
50
+ padding-bottom: 10px;
51
+ }
52
+
53
+ .upload-zone {
54
+ border: 2px dashed #667eea;
55
+ border-radius: 8px;
56
+ padding: 40px 20px;
57
+ text-align: center;
58
+ cursor: pointer;
59
+ transition: all 0.3s ease;
60
+ background: #f8f9fa;
61
+ }
62
+
63
+ .upload-zone:hover {
64
+ background: #e8eaf6;
65
+ border-color: #764ba2;
66
+ }
67
+
68
+ .upload-zone.dragover {
69
+ background: #e8eaf6;
70
+ border-color: #764ba2;
71
+ transform: scale(1.02);
72
+ }
73
+
74
+ .upload-zone input {
75
+ display: none;
76
+ }
77
+
78
+ .upload-zone p {
79
+ color: #666;
80
+ margin: 10px 0;
81
+ }
82
+
83
+ .button {
84
+ background: linear-gradient(135deg, #667eea 0%, #764ba2 100%);
85
+ color: white;
86
+ border: none;
87
+ padding: 10px 20px;
88
+ border-radius: 6px;
89
+ cursor: pointer;
90
+ font-weight: 600;
91
+ transition: transform 0.2s, box-shadow 0.2s;
92
+ margin: 5px;
93
+ }
94
+
95
+ .button:hover:not(:disabled) {
96
+ transform: translateY(-2px);
97
+ box-shadow: 0 5px 15px rgba(102, 126, 234, 0.4);
98
+ }
99
+
100
+ .button:disabled {
101
+ opacity: 0.5;
102
+ cursor: not-allowed;
103
+ }
104
+
105
+ .button.secondary {
106
+ background: #f0f0f0;
107
+ color: #333;
108
+ }
109
+
110
+ .documents-list {
111
+ display: flex;
112
+ flex-direction: column;
113
+ gap: 12px;
114
+ max-height: 500px;
115
+ overflow-y: auto;
116
+ }
117
+
118
+ .document-item {
119
+ background: #f8f9fa;
120
+ border-left: 4px solid #667eea;
121
+ padding: 16px;
122
+ border-radius: 6px;
123
+ transition: all 0.3s ease;
124
+ }
125
+
126
+ .document-item:hover {
127
+ transform: translateX(4px);
128
+ box-shadow: 0 4px 12px rgba(0, 0, 0, 0.1);
129
+ }
130
+
131
+ .document-item.processing {
132
+ border-left-color: #ff9800;
133
+ }
134
+
135
+ .document-item.ready {
136
+ border-left-color: #4caf50;
137
+ }
138
+
139
+ .document-item.error {
140
+ border-left-color: #f44336;
141
+ }
142
+
143
+ .doc-name {
144
+ font-weight: 600;
145
+ color: #333;
146
+ margin-bottom: 8px;
147
+ }
148
+
149
+ .doc-status {
150
+ display: flex;
151
+ align-items: center;
152
+ gap: 8px;
153
+ margin-bottom: 8px;
154
+ }
155
+
156
+ .status-badge {
157
+ display: inline-block;
158
+ padding: 4px 12px;
159
+ border-radius: 20px;
160
+ font-size: 12px;
161
+ font-weight: 600;
162
+ }
163
+
164
+ .status-badge.processing {
165
+ background: #fff3cd;
166
+ color: #856404;
167
+ }
168
+
169
+ .status-badge.ready {
170
+ background: #d4edda;
171
+ color: #155724;
172
+ }
173
+
174
+ .status-badge.error {
175
+ background: #f8d7da;
176
+ color: #721c24;
177
+ }
178
+
179
+ .progress-bar {
180
+ width: 100%;
181
+ height: 6px;
182
+ background: #e0e0e0;
183
+ border-radius: 3px;
184
+ overflow: hidden;
185
+ margin-bottom: 8px;
186
+ }
187
+
188
+ .progress-fill {
189
+ height: 100%;
190
+ background: linear-gradient(90deg, #667eea, #764ba2);
191
+ width: 0%;
192
+ transition: width 0.3s ease;
193
+ }
194
+
195
+ .doc-info {
196
+ display: flex;
197
+ gap: 20px;
198
+ font-size: 13px;
199
+ color: #666;
200
+ margin-bottom: 10px;
201
+ }
202
+
203
+ .doc-info span {
204
+ display: flex;
205
+ align-items: center;
206
+ gap: 4px;
207
+ }
208
+
209
+ .doc-actions {
210
+ display: flex;
211
+ gap: 8px;
212
+ }
213
+
214
+ .doc-actions button {
215
+ flex: 1;
216
+ padding: 6px 12px;
217
+ font-size: 12px;
218
+ }
219
+
220
+ .chat-container {
221
+ display: flex;
222
+ flex-direction: column;
223
+ height: 600px;
224
+ }
225
+
226
+ .chat-messages {
227
+ flex: 1;
228
+ overflow-y: auto;
229
+ margin-bottom: 16px;
230
+ padding: 12px;
231
+ background: #f8f9fa;
232
+ border-radius: 8px;
233
+ display: flex;
234
+ flex-direction: column;
235
+ gap: 12px;
236
+ }
237
+
238
+ .message {
239
+ padding: 12px 16px;
240
+ border-radius: 8px;
241
+ word-wrap: break-word;
242
+ max-width: 100%;
243
+ }
244
+
245
+ .message.user {
246
+ background: #667eea;
247
+ color: white;
248
+ align-self: flex-end;
249
+ margin-left: 20%;
250
+ }
251
+
252
+ .message.bot {
253
+ background: white;
254
+ color: #333;
255
+ align-self: flex-start;
256
+ margin-right: 20%;
257
+ border: 1px solid #ddd;
258
+ }
259
+
260
+ .chat-input-area {
261
+ display: flex;
262
+ gap: 8px;
263
+ margin-top: auto;
264
+ }
265
+
266
+ .chat-input-area input {
267
+ flex: 1;
268
+ padding: 12px;
269
+ border: 1px solid #ddd;
270
+ border-radius: 6px;
271
+ font-size: 14px;
272
+ }
273
+
274
+ .chat-input-area input:focus {
275
+ outline: none;
276
+ border-color: #667eea;
277
+ box-shadow: 0 0 0 3px rgba(102, 126, 234, 0.1);
278
+ }
279
+
280
+ select {
281
+ padding: 8px;
282
+ border: 1px solid #ddd;
283
+ border-radius: 6px;
284
+ font-size: 14px;
285
+ }
286
+
287
+ select:focus {
288
+ outline: none;
289
+ border-color: #667eea;
290
+ box-shadow: 0 0 0 3px rgba(102, 126, 234, 0.1);
291
+ }
292
+
293
+ .graph-preview {
294
+ margin-top: 12px;
295
+ text-align: center;
296
+ }
297
+
298
+ .graph-preview img {
299
+ max-width: 100%;
300
+ border-radius: 6px;
301
+ border: 1px solid #ddd;
302
+ margin-top: 8px;
303
+ }
304
+
305
+ .graph-preview a {
306
+ color: #667eea;
307
+ text-decoration: none;
308
+ font-weight: 600;
309
+ display: inline-block;
310
+ margin-top: 8px;
311
+ }
312
+
313
+ .graph-preview a:hover {
314
+ text-decoration: underline;
315
+ }
316
+
317
+ .spinner {
318
+ display: inline-block;
319
+ width: 12px;
320
+ height: 12px;
321
+ border: 2px solid #f3f3f3;
322
+ border-top: 2px solid #667eea;
323
+ border-radius: 50%;
324
+ animation: spin 0.8s linear infinite;
325
+ }
326
+
327
+ @keyframes spin {
328
+ 0% { transform: rotate(0deg); }
329
+ 100% { transform: rotate(360deg); }
330
+ }
331
+
332
+ .stats {
333
+ display: grid;
334
+ grid-template-columns: repeat(2, 1fr);
335
+ gap: 12px;
336
+ margin-top: 12px;
337
+ }
338
+
339
+ .stat-item {
340
+ background: #f8f9fa;
341
+ padding: 12px;
342
+ border-radius: 6px;
343
+ text-align: center;
344
+ }
345
+
346
+ .stat-label {
347
+ font-size: 12px;
348
+ color: #666;
349
+ margin-bottom: 4px;
350
+ }
351
+
352
+ .stat-value {
353
+ font-size: 18px;
354
+ font-weight: 700;
355
+ color: #667eea;
356
+ }
357
+
358
+ .error-text {
359
+ color: #f44336;
360
+ font-size: 12px;
361
+ margin-top: 8px;
362
+ }
363
+
364
+ .tabs {
365
+ display: flex;
366
+ gap: 8px;
367
+ margin-bottom: 16px;
368
+ }
369
+
370
+ .tab-btn {
371
+ padding: 8px 16px;
372
+ border: none;
373
+ background: #f0f0f0;
374
+ color: #666;
375
+ border-radius: 6px 6px 0 0;
376
+ cursor: pointer;
377
+ font-weight: 600;
378
+ transition: all 0.3s ease;
379
+ }
380
+
381
+ .tab-btn.active {
382
+ background: white;
383
+ color: #667eea;
384
+ border-bottom: 3px solid #667eea;
385
+ }
386
+
387
+ .tab-content {
388
+ display: none;
389
+ }
390
+
391
+ .tab-content.active {
392
+ display: block;
393
+ }
394
+
395
+ .empty-state {
396
+ text-align: center;
397
+ color: #999;
398
+ padding: 20px;
399
+ }
400
+
401
+ .loading-skeleton {
402
+ background: linear-gradient(90deg, #f0f0f0 25%, #e0e0e0 50%, #f0f0f0 75%);
403
+ background-size: 200% 100%;
404
+ animation: loading 1.5s infinite;
405
+ height: 20px;
406
+ border-radius: 4px;
407
+ margin-bottom: 10px;
408
+ }
409
+
410
+ @keyframes loading {
411
+ 0% { background-position: 200% 0; }
412
+ 100% { background-position: -200% 0; }
413
+ }
414
+ </style>
415
+ </head>
416
+ <body>
417
+ <div class="container">
418
+ <!-- Upload Section -->
419
+ <div class="card">
420
+ <h2>πŸ“€ Upload Documents</h2>
421
+
422
+ <div class="upload-zone" id="uploadZone">
423
+ <svg style="width: 48px; height: 48px; margin: 0 auto; color: #667eea;" fill="none" stroke="currentColor" viewBox="0 0 24 24">
424
+ <path stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="M7 16a4 4 0 01-.88-7.903A5 5 0 1115.9 6L16 6a5 5 0 011 9.9M15 13l-3-3m0 0l-3 3m3-3v12"/>
425
+ </svg>
426
+ <p><strong>Drop files here or click to upload</strong></p>
427
+ <p style="font-size: 12px;">Supported: PDF, CSV, TXT (max 50MB)</p>
428
+ <input type="file" id="fileInput" multiple accept=".pdf,.csv,.txt">
429
+ </div>
430
+
431
+ <div style="margin-top: 16px;">
432
+ <h3 style="margin-bottom: 12px;">πŸ“š Uploaded Documents</h3>
433
+ <div class="documents-list" id="documentsList">
434
+ <div class="empty-state">No documents uploaded yet</div>
435
+ </div>
436
+ </div>
437
+ </div>
438
+
439
+ <!-- Chat & Graph Section -->
440
+ <div class="card">
441
+ <h2>πŸ’¬ RAG Chatbot</h2>
442
+
443
+ <div class="tabs">
444
+ <button class="tab-btn active" onclick="switchTab('chat')">Chat</button>
445
+ <button class="tab-btn" onclick="switchTab('graph')">Knowledge Graph</button>
446
+ </div>
447
+
448
+ <!-- Chat Tab -->
449
+ <div id="chat" class="tab-content active" style="display: flex; flex-direction: column; flex: 1;">
450
+ <select id="documentSelect" style="margin-bottom: 12px;">
451
+ <option value="">Select a document...</option>
452
+ </select>
453
+
454
+ <div class="chat-messages" id="chatMessages"></div>
455
+
456
+ <div class="chat-input-area">
457
+ <input type="text" id="queryInput" placeholder="Ask a question..." />
458
+ <button class="button" id="sendBtn" onclick="sendQuery()" disabled>Send</button>
459
+ </div>
460
+ </div>
461
+
462
+ <!-- Graph Tab -->
463
+ <div id="graph" class="tab-content" style="flex: 1; overflow-y: auto;">
464
+ <select id="graphDocSelect" style="margin-bottom: 12px; width: 100%;">
465
+ <option value="">Select a document to view graph...</option>
466
+ </select>
467
+ <div id="graphContainer" class="empty-state">
468
+ No graph selected
469
+ </div>
470
+ </div>
471
+ </div>
472
+ </div>
473
+
474
+ <script>
475
+ const API_BASE = '/api';
476
+ let refreshInterval;
477
+ let selectedDoc = '';
478
+ let selectedGraphDoc = '';
479
+
480
+ function switchTab(tab) {
481
+ document.querySelectorAll('.tab-content').forEach(el => el.classList.remove('active'));
482
+ document.querySelectorAll('.tab-btn').forEach(el => el.classList.remove('active'));
483
+
484
+ document.getElementById(tab).classList.add('active');
485
+ event.target.classList.add('active');
486
+
487
+ if (tab === 'graph' && selectedGraphDoc) {
488
+ loadGraphVisualization(selectedGraphDoc);
489
+ }
490
+ }
491
+
492
+ function setupUpload() {
493
+ const zone = document.getElementById('uploadZone');
494
+ const fileInput = document.getElementById('fileInput');
495
+
496
+ zone.addEventListener('click', () => fileInput.click());
497
+
498
+ ['dragenter', 'dragover', 'dragleave', 'drop'].forEach(evt => {
499
+ zone.addEventListener(evt, preventDefaults, false);
500
+ });
501
+
502
+ function preventDefaults(e) {
503
+ e.preventDefault();
504
+ e.stopPropagation();
505
+ }
506
+
507
+ ['dragenter', 'dragover'].forEach(evt => {
508
+ zone.addEventListener(evt, () => zone.classList.add('dragover'), false);
509
+ });
510
+
511
+ ['dragleave', 'drop'].forEach(evt => {
512
+ zone.addEventListener(evt, () => zone.classList.remove('dragover'), false);
513
+ });
514
+
515
+ zone.addEventListener('drop', handleDrop, false);
516
+ fileInput.addEventListener('change', (e) => handleFiles(e.target.files));
517
+ }
518
+
519
+ function handleDrop(e) {
520
+ const files = e.dataTransfer.files;
521
+ handleFiles(files);
522
+ }
523
+
524
+ function handleFiles(files) {
525
+ const formData = new FormData();
526
+ for (let file of files) {
527
+ formData.append('files', file);
528
+ }
529
+
530
+ fetch(`${API_BASE}/upload`, {
531
+ method: 'POST',
532
+ body: formData
533
+ })
534
+ .then(r => r.json())
535
+ .then(data => {
536
+ if (data.success) {
537
+ document.getElementById('fileInput').value = '';
538
+ refreshDocuments();
539
+ }
540
+ })
541
+ .catch(err => console.error('Upload error:', err));
542
+ }
543
+
544
+ function refreshDocuments() {
545
+ fetch(`${API_BASE}/documents`)
546
+ .then(r => r.json())
547
+ .then(data => {
548
+ renderDocuments(data.documents);
549
+ updateSelects(data.documents);
550
+ })
551
+ .catch(err => console.error('Fetch error:', err));
552
+ }
553
+
554
+ function renderDocuments(docs) {
555
+ const container = document.getElementById('documentsList');
556
+
557
+ if (Object.keys(docs).length === 0) {
558
+ container.innerHTML = '<div class="empty-state">No documents uploaded yet</div>';
559
+ return;
560
+ }
561
+
562
+ container.innerHTML = Object.entries(docs).map(([name, doc]) => {
563
+ const status = doc.status || 'queued';
564
+ const progress = doc.progress || 0;
565
+
566
+ return `
567
+ <div class="document-item ${status}">
568
+ <div class="doc-name">${name}</div>
569
+ <div class="doc-status">
570
+ <span class="status-badge ${status}">
571
+ ${status === 'processing' ? '<span class="spinner"></span>' : ''}
572
+ ${status.toUpperCase()}
573
+ </span>
574
+ </div>
575
+ <div class="progress-bar">
576
+ <div class="progress-fill" style="width: ${progress}%"></div>
577
+ </div>
578
+ ${status === 'ready' ? `
579
+ <div class="doc-info">
580
+ <span>πŸ“Š ${doc.chunks || 0} chunks</span>
581
+ <span>πŸ”‘ ${doc.entities || 0} entities</span>
582
+ </div>
583
+ <div class="graph-preview">
584
+ <a href="${doc.graph_image}" target="_blank">πŸ“ˆ View Full Graph</a>
585
+ </div>
586
+ ` : ''}
587
+ ${doc.error ? `<div class="error-text">❌ ${doc.error}</div>` : ''}
588
+ <div class="doc-actions">
589
+ <button class="button secondary" style="flex: 1;" onclick="deleteDocument('${name}')">Delete</button>
590
+ </div>
591
+ </div>
592
+ `;
593
+ }).join('');
594
+ }
595
+
596
+ function updateSelects(docs) {
597
+ const docSelect = document.getElementById('documentSelect');
598
+ const graphDocSelect = document.getElementById('graphDocSelect');
599
+ const readyDocs = Object.entries(docs)
600
+ .filter(([_, doc]) => doc.status === 'ready')
601
+ .map(([name, _]) => name);
602
+
603
+ [docSelect, graphDocSelect].forEach(select => {
604
+ const current = select.value;
605
+ select.innerHTML = '<option value="">Select a document...</option>';
606
+ readyDocs.forEach(name => {
607
+ select.innerHTML += `<option value="${name}">${name}</option>`;
608
+ });
609
+ select.value = current;
610
+ });
611
+
612
+ if (readyDocs.length > 0) {
613
+ document.getElementById('sendBtn').disabled = false;
614
+ }
615
+ }
616
+
617
+ function deleteDocument(name) {
618
+ if (!confirm(`Delete "${name}"?`)) return;
619
+
620
+ fetch(`${API_BASE}/delete/${name}`, { method: 'DELETE' })
621
+ .then(() => refreshDocuments())
622
+ .catch(err => console.error('Delete error:', err));
623
+ }
624
+
625
+ function sendQuery() {
626
+ const query = document.getElementById('queryInput').value.trim();
627
+ const doc = document.getElementById('documentSelect').value;
628
+
629
+ if (!query || !doc) return;
630
+
631
+ const messagesDiv = document.getElementById('chatMessages');
632
+ messagesDiv.innerHTML += `<div class="message user">${query}</div>`;
633
+ document.getElementById('queryInput').value = '';
634
+
635
+ const loadingMsg = document.createElement('div');
636
+ loadingMsg.className = 'message bot';
637
+ loadingMsg.innerHTML = '<span class="spinner"></span> Thinking...';
638
+ messagesDiv.appendChild(loadingMsg);
639
+ messagesDiv.scrollTop = messagesDiv.scrollHeight;
640
+
641
+ fetch(`${API_BASE}/query`, {
642
+ method: 'POST',
643
+ headers: { 'Content-Type': 'application/json' },
644
+ body: JSON.stringify({ query, document: doc })
645
+ })
646
+ .then(r => r.json())
647
+ .then(data => {
648
+ loadingMsg.remove();
649
+ const answer = data.answer || data.error || 'Error processing query';
650
+ messagesDiv.innerHTML += `<div class="message bot">${answer}</div>`;
651
+ messagesDiv.scrollTop = messagesDiv.scrollHeight;
652
+ })
653
+ .catch(err => {
654
+ loadingMsg.remove();
655
+ messagesDiv.innerHTML += `<div class="message bot">Error: ${err.message}</div>`;
656
+ });
657
+ }
658
+
659
+ function loadGraphVisualization(docName) {
660
+ const container = document.getElementById('graphContainer');
661
+ if (!docName) {
662
+ container.innerHTML = '<div class="empty-state">No document selected</div>';
663
+ return;
664
+ }
665
+
666
+ container.innerHTML = `
667
+ <img src="/graph-image/${docName}" alt="Knowledge Graph" style="width: 100%; border-radius: 6px;">
668
+ `;
669
+ }
670
+
671
+ document.getElementById('documentSelect').addEventListener('change', (e) => {
672
+ selectedDoc = e.target.value;
673
+ });
674
+
675
+ document.getElementById('graphDocSelect').addEventListener('change', (e) => {
676
+ selectedGraphDoc = e.target.value;
677
+ loadGraphVisualization(selectedGraphDoc);
678
+ });
679
+
680
+ document.getElementById('queryInput').addEventListener('keypress', (e) => {
681
+ if (e.key === 'Enter' && !e.shiftKey) {
682
+ e.preventDefault();
683
+ sendQuery();
684
+ }
685
+ });
686
+
687
+ document.addEventListener('DOMContentLoaded', () => {
688
+ setupUpload();
689
+ refreshDocuments();
690
+ refreshInterval = setInterval(refreshDocuments, 1500);
691
+ });
692
+ </script>
693
+ </body>
694
+ </html>