elmadany commited on
Commit
2b8ba04
·
verified ·
1 Parent(s): b2aaaec

Update public/index.html

Browse files
Files changed (1) hide show
  1. public/index.html +82 -142
public/index.html CHANGED
@@ -10,7 +10,6 @@
10
  <link rel="preconnect" href="https://fonts.gstatic.com" crossorigin>
11
  <link href="https://fonts.googleapis.com/css2?family=Inter:wght@400;500;600&family=Rubik:wght@500;600;700;800&display=swap" rel="stylesheet">
12
  <link rel="stylesheet" href="https://cdnjs.cloudflare.com/ajax/libs/font-awesome/6.4.0/css/all.min.css">
13
- <script src="https://cdn.jsdelivr.net/npm/chart.js"></script>
14
 
15
  <style>
16
  :root {
@@ -19,7 +18,7 @@
19
  --text-white: #ffffff;
20
  --simba-navy: #0f172a;
21
  --border-gold: #dca02a;
22
- --grad-blue: linear-gradient(90deg, #3b82f6, #0ea5e9);
23
  --grad-red: linear-gradient(135deg, #ef4444, #f43f5e);
24
  --grad-purple: linear-gradient(135deg, #6366f1, #8b5cf6);
25
  --grad-orange: linear-gradient(135deg, #f97316, #fb923c);
@@ -46,49 +45,18 @@
46
  .btn-submit { background: var(--grad-blue); color: white !important; padding: 10px 24px; border-radius: 30px; font-weight: 600; font-size: 14px; box-shadow: 0 4px 12px rgba(59, 130, 246, 0.4); }
47
 
48
  /* --- HERO --- */
49
- .hero {
50
- max-width: 1000px;
51
- margin: 60px auto 80px;
52
- padding: 0 40px;
53
- position: relative;
54
- text-align: center;
55
- }
56
- .hero-content {
57
- z-index: 2;
58
- display: flex;
59
- flex-direction: column;
60
- align-items: center;
61
- }
62
  .conf-badge { background: white; color: black; display: inline-flex; align-items: center; gap: 10px; padding: 6px 16px; border-radius: 8px; margin-bottom: 30px; font-weight: 700; font-size: 14px; }
63
  .conf-badge i { color: #cc0000; font-size: 18px; }
64
  .hero h1 { font-family: 'Rubik', sans-serif; font-size: 64px; font-weight: 800; margin: 0; line-height: 1.1; }
65
  .hero h2 { font-family: 'Rubik', sans-serif; font-size: 32px; color: var(--text-yellow); margin: 15px 0 30px; font-weight: 700; }
66
  .hero p { color: #cbd5e1; font-size: 18px; line-height: 1.6; max-width: 700px; margin-bottom: 50px; }
67
-
68
  .hero-actions { display: flex; gap: 15px; flex-wrap: wrap; justify-content: center; }
 
 
 
69
 
70
- /* Styled Buttons (Converted from React JSX) */
71
- .action-btn {
72
- display: flex; align-items: center; gap: 8px;
73
- padding: 14px 26px; /* py-4 px-6 approx */
74
- border-radius: 9999px; /* rounded-full */
75
- color: white; font-weight: 700; font-size: 15px;
76
- transition: all 0.3s ease;
77
- box-shadow: 0 10px 15px -3px rgba(0, 0, 0, 0.1), 0 4px 6px -2px rgba(0, 0, 0, 0.05); /* shadow-xl */
78
- white-space: nowrap;
79
- }
80
- .action-btn:hover { transform: scale(1.05); } /* hover:scale-105 */
81
-
82
- .btn-red { background: #ef4444; box-shadow: 0 20px 25px -5px rgba(239, 68, 68, 0.2); }
83
- .btn-indigo { background: #6366f1; box-shadow: 0 20px 25px -5px rgba(99, 102, 241, 0.2); }
84
- .btn-orange { background: #f97316; box-shadow: 0 20px 25px -5px rgba(249, 115, 22, 0.2); }
85
- .btn-blue { background: #3b82f6; box-shadow: 0 20px 25px -5px rgba(59, 130, 246, 0.2); }
86
-
87
- .btn-red:hover { background: #dc2626; }
88
- .btn-indigo:hover { background: #4f46e5; }
89
- .btn-orange:hover { background: #ea580c; }
90
- .btn-blue:hover { background: #2563eb; }
91
-
92
  /* --- CONTENT --- */
93
  .content-container { width: 80%; max-width: 1600px; margin: 0 auto; }
94
  .main-card { background: white; border-radius: 24px; padding: 40px; color: #0f172a; min-height: 600px; position: relative; z-index: 10; margin-bottom: 60px; }
@@ -108,25 +76,36 @@
108
  .score-desc { font-size: 14px; color: #475569; background: #fff7ed; border-left: 4px solid #f97316; padding: 10px 15px; margin-bottom: 20px; border-radius: 4px; }
109
 
110
  /* ASR Buttons */
111
- .mode-btn {
112
- padding: 8px 20px; border-radius: 8px; font-weight: 600; font-size: 13px;
113
- cursor: pointer; transition: all 0.2s; border: 1px solid #e2e8f0;
114
- background: white; color: var(--simba-navy);
115
- }
116
  .mode-btn:hover { border-color: var(--text-yellow); color: var(--text-yellow); }
117
  .mode-btn.active { background-color: var(--simba-navy); color: white; border-color: var(--simba-navy); box-shadow: 0 4px 12px rgba(15, 23, 42, 0.15); }
118
 
119
- /* Charts */
120
- .chart-wrapper { width: 100%; max-height: 400px; margin-bottom: 20px; display: none; }
121
- canvas { max-width: 100%; }
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
122
 
123
- /* Summary Cards */
124
- .summary-grid { display: grid; grid-template-columns: repeat(auto-fit, minmax(250px, 1fr)); gap: 20px; margin-bottom: 40px; }
125
- .stat-card { background: #f8fafc; border: 1px solid #e2e8f0; border-radius: 12px; padding: 20px; box-shadow: 0 2px 8px rgba(0,0,0,0.05); text-align: center; transition: 0.3s; }
126
- .stat-card:hover { transform: translateY(-3px); border-color: var(--text-yellow); }
127
- .stat-title { font-size: 12px; font-weight: 700; color: #64748b; text-transform: uppercase; letter-spacing: 1px; margin-bottom: 10px; }
128
- .stat-model { font-size: 18px; font-weight: 700; color: #0f172a; margin-bottom: 5px; }
129
- .stat-val { font-size: 14px; color: var(--text-yellow); font-weight: 600; }
130
 
131
  /* Table */
132
  .table-wrap { overflow-x: auto; border: 1px solid #e2e8f0; border-radius: 12px; margin-bottom: 30px; }
@@ -161,6 +140,7 @@
161
  <div class="nav-text"><span>Voice of a</span> Continent</div>
162
  </a>
163
  <div class="nav-links">
 
164
  <a href="https://africa.dlnlp.ai/simba/" target="_blank" class="btn-submit">Submit New Results</a>
165
  </div>
166
  </nav>
@@ -171,20 +151,9 @@
171
  <h1>SimbaBench Leaderboard</h1>
172
  <h2>Mapping Africa’s Speech Technology</h2>
173
  <p>SimbaBench bridges the digital divide with a unified suite for African AI: the largest open-source speech benchmark covering 61 languages.</p>
174
-
175
  <div class="hero-actions">
176
- <a href="https://aclanthology.org/2025.emnlp-main.559" target="_blank" class="action-btn btn-red">
177
- <i class="fa-regular fa-file-pdf"></i> Read Paper
178
- </a>
179
- <a href="https://github.com/UBC-NLP/simba" target="_blank" class="action-btn btn-indigo">
180
- <i class="fa-solid fa-code-branch"></i> Pipeline
181
- </a>
182
- <a href="https://huggingface.co/datasets/UBC-NLP/simba" target="_blank" class="action-btn btn-orange">
183
- <i class="fa-solid fa-database"></i> Dataset
184
- </a>
185
- <a href="https://africa.dlnlp.ai/simba/#models" target="_blank" class="action-btn btn-blue">
186
- <i class="fa-solid fa-bolt"></i> Models
187
- </a>
188
  </div>
189
  </div>
190
  </div>
@@ -250,23 +219,14 @@
250
 
251
  <div id="lang-summary" class="summary-grid"></div>
252
 
253
- <div class="chart-section">
254
- <h4>Automatic Speech Recognition (ASR) <span class="rank-info" style="font-weight:normal;">(Ranked by WER ↓, then CER ↓)</span></h4>
255
- <div id="wrp-chart-asr" class="chart-wrapper"><canvas id="chart-asr"></canvas></div>
256
- <div id="lang-asr-table" class="table-wrap"></div>
257
- </div>
258
 
259
- <div class="chart-section">
260
- <h4>Text-to-Speech (TTS) Results</h4>
261
- <div id="wrp-chart-tts" class="chart-wrapper"><canvas id="chart-tts"></canvas></div>
262
- <div id="lang-tts-table" class="table-wrap"></div>
263
- </div>
264
 
265
- <div class="chart-section">
266
- <h4>Spoken Language Identification (SLID) Results <span class="rank-info" style="font-weight:normal;">(Ranked by Score ↑)</span></h4>
267
- <div id="wrp-chart-slid" class="chart-wrapper"><canvas id="chart-slid"></canvas></div>
268
- <div id="lang-slid-table" class="table-wrap"></div>
269
- </div>
270
  </div>
271
  </div>
272
  </div>
@@ -301,7 +261,6 @@
301
  const NAME_TO_ISO = {'Afrikaans':'afr', 'Akuapim-twi':'ak', 'Amharic':'amh', 'Asante-twi':'tw', 'Bemba':'bem', 'Hausa':'hau', 'Igbo':'ibo', 'Yoruba':'yor', 'Zulu':'zul', 'Swahili':'swa', 'Somali':'som', 'Wolof':'wol', 'Xhosa':'xho'};
302
  let DATA = null;
303
  let asrMode = 'overview';
304
- let charts = { asr:null, tts:null, slid:null };
305
 
306
  fetch('/api/data').then(r=>r.json()).then(d=>{
307
  DATA = d;
@@ -316,9 +275,10 @@
316
  populate('tts-select', DATA.metadata.tts_models);
317
 
318
  const allLangs = new Set();
319
- Object.values(DATA.asr.by_model).forEach(rows => rows.forEach(r => allLangs.add(r.Language)));
320
- Object.values(DATA.tts).forEach(rows => rows.forEach(r => allLangs.add(r.language)));
321
- if(DATA.slid) DATA.slid.forEach(r => allLangs.add(r.Language));
 
322
 
323
  populate('lang-select', Array.from(allLangs).sort());
324
 
@@ -450,11 +410,11 @@
450
  document.getElementById('slid-table').innerHTML = html;
451
  }
452
 
453
- // --- LANGUAGE VIEW + CHARTS ---
454
  function renderLanguageView() {
455
  const lang = document.getElementById('lang-select').value;
456
 
457
- // 1. ASR Data Preparation
458
  let asrRows = [];
459
  Object.keys(DATA.asr.by_model).forEach(modName => {
460
  const row = DATA.asr.by_model[modName].find(r => r.Language === lang);
@@ -462,14 +422,14 @@
462
  });
463
  asrRows.sort((a,b) => (a.wer - b.wer) || (a.cer - b.cer));
464
 
465
- // 2. TTS Data Preparation
466
  let ttsRows = [];
467
  Object.keys(DATA.tts).forEach(mod => {
468
- const row = DATA.tts[mod].find(r => r.language === lang);
469
  if(row) ttsRows.push({model: mod, ...row});
470
  });
471
 
472
- // 3. SLID Data Preparation
473
  let slidRows = [];
474
  const slidRow = DATA.slid.find(r => r.Language === lang) ||
475
  DATA.slid.find(r => r.Language.startsWith(lang + " (")) ||
@@ -480,44 +440,50 @@
480
  slidRows.sort((a,b) => b.val - a.val); // Descending
481
  }
482
 
483
- // --- RENDER SUMMARY CARDS ---
484
  let summaryHTML = "";
485
 
486
  // ASR Winner
487
  if(asrRows.length > 0) {
488
- summaryHTML += `<div class="stat-card"><div class="stat-title">Best ASR Model</div><div class="stat-model">${asrRows[0].model}</div><div class="stat-val">WER: ${fmt(asrRows[0].wer)}%</div></div>`;
489
- document.getElementById('wrp-chart-asr').style.display = 'block';
490
- updateChart('chart-asr', 'ASR WER (Lower is Better)', asrRows.map(r=>r.model), asrRows.map(r=>r.wer), '#3b82f6');
491
- } else {
492
- summaryHTML += `<div class="stat-card"><div class="stat-title">ASR</div><div class="stat-val">No Data</div></div>`;
493
- document.getElementById('wrp-chart-asr').style.display = 'none';
494
- }
 
 
495
 
496
- // TTS Winner (Lowest WER for Intelligibility)
497
  if(ttsRows.length > 0) {
498
- // Sort by WER ascending
499
  const bestTTS = [...ttsRows].sort((a,b) => a.wer - b.wer)[0];
500
- summaryHTML += `<div class="stat-card"><div class="stat-title">Best TTS (Intelligibility)</div><div class="stat-model">${bestTTS.model}</div><div class="stat-val">WER: ${fmt(bestTTS.wer)}%</div></div>`;
501
- document.getElementById('wrp-chart-tts').style.display = 'block';
502
- updateChart('chart-tts', 'TTS WER (Lower is Better)', ttsRows.map(r=>r.model), ttsRows.map(r=>r.wer), '#f97316');
503
- } else {
504
- summaryHTML += `<div class="stat-card"><div class="stat-title">TTS</div><div class="stat-val">No Data</div></div>`;
505
- document.getElementById('wrp-chart-tts').style.display = 'none';
506
- }
 
 
507
 
508
  // SLID Winner
509
  if(slidRows.length > 0) {
510
- summaryHTML += `<div class="stat-card"><div class="stat-title">Best SLID Model</div><div class="stat-model">${slidRows[0].model}</div><div class="stat-val">F1: ${fmt(slidRows[0].val)}%</div></div>`;
511
- document.getElementById('wrp-chart-slid').style.display = 'block';
512
- updateChart('chart-slid', 'SLID F1 Score (Higher is Better)', slidRows.map(r=>r.model), slidRows.map(r=>r.val), '#22c55e');
513
- } else {
514
- summaryHTML += `<div class="stat-card"><div class="stat-title">SLID</div><div class="stat-val">No Data</div></div>`;
515
- document.getElementById('wrp-chart-slid').style.display = 'none';
516
- }
 
 
 
517
  document.getElementById('lang-summary').innerHTML = summaryHTML;
518
 
519
 
520
- // --- RENDER TABLES ---
521
  // ASR Table
522
  let h1 = `<table><thead><tr><th>Model</th><th>WER <i class="fa-solid fa-arrow-down arrow-down"></i></th><th>CER <i class="fa-solid fa-arrow-down arrow-down"></i></th></tr></thead><tbody>`;
523
  if(asrRows.length === 0) h1 += `<tr><td colspan="3" class="empty-msg">No ASR results available for ${lang}.</td></tr>`;
@@ -567,32 +533,6 @@
567
  document.getElementById('lang-slid-table').innerHTML = h3;
568
  }
569
 
570
- function updateChart(canvasId, label, labels, data, color) {
571
- const ctx = document.getElementById(canvasId).getContext('2d');
572
- if(charts[canvasId]) charts[canvasId].destroy();
573
- if(labels.length === 0) return;
574
-
575
- charts[canvasId] = new Chart(ctx, {
576
- type: 'bar',
577
- data: {
578
- labels: labels,
579
- datasets: [{
580
- label: label,
581
- data: data,
582
- backgroundColor: color,
583
- borderRadius: 4
584
- }]
585
- },
586
- options: {
587
- indexAxis: 'y', // Horizontal Bar Chart
588
- responsive: true,
589
- maintainAspectRatio: false,
590
- plugins: { legend: { display: true } },
591
- scales: { x: { beginAtZero: true } }
592
- }
593
- });
594
- }
595
-
596
  // --- UI ---
597
  window.setTab = (id, btn) => {
598
  document.querySelectorAll('.view').forEach(e => e.classList.remove('active'));
@@ -619,4 +559,4 @@
619
  }
620
  </script>
621
  </body>
622
- </html>
 
10
  <link rel="preconnect" href="https://fonts.gstatic.com" crossorigin>
11
  <link href="https://fonts.googleapis.com/css2?family=Inter:wght@400;500;600&family=Rubik:wght@500;600;700;800&display=swap" rel="stylesheet">
12
  <link rel="stylesheet" href="https://cdnjs.cloudflare.com/ajax/libs/font-awesome/6.4.0/css/all.min.css">
 
13
 
14
  <style>
15
  :root {
 
18
  --text-white: #ffffff;
19
  --simba-navy: #0f172a;
20
  --border-gold: #dca02a;
21
+ --grad-blue: linear-gradient(135deg, #3b82f6, #0ea5e9);
22
  --grad-red: linear-gradient(135deg, #ef4444, #f43f5e);
23
  --grad-purple: linear-gradient(135deg, #6366f1, #8b5cf6);
24
  --grad-orange: linear-gradient(135deg, #f97316, #fb923c);
 
45
  .btn-submit { background: var(--grad-blue); color: white !important; padding: 10px 24px; border-radius: 30px; font-weight: 600; font-size: 14px; box-shadow: 0 4px 12px rgba(59, 130, 246, 0.4); }
46
 
47
  /* --- HERO --- */
48
+ .hero { max-width: 1000px; margin: 60px auto 100px; padding: 0 40px; position: relative; text-align: center; }
49
+ .hero-content { z-index: 2; display: flex; flex-direction: column; align-items: center; }
 
 
 
 
 
 
 
 
 
 
 
50
  .conf-badge { background: white; color: black; display: inline-flex; align-items: center; gap: 10px; padding: 6px 16px; border-radius: 8px; margin-bottom: 30px; font-weight: 700; font-size: 14px; }
51
  .conf-badge i { color: #cc0000; font-size: 18px; }
52
  .hero h1 { font-family: 'Rubik', sans-serif; font-size: 64px; font-weight: 800; margin: 0; line-height: 1.1; }
53
  .hero h2 { font-family: 'Rubik', sans-serif; font-size: 32px; color: var(--text-yellow); margin: 15px 0 30px; font-weight: 700; }
54
  .hero p { color: #cbd5e1; font-size: 18px; line-height: 1.6; max-width: 700px; margin-bottom: 50px; }
 
55
  .hero-actions { display: flex; gap: 15px; flex-wrap: wrap; justify-content: center; }
56
+ .action-btn { display: flex; align-items: center; gap: 8px; padding: 12px 24px; border-radius: 30px; color: white; font-weight: 600; font-size: 15px; transition: transform 0.2s; }
57
+ .action-btn:hover { transform: translateY(-3px); color: white; }
58
+ .bg-red { background: var(--grad-red); } .bg-purple { background: var(--grad-purple); }
59
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
60
  /* --- CONTENT --- */
61
  .content-container { width: 80%; max-width: 1600px; margin: 0 auto; }
62
  .main-card { background: white; border-radius: 24px; padding: 40px; color: #0f172a; min-height: 600px; position: relative; z-index: 10; margin-bottom: 60px; }
 
76
  .score-desc { font-size: 14px; color: #475569; background: #fff7ed; border-left: 4px solid #f97316; padding: 10px 15px; margin-bottom: 20px; border-radius: 4px; }
77
 
78
  /* ASR Buttons */
79
+ .mode-btn { padding: 8px 20px; border-radius: 8px; font-weight: 600; font-size: 13px; cursor: pointer; transition: all 0.2s; border: 1px solid #e2e8f0; background: white; color: var(--simba-navy); }
 
 
 
 
80
  .mode-btn:hover { border-color: var(--text-yellow); color: var(--text-yellow); }
81
  .mode-btn.active { background-color: var(--simba-navy); color: white; border-color: var(--simba-navy); box-shadow: 0 4px 12px rgba(15, 23, 42, 0.15); }
82
 
83
+ /* --- FANCY SUMMARY CARDS --- */
84
+ .summary-grid { display: grid; grid-template-columns: repeat(auto-fit, minmax(320px, 1fr)); gap: 25px; margin-bottom: 50px; }
85
+
86
+ .fancy-card {
87
+ background: white; border-radius: 16px; padding: 25px;
88
+ display: flex; align-items: center; gap: 20px;
89
+ box-shadow: 0 10px 25px -5px rgba(0,0,0,0.08); border: 1px solid #f1f5f9;
90
+ transition: transform 0.3s ease, box-shadow 0.3s ease;
91
+ }
92
+ .fancy-card:hover { transform: translateY(-5px); box-shadow: 0 20px 30px -10px rgba(0,0,0,0.12); border-color: #e2e8f0; }
93
+
94
+ .card-icon {
95
+ width: 60px; height: 60px; border-radius: 14px;
96
+ display: flex; align-items: center; justify-content: center;
97
+ font-size: 26px; color: white; flex-shrink: 0;
98
+ box-shadow: 0 8px 15px -3px rgba(0,0,0,0.2);
99
+ }
100
+ .icon-asr { background: var(--grad-blue); }
101
+ .icon-tts { background: var(--grad-orange); }
102
+ .icon-slid { background: var(--grad-green); }
103
 
104
+ .card-content { flex: 1; }
105
+ .card-title { font-size: 11px; font-weight: 700; color: #64748b; text-transform: uppercase; letter-spacing: 1px; margin-bottom: 5px; }
106
+ .card-model { font-size: 18px; font-weight: 800; color: #0f172a; margin-bottom: 4px; line-height: 1.3; }
107
+ .card-score { font-size: 14px; font-weight: 600; color: #475569; }
108
+ .card-score b { color: #0f172a; }
 
 
109
 
110
  /* Table */
111
  .table-wrap { overflow-x: auto; border: 1px solid #e2e8f0; border-radius: 12px; margin-bottom: 30px; }
 
140
  <div class="nav-text"><span>Voice of a</span> Continent</div>
141
  </a>
142
  <div class="nav-links">
143
+ <a href="https://aclanthology.org/2025.emnlp-main.559" target="_blank" class="nav-link">Paper</a>
144
  <a href="https://africa.dlnlp.ai/simba/" target="_blank" class="btn-submit">Submit New Results</a>
145
  </div>
146
  </nav>
 
151
  <h1>SimbaBench Leaderboard</h1>
152
  <h2>Mapping Africa’s Speech Technology</h2>
153
  <p>SimbaBench bridges the digital divide with a unified suite for African AI: the largest open-source speech benchmark covering 61 languages.</p>
 
154
  <div class="hero-actions">
155
+ <a href="https://aclanthology.org/2025.emnlp-main.559" target="_blank" class="action-btn bg-red"><i class="fa-regular fa-file-pdf"></i> Read Paper</a>
156
+ <a href="https://africa.dlnlp.ai/simba" target="_blank" class="action-btn bg-purple"><i class="fa-solid fa-code-branch"></i> Official Website</a>
 
 
 
 
 
 
 
 
 
 
157
  </div>
158
  </div>
159
  </div>
 
219
 
220
  <div id="lang-summary" class="summary-grid"></div>
221
 
222
+ <h4>Automatic Speech Recognition (ASR) <span class="rank-info" style="font-weight:normal;">(Ranked by WER ↓, then CER ↓)</span></h4>
223
+ <div id="lang-asr-table" class="table-wrap"></div>
 
 
 
224
 
225
+ <h4>Text-to-Speech (TTS) Results</h4>
226
+ <div id="lang-tts-table" class="table-wrap"></div>
 
 
 
227
 
228
+ <h4>Spoken Language Identification (SLID) Results <span class="rank-info" style="font-weight:normal;">(Ranked by Score ↑)</span></h4>
229
+ <div id="lang-slid-table" class="table-wrap"></div>
 
 
 
230
  </div>
231
  </div>
232
  </div>
 
261
  const NAME_TO_ISO = {'Afrikaans':'afr', 'Akuapim-twi':'ak', 'Amharic':'amh', 'Asante-twi':'tw', 'Bemba':'bem', 'Hausa':'hau', 'Igbo':'ibo', 'Yoruba':'yor', 'Zulu':'zul', 'Swahili':'swa', 'Somali':'som', 'Wolof':'wol', 'Xhosa':'xho'};
262
  let DATA = null;
263
  let asrMode = 'overview';
 
264
 
265
  fetch('/api/data').then(r=>r.json()).then(d=>{
266
  DATA = d;
 
275
  populate('tts-select', DATA.metadata.tts_models);
276
 
277
  const allLangs = new Set();
278
+ // Collect languages from all sources to be thorough
279
+ Object.values(DATA.asr.by_model).forEach(rows => rows.forEach(r => { if(r.Language) allLangs.add(r.Language); }));
280
+ Object.values(DATA.tts).forEach(rows => rows.forEach(r => { const l = r.Language || r.language; if(l) allLangs.add(l); }));
281
+ if(DATA.slid) DATA.slid.forEach(r => { if(r.Language) allLangs.add(r.Language); });
282
 
283
  populate('lang-select', Array.from(allLangs).sort());
284
 
 
410
  document.getElementById('slid-table').innerHTML = html;
411
  }
412
 
413
+ // --- LANGUAGE VIEW ---
414
  function renderLanguageView() {
415
  const lang = document.getElementById('lang-select').value;
416
 
417
+ // 1. ASR Data
418
  let asrRows = [];
419
  Object.keys(DATA.asr.by_model).forEach(modName => {
420
  const row = DATA.asr.by_model[modName].find(r => r.Language === lang);
 
422
  });
423
  asrRows.sort((a,b) => (a.wer - b.wer) || (a.cer - b.cer));
424
 
425
+ // 2. TTS Data
426
  let ttsRows = [];
427
  Object.keys(DATA.tts).forEach(mod => {
428
+ const row = DATA.tts[mod].find(r => (r.Language || r.language) === lang);
429
  if(row) ttsRows.push({model: mod, ...row});
430
  });
431
 
432
+ // 3. SLID Data
433
  let slidRows = [];
434
  const slidRow = DATA.slid.find(r => r.Language === lang) ||
435
  DATA.slid.find(r => r.Language.startsWith(lang + " (")) ||
 
440
  slidRows.sort((a,b) => b.val - a.val); // Descending
441
  }
442
 
443
+ // --- SUMMARY CARDS ---
444
  let summaryHTML = "";
445
 
446
  // ASR Winner
447
  if(asrRows.length > 0) {
448
+ summaryHTML += `<div class="fancy-card">
449
+ <div class="card-icon icon-asr"><i class="fa-solid fa-microphone-lines"></i></div>
450
+ <div class="card-content">
451
+ <div class="card-title">Best ASR Model</div>
452
+ <div class="card-model">${asrRows[0].model}</div>
453
+ <div class="card-score">WER: <b>${fmt(asrRows[0].wer)}%</b></div>
454
+ </div>
455
+ </div>`;
456
+ } else summaryHTML += `<div class="fancy-card"><div class="card-icon icon-asr"><i class="fa-solid fa-microphone-slash"></i></div><div class="card-content"><div class="card-title">ASR</div><div class="card-model">No Data</div></div></div>`;
457
 
458
+ // TTS Winner
459
  if(ttsRows.length > 0) {
 
460
  const bestTTS = [...ttsRows].sort((a,b) => a.wer - b.wer)[0];
461
+ summaryHTML += `<div class="fancy-card">
462
+ <div class="card-icon icon-tts"><i class="fa-solid fa-volume-high"></i></div>
463
+ <div class="card-content">
464
+ <div class="card-title">Best TTS (Intelligibility)</div>
465
+ <div class="card-model">${bestTTS.model}</div>
466
+ <div class="card-score">WER: <b>${fmt(bestTTS.wer)}%</b></div>
467
+ </div>
468
+ </div>`;
469
+ } else summaryHTML += `<div class="fancy-card"><div class="card-icon icon-tts"><i class="fa-solid fa-volume-xmark"></i></div><div class="card-content"><div class="card-title">TTS</div><div class="card-model">No Data</div></div></div>`;
470
 
471
  // SLID Winner
472
  if(slidRows.length > 0) {
473
+ summaryHTML += `<div class="fancy-card">
474
+ <div class="card-icon icon-slid"><i class="fa-solid fa-language"></i></div>
475
+ <div class="card-content">
476
+ <div class="card-title">Best SLID Model</div>
477
+ <div class="card-model">${slidRows[0].model}</div>
478
+ <div class="card-score">F1 Score: <b>${fmt(slidRows[0].val)}%</b></div>
479
+ </div>
480
+ </div>`;
481
+ } else summaryHTML += `<div class="fancy-card"><div class="card-icon icon-slid"><i class="fa-solid fa-circle-question"></i></div><div class="card-content"><div class="card-title">SLID</div><div class="card-model">No Data</div></div></div>`;
482
+
483
  document.getElementById('lang-summary').innerHTML = summaryHTML;
484
 
485
 
486
+ // --- TABLES ---
487
  // ASR Table
488
  let h1 = `<table><thead><tr><th>Model</th><th>WER <i class="fa-solid fa-arrow-down arrow-down"></i></th><th>CER <i class="fa-solid fa-arrow-down arrow-down"></i></th></tr></thead><tbody>`;
489
  if(asrRows.length === 0) h1 += `<tr><td colspan="3" class="empty-msg">No ASR results available for ${lang}.</td></tr>`;
 
533
  document.getElementById('lang-slid-table').innerHTML = h3;
534
  }
535
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
536
  // --- UI ---
537
  window.setTab = (id, btn) => {
538
  document.querySelectorAll('.view').forEach(e => e.classList.remove('active'));
 
559
  }
560
  </script>
561
  </body>
562
+ </html>