leaderboard / frontend /index.html
Alyafeai's picture
add arabic support
9efd00d
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>QIMMA - Open Arabic LLM Leaderboard</title>
<script src="https://cdn.tailwindcss.com"></script>
<link href="https://unpkg.com/gridjs/dist/theme/mermaid.min.css" rel="stylesheet" />
<script src="https://unpkg.com/gridjs/dist/gridjs.umd.js"></script>
<script src="https://unpkg.com/lucide@latest"></script>
<script>
tailwind.config = { darkMode: 'class', theme: { extend: { colors: { darkbg: '#0f172a' }, animation: { 'fade-in': 'fadeIn 0.5s ease-out' }, keyframes: { fadeIn: { '0%': { opacity: '0', transform: 'translateY(10px)' }, '100%': { opacity: '1', transform: 'translateY(0)' } } } } } }
</script>
<style>
html[dir="rtl"] body {
text-align: right;
}
html[dir="rtl"] #appControls {
left: 1.5rem;
right: auto;
}
html[dir="rtl"] #appTabs nav {
direction: rtl;
}
</style>
</head>
<body class="bg-slate-50 text-slate-800 font-sans transition-colors duration-300 dark:bg-darkbg dark:text-slate-100">
<div id="appControls" class="absolute top-6 right-6 z-50 flex items-center gap-3">
<button onclick="toggleLanguage()"
class="flex items-center gap-2 rounded-full bg-white px-4 py-2 text-sm font-semibold text-slate-600 shadow-md hover:bg-slate-100 dark:bg-slate-800 dark:text-sky-300 dark:hover:bg-slate-700 transition-all">
<i data-lucide="languages" class="h-4 w-4"></i>
<span id="languageToggleLabel">AR</span>
</button>
<button onclick="toggleDarkMode()"
class="p-2 rounded-full bg-white text-slate-600 shadow-md hover:bg-slate-100 dark:bg-slate-800 dark:text-yellow-400 dark:hover:bg-slate-700 transition-all">
<i data-lucide="moon" id="themeIcon" class="h-5 w-5"></i>
</button>
</div>
<div class="max-w-[98%] mx-auto px-4 sm:px-6 lg:px-8 py-8">
<div id="dynamic-header-container">
<div class="p-12 flex justify-center"><i data-lucide="loader-2"
class="w-10 h-10 animate-spin text-indigo-600"></i></div>
</div>
<div id="appTabs" class="border-b border-slate-200 dark:border-slate-700 mb-6">
<nav class="-mb-px flex gap-8 justify-center">
<button onclick="switchTab('about')" id="tab-btn-about"
class="border-transparent text-slate-500 dark:text-slate-400 hover:text-slate-700 dark:hover:text-slate-200 border-b-2 py-4 px-1 font-medium text-sm transition-colors">
<span id="tabLabelAbout">About</span></button>
<button onclick="switchTab('leaderboard')" id="tab-btn-leaderboard"
class="border-indigo-500 text-indigo-600 dark:text-indigo-400 dark:border-indigo-400 border-b-2 py-4 px-1 font-medium text-sm transition-colors">🏅
<span id="tabLabelLeaderboard">Leaderboard</span></button>
<button onclick="switchTab('submit')" id="tab-btn-submit"
class="border-transparent text-slate-500 dark:text-slate-400 hover:text-slate-700 dark:hover:text-slate-200 border-b-2 py-4 px-1 font-medium text-sm transition-colors">🚀
<span id="tabLabelSubmit">Submit Model</span></button>
</nav>
</div>
<div id="tab-content-leaderboard" class="block animate-fade-in">
<div class="flex justify-center p-12"><i data-lucide="loader-2"
class="w-8 h-8 animate-spin text-indigo-600"></i></div>
</div>
<div id="tab-content-submit" class="hidden max-w-5xl mx-auto animate-fade-in"></div>
<div id="tab-content-about" class="hidden max-w-[97%] mx-auto animate-fade-in"></div>
<div class="mt-16 text-center border-t border-slate-200 dark:border-slate-700 pt-8">
<p class="text-slate-500 dark:text-slate-400 text-sm" id="footerText">&copy; 2026 QIMMA. Built for the Arabic AI community.</p>
</div>
</div>
<script id="eval-columns-data" type="application/json">
{{ eval_columns | tojson }}
</script>
<script id="benchmark-metadata-data" type="application/json">
{{ benchmark_metadata | tojson }}
</script>
<script>
window.EVAL_COLUMNS = JSON.parse(document.getElementById('eval-columns-data').textContent);
window.BENCHMARK_METADATA = JSON.parse(document.getElementById('benchmark-metadata-data').textContent);
const $ = s => document.querySelector(s);
const SHELL_TRANSLATIONS = {
ar: {
title: "QIMMA - لوحة صدارة عربية مفتوحة لتقييم النماذج اللغوية",
language_toggle: "EN",
tab_about: "حول",
tab_leaderboard: "لوحة الصدارة",
tab_submit: "تسليم نموذج",
footer: "© 2026 QIMMA. صُمّم هذا المشروع لخدمة مجتمع الذكاء الاصطناعي العربي.",
load_error: "تعذّر تحميل لوحة الصدارة.",
},
en: {
title: "QIMMA - Open Arabic LLM Leaderboard",
language_toggle: "AR",
tab_about: "About",
tab_leaderboard: "Leaderboard",
tab_submit: "Submit Model",
footer: "© 2026 QIMMA. Built for the Arabic AI community.",
load_error: "Error loading leaderboard.",
}
};
let currentLanguage = localStorage.getItem('language') || 'en';
const languageListeners = [];
const BASE_URL = window.self !== window.top
? 'https://qimma-leaderboard.hf.space'
: '';
window.getCurrentLanguage = () => currentLanguage;
window.registerLanguageListener = (fn) => {
if (typeof fn === 'function') languageListeners.push(fn);
};
window.notifyLanguageChanged = () => {
languageListeners.forEach(fn => {
try { fn(currentLanguage); } catch (err) { console.error('language listener failed', err); }
});
};
function applyShellTranslations() {
const t = SHELL_TRANSLATIONS[currentLanguage] || SHELL_TRANSLATIONS.en;
document.title = t.title;
document.documentElement.lang = currentLanguage === 'ar' ? 'ar' : 'en';
document.documentElement.dir = currentLanguage === 'ar' ? 'rtl' : 'ltr';
document.body?.classList.toggle('rtl-ui', currentLanguage === 'ar');
document.getElementById('languageToggleLabel').innerText = t.language_toggle;
document.getElementById('tabLabelAbout').innerText = t.tab_about;
document.getElementById('tabLabelLeaderboard').innerText = t.tab_leaderboard;
document.getElementById('tabLabelSubmit').innerText = t.tab_submit;
document.getElementById('footerText').innerText = t.footer;
}
function setLanguage(lang) {
currentLanguage = lang === 'en' ? 'en' : 'ar';
localStorage.setItem('language', currentLanguage);
applyShellTranslations();
window.notifyLanguageChanged();
}
function toggleLanguage() {
setLanguage(currentLanguage === 'ar' ? 'en' : 'ar');
}
window.toggleLanguage = toggleLanguage;
window.setLanguage = setLanguage;
function toggleDarkMode() {
const isDark = document.documentElement.classList.toggle('dark');
localStorage.theme = isDark ? 'dark' : 'light';
$('#themeIcon').setAttribute('data-lucide', isDark ? 'sun' : 'moon');
lucide.createIcons();
}
if (localStorage.theme === 'dark' || (!('theme' in localStorage) && window.matchMedia('(prefers-color-scheme: dark)').matches)) toggleDarkMode();
async function init() {
try {
const res = await fetch(BASE_URL + '/api/leaderboard');
const json = await res.json();
window.allData = json.data || [];
await Promise.all([
loadTabContent(BASE_URL + '/header.html', '#dynamic-header-container'),
loadTabContent(BASE_URL + '/leaderboard.html', '#tab-content-leaderboard')
]);
if (window.initLeaderboard) {
window.initLeaderboard(allData);
}
loadTabContent(BASE_URL + '/about.html', '#tab-content-about');
loadTabContent(BASE_URL + '/submit.html', '#tab-content-submit');
} catch (err) {
console.error(err);
const t = SHELL_TRANSLATIONS[currentLanguage] || SHELL_TRANSLATIONS.en;
$('#tab-content-leaderboard').innerHTML = `<div class="p-8 text-center text-rose-500">${t.load_error}</div>`;
}
}
async function loadTabContent(url, selector) {
const container = $(selector);
if (!container || container.dataset.loaded) return;
try {
// const res = await fetch(url);
const res = await fetch(url + '?v=' + Date.now()); // leen: added this to see changes reflecting in header
if (!res.ok) throw new Error(`Status: ${res.status}`);
const html = await res.text();
const parser = new DOMParser();
const doc = parser.parseFromString(html, 'text/html');
container.innerHTML = doc.body.innerHTML;
container.dataset.loaded = "true";
if (window.lucide) lucide.createIcons();
doc.body.querySelectorAll('script').forEach(oldScript => {
const newScript = document.createElement('script');
Array.from(oldScript.attributes).forEach(attr => newScript.setAttribute(attr.name, attr.value));
newScript.textContent = oldScript.textContent;
document.body.appendChild(newScript);
});
window.notifyLanguageChanged();
} catch (err) { console.error(`Failed to load ${url}:`, err); }
}
function switchTab(tab) {
['leaderboard', 'submit', 'about'].forEach(t => {
$(`#tab-content-${t}`).classList[t === tab ? 'remove' : 'add']('hidden');
$(`#tab-btn-${t}`).className = t === tab
? "border-indigo-500 text-indigo-600 dark:text-indigo-400 dark:border-indigo-400 border-b-2 py-4 px-1 font-medium text-sm transition-colors"
: "border-transparent text-slate-500 dark:text-slate-400 hover:text-slate-700 dark:hover:text-slate-200 border-b-2 py-4 px-1 font-medium text-sm transition-colors";
});
}
applyShellTranslations();
init();
lucide.createIcons();
</script>
</body>
</html>