Spaces:
Running
Running
<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; } | |
/* Large screens - even bigger */ | |
@media (min-width: 1400px) { | |
.iframe-container { height: 700px; } | |
} | |
/* Medium screens */ | |
@media (max-width: 1200px) { | |
.iframe-container { height: 500px; } | |
} | |
/* Small screens - stack vertically */ | |
@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 { | |
// Look for "chosen:" and "reason:" patterns | |
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(); | |
// Calculate win rates | |
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; | |
// Clean HTML content by removing markdown code blocks | |
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(/```$/, ''); | |
} | |
// Parse evaluation data | |
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); | |
}); | |
} | |
// Load apps when page loads | |
loadAppsFromHuggingFace(); | |
</script> | |
</body> | |
</html> |