MIragE-Ala commited on
Commit
8973425
·
verified ·
1 Parent(s): 1dd7bfc

Add 2 files

Browse files
Files changed (2) hide show
  1. README.md +7 -5
  2. index.html +1180 -19
README.md CHANGED
@@ -1,10 +1,12 @@
1
  ---
2
- title: Alatest
3
- emoji: 📚
4
- colorFrom: red
5
- colorTo: gray
6
  sdk: static
7
  pinned: false
 
 
8
  ---
9
 
10
- Check out the configuration reference at https://huggingface.co/docs/hub/spaces-config-reference
 
1
  ---
2
+ title: alatest
3
+ emoji: 🐳
4
+ colorFrom: blue
5
+ colorTo: blue
6
  sdk: static
7
  pinned: false
8
+ tags:
9
+ - deepsite
10
  ---
11
 
12
+ Check out the configuration reference at https://huggingface.co/docs/hub/spaces-config-reference
index.html CHANGED
@@ -1,19 +1,1180 @@
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>Token Uncertainty Visualization</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: #4a6fa5;
11
+ --secondary-color: #6c757d;
12
+ --success-color: #28a745;
13
+ --danger-color: #dc3545;
14
+ --warning-color: #ffc107;
15
+ --info-color: #17a2b8;
16
+ --light-bg: #f8f9fa;
17
+ --dark-bg: #343a40;
18
+ --border-radius: 4px;
19
+ --box-shadow: 0 4px 6px rgba(0, 0, 0, 0.1);
20
+ --transition: all 0.3s ease;
21
+ }
22
+ /* Added new color classes for better visualization */
23
+ .color-percentile-0 { background-color: #2ecc71; } /* 0-33%: Green */
24
+ .color-percentile-1 { background-color: #f39c12; } /* 33-66%: Orange */
25
+ .color-percentile-2 { background-color: #e74c3c; } /* 66-100%: Red */
26
+
27
+ /* For probability (higher is better) */
28
+ .color-percentile-prob-0 { background-color: #e74c3c; } /* 0-33%: Red (low probability) */
29
+ .color-percentile-prob-1 { background-color: #f39c12; } /* 33-66%: Orange */
30
+ .color-percentile-prob-2 { background-color: #2ecc71; } /* 66-100%: Green (high probability) */
31
+ body {
32
+ font-family: 'Segoe UI', Tahoma, Geneva, Verdana, sans-serif;
33
+ line-height: 1.6;
34
+ margin: 0;
35
+ padding: 0;
36
+ background-color: #f5f7fa;
37
+ color: #333;
38
+ }
39
+
40
+ .container {
41
+ max-width: 1200px;
42
+ margin: 0 auto;
43
+ padding: 20px;
44
+ }
45
+
46
+ header {
47
+ background-color: var(--primary-color);
48
+ color: white;
49
+ padding: 20px 0;
50
+ text-align: center;
51
+ margin-bottom: 30px;
52
+ border-radius: 0 0 var(--border-radius) var(--border-radius);
53
+ box-shadow: var(--box-shadow);
54
+ }
55
+
56
+ h1 {
57
+ margin: 0;
58
+ font-size: 2.2rem;
59
+ }
60
+
61
+ .subtitle {
62
+ font-size: 1rem;
63
+ opacity: 0.9;
64
+ margin-top: 8px;
65
+ }
66
+
67
+ .card {
68
+ background-color: white;
69
+ border-radius: var(--border-radius);
70
+ box-shadow: var(--box-shadow);
71
+ overflow: hidden;
72
+ margin-bottom: 20px;
73
+ }
74
+
75
+ .card-header {
76
+ padding: 15px 20px;
77
+ background-color: var(--light-bg);
78
+ border-bottom: 1px solid #e9ecef;
79
+ display: flex;
80
+ justify-content: space-between;
81
+ align-items: center;
82
+ }
83
+
84
+ .card-title {
85
+ margin: 0;
86
+ font-size: 1.25rem;
87
+ color: var(--primary-color);
88
+ }
89
+
90
+ .card-body {
91
+ padding: 20px;
92
+ }
93
+
94
+ .controls {
95
+ display: flex;
96
+ gap: 15px;
97
+ margin-bottom: 20px;
98
+ align-items: center;
99
+ flex-wrap: wrap;
100
+ }
101
+
102
+ .control-group {
103
+ display: flex;
104
+ gap: 5px;
105
+ align-items: center;
106
+ }
107
+
108
+ label {
109
+ font-weight: 500;
110
+ font-size: 0.9rem;
111
+ }
112
+
113
+ input, select {
114
+ padding: 8px 12px;
115
+ border: 1px solid #ced4da;
116
+ border-radius: var(--border-radius);
117
+ font-size: 0.9rem;
118
+ }
119
+
120
+ button {
121
+ background-color: var(--primary-color);
122
+ color: white;
123
+ border: none;
124
+ padding: 8px 15px;
125
+ border-radius: var(--border-radius);
126
+ cursor: pointer;
127
+ font-size: 0.9rem;
128
+ transition: var(--transition);
129
+ }
130
+
131
+ button:hover {
132
+ background-color: #3a5a80;
133
+ }
134
+
135
+ .token-container {
136
+ display: flex;
137
+ flex-wrap: wrap;
138
+ gap: 8px;
139
+ margin-bottom: 20px;
140
+ }
141
+
142
+ .token {
143
+ position: relative;
144
+ background-color: #e9ecef;
145
+ padding: 6px 12px;
146
+ border-radius: 20px;
147
+ font-size: 0.9rem;
148
+ cursor: pointer;
149
+ transition: var(--transition);
150
+ border: 1px solid transparent;
151
+ }
152
+
153
+ .token:hover {
154
+ transform: translateY(-2px);
155
+ box-shadow: 0 2px 8px rgba(0,0,0,0.1);
156
+ }
157
+
158
+ .token.active {
159
+ border-color: var(--primary-color);
160
+ background-color: #e3f2fd;
161
+ }
162
+
163
+ .uncertainty-marker {
164
+ position: absolute;
165
+ bottom: -4px;
166
+ left: 0;
167
+ width: 100%;
168
+ height: 3px;
169
+ background-color: var(--danger-color);
170
+ opacity: 0.7;
171
+ transition: var(--transition);
172
+ }
173
+
174
+ .detail-panel {
175
+ display: none;
176
+ background-color: white;
177
+ border-radius: var(--border-radius);
178
+ padding: 20px;
179
+ box-shadow: var(--box-shadow);
180
+ margin-top: 20px;
181
+ }
182
+
183
+ .detail-panel.active {
184
+ display: block;
185
+ animation: fadeIn 0.3s ease;
186
+ }
187
+
188
+ @keyframes fadeIn {
189
+ from { opacity: 0; transform: translateY(10px); }
190
+ to { opacity: 1; transform: translateY(0); }
191
+ }
192
+
193
+ .metric-grid {
194
+ display: grid;
195
+ grid-template-columns: repeat(auto-fill, minmax(200px, 1fr));
196
+ gap: 15px;
197
+ margin-bottom: 20px;
198
+ }
199
+
200
+ .metric-item {
201
+ background-color: var(--light-bg);
202
+ padding: 10px;
203
+ border-radius: var(--border-radius);
204
+ text-align: center;
205
+ }
206
+
207
+ .metric-value {
208
+ font-weight: bold;
209
+ font-size: 1.25rem;
210
+ color: var(--primary-color);
211
+ margin: 5px 0;
212
+ }
213
+
214
+ .metric-label {
215
+ font-size: 0.8rem;
216
+ color: var(--secondary-color);
217
+ }
218
+
219
+ .heatmap-container {
220
+ margin-top: 20px;
221
+ }
222
+
223
+ .heatmap-row {
224
+ display: flex;
225
+ margin-bottom: 5px;
226
+ align-items: center;
227
+ }
228
+
229
+ .heatmap-label {
230
+ width: 120px;
231
+ font-size: 0.8rem;
232
+ margin-right: 10px;
233
+ color: var(--secondary-color);
234
+ }
235
+
236
+ .heatmap-bars {
237
+ flex-grow: 1;
238
+ display: flex;
239
+ }
240
+
241
+ .heatmap-bar {
242
+ height: 20px;
243
+ margin-right: 2px;
244
+ transition: var(--transition);
245
+ position: relative;
246
+ }
247
+
248
+ .heatmap-bar:hover {
249
+ transform: scaleY(1.2);
250
+ }
251
+
252
+ .heatmap-tooltip {
253
+ position: absolute;
254
+ bottom: 100%;
255
+ left: 50%;
256
+ transform: translateX(-50%);
257
+ background-color: var(--dark-bg);
258
+ color: white;
259
+ padding: 5px 10px;
260
+ border-radius: var(--border-radius);
261
+ font-size: 0.7rem;
262
+ white-space: nowrap;
263
+ opacity: 0;
264
+ pointer-events: none;
265
+ transition: var(--transition);
266
+ }
267
+
268
+ .heatmap-bar:hover .heatmap-tooltip {
269
+ opacity: 1;
270
+ bottom: 120%;
271
+ }
272
+
273
+ /* Color scales */
274
+ .color-low {
275
+ background-color: #2ecc71;
276
+ }
277
+
278
+ .color-medium {
279
+ background-color: #f39c12;
280
+ }
281
+
282
+ .color-high {
283
+ background-color: #e74c3c;
284
+ }
285
+
286
+ /* Responsive design */
287
+ @media (max-width: 768px) {
288
+ .metric-grid {
289
+ grid-template-columns: 1fr 1fr;
290
+ }
291
+
292
+ .controls {
293
+ flex-direction: column;
294
+ align-items: flex-start;
295
+ }
296
+ }
297
+
298
+ @media (max-width: 576px) {
299
+ .metric-grid {
300
+ grid-template-columns: 1fr;
301
+ }
302
+ }
303
+
304
+ /* Tabs */
305
+ .tabs {
306
+ display: flex;
307
+ border-bottom: 1px solid #dee2e6;
308
+ margin-bottom: 15px;
309
+ overflow-x: auto;
310
+ }
311
+
312
+ .tab {
313
+ padding: 10px 15px;
314
+ cursor: pointer;
315
+ border: 1px solid transparent;
316
+ border-bottom: none;
317
+ border-radius: 4px 4px 0 0;
318
+ margin-right: 5px;
319
+ background-color: transparent;
320
+ transition: var(--transition);
321
+ flex-shrink: 0;
322
+ }
323
+
324
+ .tab:hover {
325
+ background-color: #f8f9fa;
326
+ }
327
+
328
+ .tab.active {
329
+ background-color: white;
330
+ border-color: #dee2e6 #dee2e6 white;
331
+ color: var(--primary-color);
332
+ font-weight: bold;
333
+ }
334
+
335
+ .chart-container {
336
+ height: 300px;
337
+ margin-top: 20px;
338
+ }
339
+
340
+ /* Token info panel */
341
+ .token-info {
342
+ background-color: white;
343
+ border-radius: var(--border-radius);
344
+ padding: 15px;
345
+ box-shadow: var(--box-shadow);
346
+ margin-top: 15px;
347
+ }
348
+
349
+ .token-info-header {
350
+ display: flex;
351
+ justify-content: space-between;
352
+ align-items: center;
353
+ margin-bottom: 10px;
354
+ }
355
+
356
+ .token-info-title {
357
+ font-size: 1rem;
358
+ margin: 0;
359
+ color: var(--primary-color);
360
+ }
361
+
362
+ .token-info-content {
363
+ font-size: 0.9rem;
364
+ line-height: 1.5;
365
+ }
366
+
367
+ /* Legend */
368
+ .legend {
369
+ display: flex;
370
+ justify-content: center;
371
+ margin: 15px 0;
372
+ flex-wrap: wrap;
373
+ gap: 15px;
374
+ }
375
+
376
+ .legend-item {
377
+ display: flex;
378
+ align-items: center;
379
+ font-size: 0.8rem;
380
+ }
381
+
382
+ .legend-color {
383
+ width: 16px;
384
+ height: 16px;
385
+ border-radius: 3px;
386
+ margin-right: 5px;
387
+ }
388
+
389
+ .toggle-btn {
390
+ background-color: transparent;
391
+ color: var(--secondary-color);
392
+ border: 1px solid #ced4da;
393
+ padding: 5px 10px;
394
+ font-size: 0.8rem;
395
+ }
396
+
397
+ .toggle-btn.active {
398
+ background-color: var(--primary-color);
399
+ color: white;
400
+ border-color: var(--primary-color);
401
+ }
402
+
403
+ .file-input-wrapper {
404
+ position: relative;
405
+ overflow: hidden;
406
+ display: inline-block;
407
+ }
408
+
409
+ .file-input-wrapper input[type="file"] {
410
+ font-size: 100px;
411
+ position: absolute;
412
+ left: 0;
413
+ top: 0;
414
+ opacity: 0;
415
+ }
416
+
417
+ .loading {
418
+ display: none;
419
+ text-align: center;
420
+ padding: 20px;
421
+ }
422
+
423
+ .loading.active {
424
+ display: block;
425
+ }
426
+
427
+ .spinner {
428
+ border: 4px solid rgba(0, 0, 0, 0.1);
429
+ border-radius: 50%;
430
+ border-top: 4px solid var(--primary-color);
431
+ width: 30px;
432
+ height: 30px;
433
+ animation: spin 1s linear infinite;
434
+ margin: 0 auto 10px;
435
+ }
436
+
437
+ @keyframes spin {
438
+ 0% { transform: rotate(0deg); }
439
+ 100% { transform: rotate(360deg); }
440
+ }
441
+
442
+ .error-message {
443
+ color: var(--danger-color);
444
+ background-color: #f8d7da;
445
+ padding: 10px;
446
+ border-radius: var(--border-radius);
447
+ margin: 10px 0;
448
+ display: none;
449
+ }
450
+
451
+ .file-info {
452
+ font-size: 0.8rem;
453
+ color: var(--secondary-color);
454
+ margin-left: 10px;
455
+ }
456
+
457
+ /* Range slider */
458
+ .range-slider {
459
+ width: 200px;
460
+ margin: 0 10px;
461
+ }
462
+
463
+ .range-value {
464
+ min-width: 30px;
465
+ text-align: center;
466
+ }
467
+
468
+ /* Progress bar */
469
+ .progress-container {
470
+ width: 100%;
471
+ background-color: #e9ecef;
472
+ border-radius: 4px;
473
+ margin: 10px 0;
474
+ }
475
+
476
+ .progress-bar {
477
+ height: 20px;
478
+ background-color: var(--primary-color);
479
+ border-radius: 4px;
480
+ width: 0%;
481
+ transition: width 0.3s ease;
482
+ }
483
+ </style>
484
+ </head>
485
+ <body>
486
+ <header>
487
+ <div class="container">
488
+ <h1>Token Uncertainty Visualization</h1>
489
+ <p class="subtitle">Analyze model confidence for JSONL files with question/answer data</p>
490
+ </div>
491
+ </header>
492
+
493
+ <div class="container">
494
+ <div class="card">
495
+ <div class="card-header">
496
+ <h2 class="card-title">Data Selection</h2>
497
+ <div class="legend">
498
+ <div class="legend-item">
499
+ <div class="legend-color color-low"></div>
500
+ <span>Low Uncertainty</span>
501
+ </div>
502
+ <div class="legend-item">
503
+ <div class="legend-color color-medium"></div>
504
+ <span>Medium Uncertainty</span>
505
+ </div>
506
+ <div class="legend-item">
507
+ <div class="legend-color color-high"></div>
508
+ <span>High Uncertainty</span>
509
+ </div>
510
+ </div>
511
+ </div>
512
+ <div class="card-body">
513
+ <div class="controls">
514
+ <div class="control-group">
515
+ <div class="file-input-wrapper">
516
+ <button type="button" id="uploadBtn">
517
+ <i class="fas fa-upload"></i> Upload JSONL File
518
+ </button>
519
+ <input type="file" id="fileInput" accept=".jsonl,.json">
520
+ </div>
521
+ <span class="file-info" id="fileInfo">No file selected</span>
522
+ </div>
523
+
524
+ <div class="control-group">
525
+ <label for="questionId">Question ID:</label>
526
+ <input type="number" id="questionId" min="0" value="0">
527
+ <button id="loadQuestionBtn">Load</button>
528
+ </div>
529
+
530
+ <div class="control-group">
531
+ <label for="autoToggle">Auto Load:</label>
532
+ <button id="autoToggle" class="toggle-btn">Off</button>
533
+ </div>
534
+
535
+ <div class="control-group">
536
+ <label for="heatmapType">Heatmap:</label>
537
+ <select id="heatmapType">
538
+ <option value="prob">Probability</option>
539
+ <option value="entropy">Entropy</option>
540
+ <option value="au">Aleatoric Uncertainty</option>
541
+ <option value="eu">Epistemic Uncertainty</option>
542
+ </select>
543
+ <button id="updateHeatmapBtn">Update</button>
544
+ </div>
545
+ </div>
546
+
547
+ <div class="progress-container" id="fileProgressContainer">
548
+ <div class="progress-bar" id="fileProgressBar"></div>
549
+ </div>
550
+
551
+ <div class="error-message" id="errorMessage"></div>
552
+
553
+ <div class="loading" id="loadingIndicator">
554
+ <div class="spinner"></div>
555
+ <p>Processing data...</p>
556
+ </div>
557
+
558
+ <div class="token-container" id="tokenContainer">
559
+ <!-- Tokens will be inserted here by JavaScript -->
560
+ </div>
561
+
562
+ <div class="detail-panel" id="detailPanel">
563
+ <div class="tabs">
564
+ <button class="tab active" data-tab="metrics">Metrics</button>
565
+ <button class="tab" data-tab="probability">Probability</button>
566
+ <button class="tab" data-tab="entropy">Entropy</button>
567
+ <button class="tab" data-tab="au">Aleatoric Uncertainty</button>
568
+ <button class="tab" data-tab="eu">Epistemic Uncertainty</button>
569
+ <button class="tab" data-tab="logits">Top Logits</button>
570
+ <button class="tab" data-tab="rawData">Raw Data</button>
571
+ </div>
572
+
573
+ <div id="metricsTab" class="tab-content active">
574
+ <div class="metric-grid">
575
+ <div class="metric-item">
576
+ <div class="metric-label">Probability</div>
577
+ <div class="metric-value" id="probValue">0.00</div>
578
+ <div class="metric-note">Confidence score (0-1)</div>
579
+ </div>
580
+ <div class="metric-item">
581
+ <div class="metric-label">Entropy</div>
582
+ <div class="metric-value" id="entropyValue">0.00</div>
583
+ <div class="metric-note">Uncertainty measure</div>
584
+ </div>
585
+ <div class="metric-item">
586
+ <div class="metric-label">AU</div>
587
+ <div class="metric-value" id="auValue">0.00</div>
588
+ <div class="metric-note">Aleatoric Uncertainty</div>
589
+ </div>
590
+ <div class="metric-item">
591
+ <div class="metric-label">EU</div>
592
+ <div class="metric-value" id="euValue">0.00</div>
593
+ <div class="metric-note">Epistemic Uncertainty</div>
594
+ </div>
595
+ </div>
596
+
597
+ <div class="heatmap-container">
598
+ <h4>Uncertainty Heatmap</h4>
599
+ <div class="heatmap-row">
600
+ <div class="heatmap-label" id="currentHeatmapLabel">Probability</div>
601
+ <div class="heatmap-bars" id="currentHeatmap"></div>
602
+ </div>
603
+ </div>
604
+ </div>
605
+
606
+ <div id="probabilityTab" class="tab-content">
607
+ <div class="token-info">
608
+ <div class="token-info-header">
609
+ <h3 class="token-info-title">Probability Distribution</h3>
610
+ </div>
611
+ <div class="token-info-content">
612
+ <p>The model predicted this token with a probability of <strong id="probValue2">0.00</strong>.</p>
613
+ <p>Higher probability values indicate greater confidence in the predicted token.</p>
614
+ <div class="heatmap-container">
615
+ <div class="heatmap-row">
616
+ <div class="heatmap-label">Probability</div>
617
+ <div class="heatmap-bars" id="probHeatmap"></div>
618
+ </div>
619
+ </div>
620
+ </div>
621
+ </div>
622
+ </div>
623
+
624
+ <div id="entropyTab" class="tab-content">
625
+ <div class="token-info">
626
+ <div class="token-info-header">
627
+ <h3 class="token-info-title">Entropy Analysis</h3>
628
+ </div>
629
+ <div class="token-info-content">
630
+ <p>This token has an entropy value of <strong id="entropyValue2">0.00</strong>.</p>
631
+ <p>Higher entropy values indicate greater uncertainty in the prediction.</p>
632
+ <div class="heatmap-container">
633
+ <div class="heatmap-row">
634
+ <div class="heatmap-label">Entropy</div>
635
+ <div class="heatmap-bars" id="entropyHeatmap"></div>
636
+ </div>
637
+ </div>
638
+ </div>
639
+ </div>
640
+ </div>
641
+
642
+ <div id="auTab" class="tab-content">
643
+ <div class="token-info">
644
+ <div class="token-info-header">
645
+ <h3 class="token-info-title">Aleatoric Uncertainty</h3>
646
+ </div>
647
+ <div class="token-info-content">
648
+ <p>This token has an Aleatoric Uncertainty (AU) value of <strong id="auValue2">0.00</strong>.</p>
649
+ <p>AU represents the inherent noise in the data that cannot be reduced.</p>
650
+ <div class="heatmap-container">
651
+ <div class="heatmap-row">
652
+ <div class="heatmap-label">Aleatoric Uncertainty</div>
653
+ <div class="heatmap-bars" id="auHeatmap"></div>
654
+ </div>
655
+ </div>
656
+ </div>
657
+ </div>
658
+ </div>
659
+
660
+ <div id="euTab" class="tab-content">
661
+ <div class="token-info">
662
+ <div class="token-info-header">
663
+ <h3 class="token-info-title">Epistemic Uncertainty</h3>
664
+ </div>
665
+ <div class="token-info-content">
666
+ <p>This token has an Epistemic Uncertainty (EU) value of <strong id="euValue2">0.00</strong>.</p>
667
+ <p>EU represents uncertainty due to limited knowledge and can be reduced with more data.</p>
668
+ <div class="heatmap-container">
669
+ <div class="heatmap-row">
670
+ <div class="heatmap-label">Epistemic Uncertainty</div>
671
+ <div class="heatmap-bars" id="euHeatmap"></div>
672
+ </div>
673
+ </div>
674
+ </div>
675
+ </div>
676
+ </div>
677
+
678
+ <div id="logitsTab" class="tab-content">
679
+ <div class="token-info">
680
+ <div class="token-info-header">
681
+ <h3 class="token-info-title">Top Alternative Predictions</h3>
682
+ </div>
683
+ <div class="token-info-content">
684
+ <p>These are the other most likely tokens the model considered (logits):</p>
685
+ <table id="logitsTable" style="width:100%; font-size:0.8rem; border-collapse: collapse;">
686
+ <thead>
687
+ <tr style="background-color: #f8f9fa; border-top: 1px solid #dee2e6; border-bottom: 1px solid #dee2e6;">
688
+ <th style="padding: 8px; text-align: left;">Rank</th>
689
+ <th style="padding: 8px; text-align: left;">Token ID</th>
690
+ <th style="padding: 8px; text-align: left;">Logit Value</th>
691
+ </tr>
692
+ </thead>
693
+ <tbody>
694
+ <!-- Will be populated by JavaScript -->
695
+ </tbody>
696
+ </table>
697
+ </div>
698
+ </div>
699
+ </div>
700
+
701
+ <div id="rawDataTab" class="tab-content">
702
+ <div class="token-info">
703
+ <div class="token-info-header">
704
+ <h3 class="token-info-title">Complete Question Data</h3>
705
+ </div>
706
+ <div class="token-info-content">
707
+ <p>Full JSON data for the selected question:</p>
708
+ <pre id="rawData" style="background-color: #f8f9fa; padding: 10px; border-radius: var(--border-radius); overflow-x: auto; max-height: 300px;"></pre>
709
+ </div>
710
+ </div>
711
+ </div>
712
+ </div>
713
+ </div>
714
+ </div>
715
+ </div>
716
+
717
+ <script>
718
+ document.addEventListener('DOMContentLoaded', function() {
719
+ // DOM elements
720
+ const fileInput = document.getElementById('fileInput');
721
+ const uploadBtn = document.getElementById('uploadBtn');
722
+ const fileInfo = document.getElementById('fileInfo');
723
+ const questionIdInput = document.getElementById('questionId');
724
+ const loadQuestionBtn = document.getElementById('loadQuestionBtn');
725
+ const autoToggle = document.getElementById('autoToggle');
726
+ const heatmapTypeSelect = document.getElementById('heatmapType');
727
+ const updateHeatmapBtn = document.getElementById('updateHeatmapBtn');
728
+ const tokenContainer = document.getElementById('tokenContainer');
729
+ const detailPanel = document.getElementById('detailPanel');
730
+ const loadingIndicator = document.getElementById('loadingIndicator');
731
+ const errorMessage = document.getElementById('errorMessage');
732
+ const fileProgressBar = document.getElementById('fileProgressBar');
733
+ const fileProgressContainer = document.getElementById('fileProgressContainer');
734
+
735
+ // State variables
736
+ let jsonlData = null;
737
+ let currentQuestionIndex = 0;
738
+ let autoLoadEnabled = false;
739
+ let currentHeatmapType = 'prob';
740
+ let questionDataCache = {};
741
+ let sortedRanks = {
742
+ prob: [],
743
+ entropy: [],
744
+ au: [],
745
+ eu: []
746
+ };
747
+
748
+ // Initialize the application
749
+ init();
750
+
751
+ function init() {
752
+ // Event listeners
753
+ fileInput.addEventListener('change', handleFileUpload);
754
+ uploadBtn.addEventListener('click', () => fileInput.click());
755
+ loadQuestionBtn.addEventListener('click', loadCurrentQuestion);
756
+ autoToggle.addEventListener('click', toggleAutoLoad);
757
+ questionIdInput.addEventListener('change', handleQuestionIdChange);
758
+ heatmapTypeSelect.addEventListener('change', updateHeatmapSelection);
759
+ updateHeatmapBtn.addEventListener('click', updateCurrentHeatmap);
760
+
761
+ // Initialize with no progress bar visible
762
+ fileProgressContainer.style.display = 'none';
763
+ }
764
+
765
+ function handleFileUpload(event) {
766
+ const file = event.target.files[0];
767
+ if (!file) return;
768
+
769
+ fileInfo.textContent = file.name;
770
+ loadingIndicator.classList.add('active');
771
+ errorMessage.style.display = 'none';
772
+
773
+ // Show progress bar
774
+ fileProgressContainer.style.display = 'block';
775
+ fileProgressBar.style.width = '0%';
776
+
777
+ let lineCount = 0;
778
+ let processedLines = 0;
779
+ const reader = new FileReader();
780
+
781
+ // First pass to count lines
782
+ const countReader = new FileReader();
783
+ countReader.onload = function(e) {
784
+ const text = e.target.result;
785
+ lineCount = text.split('\n').filter(line => line.trim()).length;
786
+
787
+ // Now process the file
788
+ reader.onload = function(e) {
789
+ try {
790
+ // Parse JSONL file (each line is a separate JSON object)
791
+ const lines = e.target.result.split('\n').filter(line => line.trim());
792
+ jsonlData = [];
793
+
794
+ lines.forEach((line, index) => {
795
+ try {
796
+ const data = JSON.parse(line);
797
+ jsonlData.push(data);
798
+ processedLines++;
799
+
800
+ // Update progress
801
+ const progress = Math.round((processedLines / lineCount) * 100);
802
+ fileProgressBar.style.width = `${progress}%`;
803
+
804
+ // Every 10% or when complete, update the UI
805
+ if (progress % 10 === 0 || progress === 100) {
806
+ setTimeout(() => {
807
+ // Force UI update
808
+ }, 0);
809
+ }
810
+ } catch (parseError) {
811
+ console.error(`Error parsing line ${index}:`, parseError);
812
+ }
813
+ });
814
+
815
+ // Sort data by question_id for easier navigation
816
+ jsonlData.sort((a, b) => a.question_id - b.question_id);
817
+
818
+ // Update the question ID input range
819
+ if (jsonlData.length > 0) {
820
+ const minId = jsonlData[0].question_id;
821
+ const maxId = jsonlData[jsonlData.length - 1].question_id;
822
+ questionIdInput.min = minId;
823
+ questionIdInput.max = maxId;
824
+ questionIdInput.value = minId;
825
+
826
+ // Load the first question
827
+ loadCurrentQuestion();
828
+ }
829
+
830
+ loadingIndicator.classList.remove('active');
831
+ fileProgressContainer.style.display = 'none';
832
+ } catch (error) {
833
+ showError("Error parsing JSONL file. Please check the file format.");
834
+ console.error(error);
835
+ loadingIndicator.classList.remove('active');
836
+ fileProgressContainer.style.display = 'none';
837
+ }
838
+ };
839
+ reader.onerror = function() {
840
+ showError("Error reading file. Please try again.");
841
+ loadingIndicator.classList.remove('active');
842
+ fileProgressContainer.style.display = 'none';
843
+ };
844
+ reader.readAsText(file);
845
+ };
846
+ countReader.readAsText(file);
847
+ }
848
+
849
+ function handleQuestionIdChange() {
850
+ if (autoLoadEnabled) {
851
+ loadCurrentQuestion();
852
+ }
853
+ }
854
+
855
+ function loadCurrentQuestion() {
856
+ if (!jsonlData || jsonlData.length === 0) {
857
+ showError("No data loaded. Please upload a JSONL file first.");
858
+ return;
859
+ }
860
+
861
+ const questionId = parseInt(questionIdInput.value);
862
+ const questionData = jsonlData.find(item => item.question_id === questionId);
863
+
864
+ if (questionData) {
865
+ currentQuestionIndex = questionId;
866
+ renderQuestionData(questionData);
867
+ } else {
868
+ showError(`Question with ID ${questionId} not found in the file.`);
869
+ }
870
+ }
871
+
872
+ function toggleAutoLoad() {
873
+ autoLoadEnabled = !autoLoadEnabled;
874
+ autoToggle.textContent = autoLoadEnabled ? 'On' : 'Off';
875
+ autoToggle.classList.toggle('active', autoLoadEnabled);
876
+ }
877
+
878
+ function updateHeatmapSelection() {
879
+ currentHeatmapType = heatmapTypeSelect.value;
880
+ updateCurrentHeatmap();
881
+ }
882
+
883
+ function updateCurrentHeatmap() {
884
+ if (!questionDataCache || !questionDataCache[currentHeatmapType]) return;
885
+
886
+ document.getElementById('currentHeatmapLabel').textContent =
887
+ currentHeatmapType === 'prob' ? 'Probability' :
888
+ currentHeatmapType === 'entropy' ? 'Entropy' :
889
+ currentHeatmapType === 'au' ? 'Aleatoric Uncertainty' : 'Epistemic Uncertainty';
890
+
891
+ createPercentileHeatmap(
892
+ 'currentHeatmap',
893
+ questionDataCache[currentHeatmapType],
894
+ sortedRanks[currentHeatmapType],
895
+ currentHeatmapType === 'prob' // Reverse for probability (higher is better)
896
+ );
897
+
898
+ // NEW: Update all token markers based on the current heatmap selection
899
+ updateTokenMarkers();
900
+ }
901
+ function updateTokenMarkers() {
902
+ const tokens = document.querySelectorAll('.token');
903
+ tokens.forEach(tokenEl => {
904
+ const index = parseInt(tokenEl.dataset.index);
905
+ const marker = tokenEl.querySelector('.uncertainty-marker');
906
+ if (marker) {
907
+ const percentile = getPercentile(index, currentHeatmapType);
908
+
909
+ // Clear previous color classes
910
+ marker.className = 'uncertainty-marker';
911
+
912
+ // Add appropriate color class based on percentile and metric type
913
+ if (currentHeatmapType === 'prob') {
914
+ // For probability - higher is better (red to green)
915
+ if (percentile < 0.33) marker.classList.add('color-percentile-prob-0');
916
+ else if (percentile < 0.66) marker.classList.add('color-percentile-prob-1');
917
+ else marker.classList.add('color-percentile-prob-2');
918
+ } else {
919
+ // For entropy, AU, EU - higher is worse (green to red)
920
+ if (percentile < 0.33) marker.classList.add('color-percentile-0');
921
+ else if (percentile < 0.66) marker.classList.add('color-percentile-1');
922
+ else marker.classList.add('color-percentile-2');
923
+ }
924
+ }
925
+ });
926
+ }
927
+
928
+ function showError(message) {
929
+ errorMessage.textContent = message;
930
+ errorMessage.style.display = 'block';
931
+ }
932
+
933
+ function renderQuestionData(data) {
934
+ // Cache the current question data
935
+ questionDataCache = data;
936
+
937
+ // Update the raw data display
938
+ document.getElementById('rawData').textContent = JSON.stringify(data, null, 2);
939
+
940
+ // Clear existing tokens
941
+ tokenContainer.innerHTML = '';
942
+
943
+ // Check if the required data fields exist
944
+ if (!data.token_char_list || !data.prob || !data.entropy || !data.au || !data.eu) {
945
+ showError("The selected question doesn't contain the required token analysis data.");
946
+ detailPanel.classList.remove('active');
947
+ return;
948
+ }
949
+
950
+ // Calculate sorted rankings for each metric
951
+ sortedRanks.prob = getSortedRanks(data.prob, false); // Higher is better
952
+ sortedRanks.entropy = getSortedRanks(data.entropy, true); // Higher is worse
953
+ sortedRanks.au = getSortedRanks(data.au, true); // Higher is worse
954
+ sortedRanks.eu = getSortedRanks(data.eu, true); // Higher is worse
955
+
956
+ // Initialize the token display
957
+ data.token_char_list.forEach((token, index) => {
958
+ const tokenEl = document.createElement('div');
959
+ tokenEl.className = 'token';
960
+ tokenEl.textContent = token;
961
+ tokenEl.dataset.index = index;
962
+
963
+ // Add uncertainty marker that will be styled dynamically
964
+ const marker = document.createElement('div');
965
+ marker.className = 'uncertainty-marker';
966
+ tokenEl.appendChild(marker);
967
+
968
+ tokenEl.addEventListener('click', () => showTokenDetails(index));
969
+ tokenContainer.appendChild(tokenEl);
970
+ });
971
+
972
+ // Create all heatmaps
973
+ createPercentileHeatmap('probHeatmap', data.prob, sortedRanks.prob, true);
974
+ createPercentileHeatmap('entropyHeatmap', data.entropy, sortedRanks.entropy, false);
975
+ createPercentileHeatmap('auHeatmap', data.au, sortedRanks.au, false);
976
+ createPercentileHeatmap('euHeatmap', data.eu, sortedRanks.eu, false);
977
+
978
+ // Set current heatmap to show probability by default
979
+ updateCurrentHeatmap();
980
+
981
+ // Show details for first token by default
982
+ if (data.token_char_list.length > 0) {
983
+ showTokenDetails(0);
984
+ } else {
985
+ detailPanel.classList.remove('active');
986
+ }
987
+ updateTokenMarkers();
988
+ }
989
+
990
+ function getSortedRanks(values, higherIsWorse = true) {
991
+ // Create an array of indices and sort them based on values
992
+ const indices = Array.from({length: values.length}, (_, i) => i);
993
+
994
+ // Sort indices based on the values
995
+ indices.sort((a, b) => higherIsWorse ?
996
+ (values[b] - values[a]) : // Higher values come first for entropy/au/eu
997
+ (values[a] - values[b])); // Lower values come first for probability
998
+
999
+ // Create a rank array where rank[i] is the position of the i-th element in the sorted array
1000
+ const ranks = new Array(values.length);
1001
+ indices.forEach((originalIndex, sortedPos) => {
1002
+ ranks[originalIndex] = sortedPos;
1003
+ });
1004
+
1005
+ return ranks;
1006
+ }
1007
+
1008
+ function getPercentile(index, metricType) {
1009
+ if (!sortedRanks[metricType] || sortedRanks[metricType].length <= index) {
1010
+ return 0;
1011
+ }
1012
+
1013
+ // Calculate percentile (0 to 1) based on rank
1014
+ const rank = sortedRanks[metricType][index];
1015
+ const total = sortedRanks[metricType].length;
1016
+ return rank / (total - 1); // Normalize to 0-1 range
1017
+ }
1018
+
1019
+ function showTokenDetails(index) {
1020
+ const data = questionDataCache;
1021
+
1022
+ if (!data || index >= (data.token_char_list?.length || 0)) {
1023
+ return;
1024
+ }
1025
+
1026
+ // Update active token styling
1027
+ document.querySelectorAll('.token').forEach(token => token.classList.remove('active'));
1028
+ const activeToken = document.querySelector(`.token[data-index="${index}"]`);
1029
+ if (activeToken) {
1030
+ activeToken.classList.add('active');
1031
+
1032
+ // Scroll the selection into view if needed
1033
+ activeToken.scrollIntoView({
1034
+ behavior: 'smooth',
1035
+ block: 'nearest'
1036
+ });
1037
+ }
1038
+
1039
+ // Show the detail panel if hidden
1040
+ detailPanel.classList.add('active');
1041
+
1042
+ // Update metrics
1043
+ document.getElementById('probValue').textContent = data.prob[index].toFixed(4);
1044
+ document.getElementById('probValue2').textContent = data.prob[index].toFixed(4);
1045
+ document.getElementById('entropyValue').textContent = data.entropy[index].toFixed(4);
1046
+ document.getElementById('entropyValue2').textContent = data.entropy[index].toFixed(4);
1047
+ document.getElementById('auValue').textContent = data.au[index].toFixed(4);
1048
+ document.getElementById('auValue2').textContent = data.au[index].toFixed(4);
1049
+ document.getElementById('euValue').textContent = data.eu[index].toFixed(4);
1050
+ document.getElementById('euValue2').textContent = data.eu[index].toFixed(4);
1051
+
1052
+ // Highlight the current token in all heatmaps
1053
+ highlightHeatmapToken('probHeatmap', index, sortedRanks.prob, true);
1054
+ highlightHeatmapToken('entropyHeatmap', index, sortedRanks.entropy, false);
1055
+ highlightHeatmapToken('auHeatmap', index, sortedRanks.au, false);
1056
+ highlightHeatmapToken('euHeatmap', index, sortedRanks.eu, false);
1057
+ highlightHeatmapToken('currentHeatmap', index, sortedRanks[currentHeatmapType], currentHeatmapType === 'prob');
1058
+
1059
+ // Update logits table if data exists
1060
+ const logitsTable = document.querySelector('#logitsTable tbody');
1061
+ logitsTable.innerHTML = '';
1062
+
1063
+ if (data.Token_logit_dict && data.Token_logit_dict[index]) {
1064
+ const logits = data.Token_logit_dict[index];
1065
+ const topValues = logits.top_values || [];
1066
+ const topIndices = logits.top_indices || [];
1067
+
1068
+ for (let i = 0; i < topValues.length; i++) {
1069
+ const row = document.createElement('tr');
1070
+ row.style.borderBottom = '1px solid #dee2e6';
1071
+
1072
+ const rankCell = document.createElement('td');
1073
+ rankCell.textContent = i + 1;
1074
+ rankCell.style.padding = '8px';
1075
+
1076
+ const idCell = document.createElement('td');
1077
+ idCell.textContent = topIndices[i] || 'N/A';
1078
+ idCell.style.padding = '8px';
1079
+
1080
+ const valueCell = document.createElement('td');
1081
+ valueCell.textContent = topValues[i] ? topValues[i].toFixed(4) : 'N/A';
1082
+ valueCell.style.padding = '8px';
1083
+
1084
+ row.appendChild(rankCell);
1085
+ row.appendChild(idCell);
1086
+ row.appendChild(valueCell);
1087
+ logitsTable.appendChild(row);
1088
+ }
1089
+ }
1090
+ }
1091
+
1092
+ function createPercentileHeatmap(containerId, values, ranks, reverseColor = false) {
1093
+ const container = document.getElementById(containerId);
1094
+ container.innerHTML = '';
1095
+
1096
+ if (!values || !ranks || values.length !== ranks.length) return;
1097
+
1098
+ for (let i = 0; i < values.length; i++) {
1099
+ const percentile = ranks[i] / (ranks.length - 1); // 0 to 1
1100
+ const color = getUncertaintyColor(percentile, reverseColor);
1101
+ const value = values[i];
1102
+
1103
+ const bar = createHeatmapBar(value, percentile, color, i);
1104
+ container.appendChild(bar);
1105
+ }
1106
+ }
1107
+
1108
+ function highlightHeatmapToken(containerId, index, ranks, reverseColor = false) {
1109
+ const container = document.getElementById(containerId);
1110
+ if (!container || !container.children[index]) return;
1111
+
1112
+ // Reset all bars first
1113
+ Array.from(container.children).forEach(bar => {
1114
+ bar.style.opacity = '1';
1115
+ bar.style.border = 'none';
1116
+ });
1117
+
1118
+ // Highlight the selected bar
1119
+ const bar = container.children[index];
1120
+ bar.style.opacity = '1';
1121
+ bar.style.border = '2px solid var(--primary-color)';
1122
+ bar.style.boxSizing = 'border-box';
1123
+
1124
+ // Scroll to make the bar visible if needed
1125
+ bar.scrollIntoView({
1126
+ behavior: 'smooth',
1127
+ block: 'nearest',
1128
+ inline: 'center'
1129
+ });
1130
+ }
1131
+
1132
+ function createHeatmapBar(value, percentile, color, index) {
1133
+ const bar = document.createElement('div');
1134
+ bar.className = 'heatmap-bar';
1135
+ bar.style.width = '10px';
1136
+ bar.style.height = '20px';
1137
+ bar.style.backgroundColor = color;
1138
+ bar.dataset.index = index;
1139
+
1140
+ const tooltip = document.createElement('div');
1141
+ tooltip.className = 'heatmap-tooltip';
1142
+ tooltip.textContent = `Token ${index}: ${value.toFixed(4)} (Top ${Math.round((1 - percentile) * 100)}%)`;
1143
+ bar.appendChild(tooltip);
1144
+
1145
+ // Make bars clickable to select tokens
1146
+ bar.addEventListener('click', function() {
1147
+ showTokenDetails(index);
1148
+ });
1149
+
1150
+ return bar;
1151
+ }
1152
+
1153
+ function getUncertaintyColor(percentile, higherIsBetter = false) {
1154
+ // If higher is better (like probability), invert the percentile
1155
+ const effectivePercentile = higherIsBetter ? (1 - percentile) : percentile;
1156
+
1157
+ // Returns a color based on the percentile (0-1)
1158
+ if (effectivePercentile < 0.33) return '#2ecc71'; // Green - low uncertainty
1159
+ if (effectivePercentile < 0.66) return '#f39c12'; // Orange - medium uncertainty
1160
+ return '#e74c3c'; // Red - high uncertainty
1161
+ }
1162
+
1163
+ // Tab switching functionality
1164
+ const tabs = document.querySelectorAll('.tab');
1165
+ tabs.forEach(tab => {
1166
+ tab.addEventListener('click', () => {
1167
+ // Remove active class from all tabs and content
1168
+ tabs.forEach(t => t.classList.remove('active'));
1169
+ document.querySelectorAll('.tab-content').forEach(c => c.classList.remove('active'));
1170
+
1171
+ // Add active class to clicked tab and corresponding content
1172
+ tab.classList.add('active');
1173
+ const tabId = tab.dataset.tab + 'Tab';
1174
+ document.getElementById(tabId).classList.add('active');
1175
+ });
1176
+ });
1177
+ });
1178
+ </script>
1179
+ <p style="border-radius: 8px; text-align: center; font-size: 12px; color: #fff; margin-top: 16px;position: absolute; left: 8px; bottom: 8px; z-index: 10; background: rgba(0, 0, 0, 0.8); padding: 4px 8px;">This website has been generated by <a href="https://enzostvs-deepsite.hf.space" style="color: #fff;" target="_blank" >DeepSite</a> <img src="https://enzostvs-deepsite.hf.space/logo.svg" alt="DeepSite Logo" style="width: 16px; height: 16px; vertical-align: middle;"></p></body>
1180
+ </html>