SLMLeaderboard / index.html
cyhuang-tw's picture
Upload index.html
a7d69f1 verified
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>Spoken Language Model Leaderboard</title>
<script src="https://cdnjs.cloudflare.com/ajax/libs/PapaParse/5.4.1/papaparse.min.js"></script>
<style>
* {
margin: 0;
padding: 0;
box-sizing: border-box;
}
body {
font-family: -apple-system, BlinkMacSystemFont, 'Segoe UI', Roboto, Arial, sans-serif;
background: #f5f5f5;
padding: 20px;
}
.container {
max-width: 1400px;
margin: 0 auto;
background: white;
border-radius: 8px;
box-shadow: 0 2px 10px rgba(0,0,0,0.1);
overflow: hidden;
}
.header {
background: linear-gradient(135deg, #4f46e5, #7c3aed);
color: white;
padding: 30px;
text-align: center;
}
.header h1 {
font-size: 2em;
margin-bottom: 10px;
}
.header p {
opacity: 0.9;
}
.controls {
padding: 20px 30px;
border-bottom: 1px solid #e5e5e5;
display: flex;
gap: 20px;
flex-wrap: wrap;
}
.search-container {
flex: 1;
min-width: 200px;
}
.search-container input {
width: 100%;
padding: 10px;
border: 1px solid #ddd;
border-radius: 4px;
font-size: 14px;
}
.info {
padding: 10px 30px;
background: #f9f9f9;
font-size: 14px;
color: #666;
}
.table-container {
overflow-x: auto;
max-height: 600px;
overflow-y: auto;
}
table {
width: 100%;
border-collapse: collapse;
}
thead {
background: #f9f9f9;
position: sticky;
top: 0;
z-index: 10;
}
th {
padding: 12px;
text-align: left;
font-weight: 600;
font-size: 14px;
border-bottom: 2px solid #e5e5e5;
cursor: pointer;
white-space: nowrap;
background: #f9f9f9;
}
th:hover {
background: #f0f0f0;
}
th.sort-asc::after {
content: ' ↑';
color: #4f46e5;
}
th.sort-desc::after {
content: ' ↓';
color: #4f46e5;
}
tbody tr {
border-bottom: 1px solid #f0f0f0;
}
tbody tr:hover {
background: #f9f9f9;
}
td {
padding: 10px 12px;
font-size: 14px;
white-space: nowrap;
}
.rank {
font-weight: bold;
color: #4f46e5;
}
.model-name {
font-weight: 500;
}
.empty {
color: #ccc;
font-style: italic;
}
.loading {
padding: 50px;
text-align: center;
color: #666;
}
.error {
padding: 50px;
text-align: center;
color: #dc2626;
}
@media (max-width: 768px) {
.controls {
flex-direction: column;
}
}
</style>
</head>
<body>
<div class="container">
<div class="header">
<h1>Spoken Language Model Leaderboard</h1>
<p></p>
</div>
<div class="controls">
<div class="search-container">
<input type="text" id="searchInput" placeholder="Search models...">
</div>
<div class="search-container">
<input type="text" id="columnSearchInput" placeholder="Search benchmarks...">
</div>
</div>
<div class="info" id="info">
Loading data...
</div>
<div class="table-container">
<div id="loading" class="loading">Loading leaderboard data...</div>
<div id="error" class="error" style="display: none;"></div>
<table id="leaderboard" style="display: none;">
<thead id="tableHead"></thead>
<tbody id="tableBody"></tbody>
</table>
</div>
</div>
<script>
let data = [];
let filteredData = [];
let columns = [];
let selectedColumns = [];
let filteredColumns = [];
let sortColumn = '';
let sortOrder = 'desc';
// Load and parse CSV
function loadCSV() {
fetch('data.csv')
.then(response => {
if (!response.ok) {
throw new Error('Could not load data.csv');
}
return response.text();
})
.then(csvText => {
parseCSV(csvText);
})
.catch(error => {
showError('Error loading data.csv: ' + error.message +
'<br>Please ensure data.csv is in the same directory as this HTML file.' +
'<br>Note: This page needs to be served via a web server (not file://)');
});
}
// Parse CSV data
function parseCSV(csvText) {
const result = Papa.parse(csvText, {
header: true,
dynamicTyping: true,
skipEmptyLines: true
});
if (result.errors.length > 0 && result.errors[0].code !== 'TooFewFields') {
console.warn('CSV parsing warnings:', result.errors);
}
data = result.data;
filteredData = [...data];
columns = result.meta.fields || [];
// Clean column names
columns = columns.map(col => col.trim());
// Show all columns
selectedColumns = columns;
filteredColumns = columns;
initializeControls();
updateTable();
hideLoading();
}
// Initialize controls
function initializeControls() {
// Search
document.getElementById('searchInput').addEventListener('input', function(e) {
const searchTerm = e.target.value.toLowerCase();
if (searchTerm === '') {
filteredData = [...data];
} else {
filteredData = data.filter(row => {
return Object.values(row).some(val =>
val && val.toString().toLowerCase().includes(searchTerm)
);
});
}
updateTable();
});
// Column search
document.getElementById('columnSearchInput').addEventListener('input', function(e) {
const searchTerm = e.target.value.toLowerCase();
if (searchTerm === '') {
filteredColumns = [...columns];
} else {
filteredColumns = columns.filter(col =>
col.toLowerCase().includes(searchTerm)
);
// Always keep first column
if (!filteredColumns.includes(columns[0])) {
filteredColumns.unshift(columns[0]);
}
}
updateTable();
});
}
// Sort data
function sortData(column) {
if (sortColumn === column) {
sortOrder = sortOrder === 'asc' ? 'desc' : 'asc';
} else {
sortColumn = column;
sortOrder = 'desc';
}
filteredData.sort((a, b) => {
let aVal = a[column];
let bVal = b[column];
// Handle empty values
if (aVal === null || aVal === undefined || aVal === '') return 1;
if (bVal === null || bVal === undefined || bVal === '') return -1;
// Numeric comparison
if (typeof aVal === 'number' && typeof bVal === 'number') {
return sortOrder === 'asc' ? aVal - bVal : bVal - aVal;
}
// String comparison
const result = String(aVal).localeCompare(String(bVal));
return sortOrder === 'asc' ? result : -result;
});
updateTable();
}
// Update table display
function updateTable() {
const thead = document.getElementById('tableHead');
const tbody = document.getElementById('tableBody');
// Update info
document.getElementById('info').textContent =
`Showing ${filteredData.length} of ${data.length} models | ${filteredColumns.length} of ${columns.length} columns`;
// Create header
thead.innerHTML = '';
const headerRow = document.createElement('tr');
// Rank column
const rankTh = document.createElement('th');
rankTh.textContent = '';
headerRow.appendChild(rankTh);
// Data columns
filteredColumns.forEach(col => {
const th = document.createElement('th');
th.textContent = col;
th.onclick = () => sortData(col);
if (col === sortColumn) {
th.className = sortOrder === 'asc' ? 'sort-asc' : 'sort-desc';
}
headerRow.appendChild(th);
});
thead.appendChild(headerRow);
// Create body
tbody.innerHTML = '';
filteredData.forEach((row, index) => {
const tr = document.createElement('tr');
// Rank
const rankTd = document.createElement('td');
rankTd.className = 'rank';
rankTd.textContent = index + 1;
tr.appendChild(rankTd);
// Data cells
filteredColumns.forEach((col, colIndex) => {
const td = document.createElement('td');
const value = row[col];
if (value === null || value === undefined || value === '') {
td.innerHTML = '<span class="empty">—</span>';
} else if (colIndex === 0) {
td.className = 'model-name';
td.textContent = value;
} else if (typeof value === 'number') {
td.textContent = Number.isInteger(value) ? value : value.toFixed(4);
} else {
td.textContent = value;
}
tr.appendChild(td);
});
tbody.appendChild(tr);
});
document.getElementById('leaderboard').style.display = 'table';
}
// Hide loading message
function hideLoading() {
document.getElementById('loading').style.display = 'none';
}
// Show error message
function showError(message) {
document.getElementById('loading').style.display = 'none';
document.getElementById('error').innerHTML = message;
document.getElementById('error').style.display = 'block';
}
// Initialize on page load
window.addEventListener('DOMContentLoaded', loadCSV);
</script>
</body>
</html>