|
|
<!DOCTYPE html> |
|
|
<html> |
|
|
<head> |
|
|
<meta charset="UTF-8"> |
|
|
<title>Live WebApp Viewer</title> |
|
|
<style> |
|
|
body { margin: 0; font-family: Arial, sans-serif; background: #f5f5f5; } |
|
|
.container { width: 100vw; padding: 10px; box-sizing: border-box; } |
|
|
.loading { text-align: center; padding: 50px; font-size: 18px; color: #666; } |
|
|
.main-title { |
|
|
text-align: center; |
|
|
margin-bottom: 20px; |
|
|
padding: 0 20px; |
|
|
} |
|
|
.main-title h1 { |
|
|
font-size: 48px; |
|
|
font-weight: 800; |
|
|
margin: 0 0 8px 0; |
|
|
letter-spacing: -1px; |
|
|
background: linear-gradient(135deg, #667eea 0%, #764ba2 100%); |
|
|
-webkit-background-clip: text; |
|
|
-webkit-text-fill-color: transparent; |
|
|
background-clip: text; |
|
|
} |
|
|
.main-title p { |
|
|
font-size: 20px; |
|
|
margin: 0; |
|
|
color: #666; |
|
|
font-weight: 400; |
|
|
line-height: 1.4; |
|
|
} |
|
|
.stats-header { |
|
|
background: linear-gradient(135deg, #667eea 0%, #764ba2 100%); |
|
|
color: white; |
|
|
padding: 30px 20px; |
|
|
text-align: center; |
|
|
margin-bottom: 30px; |
|
|
border-radius: 12px; |
|
|
box-shadow: 0 8px 32px rgba(0,0,0,0.1); |
|
|
} |
|
|
.stats-header p { |
|
|
font-size: 16px; |
|
|
margin: 0 0 20px 0; |
|
|
opacity: 0.9; |
|
|
font-weight: 400; |
|
|
line-height: 1.5; |
|
|
} |
|
|
.win-stats { |
|
|
display: flex; |
|
|
justify-content: center; |
|
|
gap: 50px; |
|
|
margin-top: 20px; |
|
|
flex-wrap: wrap; |
|
|
} |
|
|
.stat { |
|
|
font-size: 16px; |
|
|
background: rgba(255,255,255,0.15); |
|
|
padding: 12px 20px; |
|
|
border-radius: 8px; |
|
|
backdrop-filter: blur(10px); |
|
|
border: 1px solid rgba(255,255,255,0.2); |
|
|
} |
|
|
.stat .model { |
|
|
font-weight: 600; |
|
|
display: block; |
|
|
margin-bottom: 4px; |
|
|
} |
|
|
.stat .wins { |
|
|
color: #4ade80; |
|
|
font-weight: 700; |
|
|
font-size: 18px; |
|
|
} |
|
|
.app-section { margin-bottom: 30px; background: white; border-radius: 12px; box-shadow: 0 4px 20px rgba(0,0,0,0.08); overflow: hidden; } |
|
|
.description-header { |
|
|
background: linear-gradient(135deg, #667eea 0%, #764ba2 100%); |
|
|
color: white; |
|
|
padding: 20px 15px; |
|
|
text-align: center; |
|
|
font-size: 18px; |
|
|
font-weight: 600; |
|
|
letter-spacing: -0.3px; |
|
|
} |
|
|
.evaluation-section { background: #f8f9fa; border-bottom: 1px solid #eee; padding: 15px; } |
|
|
.evaluation-result { background: #d4edda; border: 1px solid #c3e6cb; border-radius: 4px; padding: 12px; margin-bottom: 10px; } |
|
|
.eval-label { font-size: 12px; color: #666; margin-bottom: 5px; } |
|
|
.winner { color: #155724; font-weight: bold; margin-bottom: 5px; } |
|
|
.reason { color: #155724; } |
|
|
.view-eval-btn { background: #007bff; color: white; border: none; padding: 5px 10px; border-radius: 3px; cursor: pointer; margin-top: 10px; font-size: 12px; } |
|
|
.full-evaluation { background: #f8f9fa; border: 1px solid #dee2e6; border-radius: 4px; padding: 15px; margin-top: 10px; display: none; } |
|
|
.thinking-content { max-height: 300px; overflow-y: auto; font-size: 14px; line-height: 1.5; white-space: pre-wrap; text-align: left; color: #495057; } |
|
|
.implementations { display: grid; grid-template-columns: 1fr 1fr; gap: 0; } |
|
|
.impl-panel { border-right: 1px solid #eee; } |
|
|
.impl-panel:last-child { border-right: none; } |
|
|
.impl-header { |
|
|
background: linear-gradient(135deg, #667eea 0%, #764ba2 100%); |
|
|
color: white; |
|
|
padding: 12px 15px; |
|
|
font-weight: 600; |
|
|
text-align: center; |
|
|
font-size: 15px; |
|
|
letter-spacing: -0.2px; |
|
|
} |
|
|
.iframe-container { height: 600px; } |
|
|
iframe { width: 100%; height: 100%; border: none; transform: scale(1); transform-origin: top left; } |
|
|
.error { color: red; text-align: center; padding: 20px; } |
|
|
|
|
|
@media (min-width: 1400px) { |
|
|
.iframe-container { height: 700px; } |
|
|
} |
|
|
|
|
|
|
|
|
@media (max-width: 1200px) { |
|
|
.iframe-container { height: 500px; } |
|
|
} |
|
|
|
|
|
|
|
|
@media (max-width: 768px) { |
|
|
.implementations { grid-template-columns: 1fr; } |
|
|
.impl-panel { border-right: none; border-bottom: 1px solid #eee; } |
|
|
.impl-panel:last-child { border-bottom: none; } |
|
|
.iframe-container { height: 400px; } |
|
|
.container { padding: 5px; } |
|
|
.description-header { padding: 10px; font-size: 14px; } |
|
|
} |
|
|
</style> |
|
|
</head> |
|
|
<body> |
|
|
<div class="container"> |
|
|
<div id="apps-container" class="loading">Loading apps from Hugging Face...</div> |
|
|
</div> |
|
|
|
|
|
<script> |
|
|
function parseEvaluation(evalText) { |
|
|
if (!evalText) return null; |
|
|
|
|
|
try { |
|
|
|
|
|
const chosenMatch = evalText.match(/chosen:\s*(.+?)(?:\n|$)/i); |
|
|
const reasonMatch = evalText.match(/reason:\s*(.+?)(?:\n|$)/is); |
|
|
|
|
|
if (chosenMatch) { |
|
|
return { |
|
|
winner: chosenMatch[1].trim(), |
|
|
reason: reasonMatch ? reasonMatch[1].trim() : '', |
|
|
fullEval: evalText |
|
|
}; |
|
|
} |
|
|
|
|
|
return null; |
|
|
} catch (e) { |
|
|
console.error('Error parsing evaluation:', e); |
|
|
return null; |
|
|
} |
|
|
} |
|
|
|
|
|
function createEvaluationSection(evaluation, index) { |
|
|
const winner = evaluation.winner.toLowerCase().includes('kimi') ? 'Kimi-K2' : |
|
|
evaluation.winner.toLowerCase().includes('qwen') ? 'Qwen3-Coder' : |
|
|
evaluation.winner; |
|
|
|
|
|
return ` |
|
|
<div class="evaluation-section"> |
|
|
<div class="eval-label">(Kimi-K2 judge)</div> |
|
|
<div class="evaluation-result"> |
|
|
<div class="winner">🏆 Winner: ${winner}</div> |
|
|
<button class="view-eval-btn" onclick="toggleFullEval(${index})">View Reason</button> |
|
|
</div> |
|
|
<div class="full-evaluation" id="full-eval-${index}"> |
|
|
<div class="thinking-content">${evaluation.reason}</div> |
|
|
</div> |
|
|
</div> |
|
|
`; |
|
|
} |
|
|
|
|
|
function calculateWinRates(rows) { |
|
|
let kimiWins = 0; |
|
|
let qwenWins = 0; |
|
|
let ties = 0; |
|
|
let totalEvaluated = 0; |
|
|
|
|
|
rows.forEach(row => { |
|
|
const evaluation = parseEvaluation(row.row['r1-evaluation'] || ''); |
|
|
if (evaluation) { |
|
|
totalEvaluated++; |
|
|
const winner = evaluation.winner.toLowerCase(); |
|
|
if (winner.includes('kimi')) { |
|
|
kimiWins++; |
|
|
} else if (winner.includes('qwen')) { |
|
|
qwenWins++; |
|
|
} else { |
|
|
ties++; |
|
|
} |
|
|
} |
|
|
}); |
|
|
|
|
|
const kimiRate = totalEvaluated > 0 ? Math.round((kimiWins / totalEvaluated) * 100) : 0; |
|
|
const qwenRate = totalEvaluated > 0 ? Math.round((qwenWins / totalEvaluated) * 100) : 0; |
|
|
|
|
|
return { |
|
|
kimi: kimiWins, |
|
|
qwen: qwenWins, |
|
|
ties: ties, |
|
|
kimiRate: kimiRate, |
|
|
qwenRate: qwenRate, |
|
|
total: totalEvaluated |
|
|
}; |
|
|
} |
|
|
|
|
|
function toggleFullEval(index) { |
|
|
const fullEval = document.getElementById(`full-eval-${index}`); |
|
|
|
|
|
if (fullEval.style.display === 'block') { |
|
|
fullEval.style.display = 'none'; |
|
|
} else { |
|
|
fullEval.style.display = 'block'; |
|
|
} |
|
|
} |
|
|
|
|
|
async function loadAppsFromHuggingFace() { |
|
|
const container = document.getElementById('apps-container'); |
|
|
|
|
|
const response = await fetch('https://datasets-server.huggingface.co/rows?dataset=dvilasuero/JSVibes&config=default&split=train&offset=0&length=50'); |
|
|
const data = await response.json(); |
|
|
|
|
|
|
|
|
const winStats = calculateWinRates(data.rows); |
|
|
|
|
|
container.innerHTML = ` |
|
|
<div class="main-title"> |
|
|
<h1>JSVibes</h1> |
|
|
<p>Vibe testing open models for simple but useful code tasks</p> |
|
|
</div> |
|
|
<div class="stats-header"> |
|
|
<p style="font-size: 14px; opacity: 0.8;">Automatically evaluated by Kimi K2 as a judge. Judgments are imperfect, test them yourself!</p> |
|
|
<div class="win-stats"> |
|
|
<div class="stat"> |
|
|
<span class="model">Kimi-K2</span> |
|
|
<span class="wins">${winStats.kimi} wins</span> |
|
|
<div style="font-size: 14px; opacity: 0.8;">${winStats.kimiRate}%</div> |
|
|
</div> |
|
|
<div class="stat"> |
|
|
<span class="model">Qwen3-Coder</span> |
|
|
<span class="wins">${winStats.qwen} wins</span> |
|
|
<div style="font-size: 14px; opacity: 0.8;">${winStats.qwenRate}%</div> |
|
|
</div> |
|
|
<div class="stat"> |
|
|
<span class="model">Ties</span> |
|
|
<span class="wins">${winStats.ties}</span> |
|
|
</div> |
|
|
</div> |
|
|
</div> |
|
|
`; |
|
|
|
|
|
data.rows.forEach((row, index) => { |
|
|
const app = row.row; |
|
|
|
|
|
|
|
|
let kimiHtml = app['kimi-k2'] || ''; |
|
|
let qwenHtml = app['qwen3-coder'] || ''; |
|
|
|
|
|
if (kimiHtml.startsWith('```html')) { |
|
|
kimiHtml = kimiHtml.replace(/```html\n?/, '').replace(/```$/, ''); |
|
|
} |
|
|
if (qwenHtml.startsWith('```html')) { |
|
|
qwenHtml = qwenHtml.replace(/```html\n?/, '').replace(/```$/, ''); |
|
|
} |
|
|
|
|
|
|
|
|
const evaluation = parseEvaluation(app['r1-evaluation'] || ''); |
|
|
if (!evaluation && app['r1-evaluation']) { |
|
|
console.log(`Failed to parse evaluation for app ${index}:`, app['r1-evaluation']); |
|
|
} |
|
|
|
|
|
const section = document.createElement('div'); |
|
|
section.className = 'app-section'; |
|
|
section.innerHTML = ` |
|
|
<div class="description-header"> |
|
|
${index + 1}. ${app.description || 'No description available'} |
|
|
</div> |
|
|
${evaluation ? createEvaluationSection(evaluation, index) : ''} |
|
|
<div class="implementations"> |
|
|
<div class="impl-panel"> |
|
|
<div class="impl-header">Kimi-K2</div> |
|
|
<div class="iframe-container"> |
|
|
<iframe srcdoc="${kimiHtml.replace(/"/g, '"')}"></iframe> |
|
|
</div> |
|
|
</div> |
|
|
<div class="impl-panel"> |
|
|
<div class="impl-header">Qwen3-Coder</div> |
|
|
<div class="iframe-container"> |
|
|
<iframe srcdoc="${qwenHtml.replace(/"/g, '"')}"></iframe> |
|
|
</div> |
|
|
</div> |
|
|
</div> |
|
|
`; |
|
|
|
|
|
container.appendChild(section); |
|
|
}); |
|
|
} |
|
|
|
|
|
|
|
|
loadAppsFromHuggingFace(); |
|
|
</script> |
|
|
</body> |
|
|
</html> |