fartcore / static /index.html
josephrw's picture
Upload static/index.html with huggingface_hub
42d8bdb verified
Raw
History Blame Contribute Delete
6.84 kB
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>AFIP β€” Acoustic Flatulence Intelligence Platform</title>
<style>
:root { --bg: #0a0a0f; --surface: #14141a; --border: #2a2a35; --text: #e2e2e8; --text-muted: #888898; --accent: #3b82f6; --success: #22c55e; --warn: #eab308; }
* { box-sizing: border-box; margin: 0; }
body { background: var(--bg); color: var(--text); font-family: -apple-system, BlinkMacSystemFont, "Segoe UI", Roboto, sans-serif; padding: 40px 24px; }
.container { max-width: 720px; margin: 0 auto; }
header { text-align: center; margin-bottom: 40px; }
h1 { font-size: 2rem; margin-bottom: 8px; }
p { color: var(--text-muted); }
.actions { display: flex; gap: 12px; justify-content: center; margin-bottom: 32px; flex-wrap: wrap; }
button {
background: linear-gradient(135deg, var(--accent), #8b5cf6); color: #fff; border: none;
padding: 12px 28px; border-radius: 10px; font-size: 1rem; cursor: pointer; transition: transform 0.15s;
}
button:hover { transform: translateY(-2px); }
button.secondary { background: var(--surface); border: 1px solid var(--border); color: var(--text); }
.dropzone {
border: 2px dashed var(--border); border-radius: 12px; padding: 48px 24px;
text-align: center; background: var(--surface); cursor: pointer; transition: border-color 0.2s;
}
.dropzone:hover, .dropzone.dragover { border-color: var(--accent); }
.dropzone input { display: none; }
.result { background: var(--surface); border: 1px solid var(--border); border-radius: 12px; padding: 20px; margin-top: 24px; display: none; }
.result.show { display: block; }
.result h3 { margin-bottom: 12px; }
.grid { display: grid; grid-template-columns: 1fr 1fr; gap: 12px; }
.kv { display: flex; justify-content: space-between; padding: 8px 0; border-bottom: 1px solid var(--border); font-size: 0.9rem; }
.kv .key { color: var(--text-muted); }
.badge { background: rgba(34,197,94,0.15); color: var(--success); padding: 2px 10px; border-radius: 999px; font-size: 0.75rem; }
.notes { max-height: 200px; overflow-y: auto; font-family: ui-monospace, monospace; font-size: 0.82rem; margin-top: 12px; }
.note-row { padding: 4px 0; border-bottom: 1px solid var(--border); display: flex; gap: 12px; }
.note-row span { color: var(--text-muted); }
.loading { text-align: center; color: var(--text-muted); margin-top: 20px; display: none; }
.leaderboard { margin-top: 32px; }
.leaderboard h3 { margin-bottom: 12px; }
.lb-row { display: flex; justify-content: space-between; padding: 10px 0; border-bottom: 1px solid var(--border); font-size: 0.9rem; }
.lb-row .score { color: var(--success); font-weight: 600; }
</style>
</head>
<body>
<div class="container">
<header>
<h1>πŸ’¨ AFIP</h1>
<p>Acoustic Flatulence Intelligence Platform v3.0<br>Upload a .wav β€” get a FartScore, MIDI, and Solana wallet.</p>
</header>
<div class="actions">
<button onclick="document.getElementById('fileInput').click()">πŸ“ Upload WAV</button>
<button class="secondary" onclick="runMock()">🎲 Generate Mock</button>
<button class="secondary" onclick="loadLeaderboard()">πŸ† Leaderboard</button>
</div>
<div class="dropzone" id="dropzone" onclick="document.getElementById('fileInput').click()">
<input type="file" id="fileInput" accept=".wav" onchange="handleFile(this.files[0])">
<div style="font-size: 3rem; margin-bottom: 8px;">🎡</div>
<h3>Drop a WAV file</h3>
<p style="color: var(--text-muted);">or click to browse</p>
</div>
<div class="loading" id="loading">Analyzing...</div>
<div class="result" id="result">
<h3>Analysis Result <span class="badge" id="scoreBadge">FartScore: 0</span></h3>
<div class="grid" id="grid"></div>
<div class="notes" id="notes"></div>
<div style="margin-top: 12px;">
<a id="midiLink" href="#" style="color: var(--accent); text-decoration: none;">⬇ Download MIDI</a>
</div>
</div>
<div class="leaderboard" id="leaderboard" style="display:none;">
<h3>πŸ† Leaderboard</h3>
<div id="lbList"></div>
</div>
</div>
<script>
const $ = id => document.getElementById(id);
async function handleFile(file) {
if (!file) return;
$('loading').style.display = 'block';
$('result').classList.remove('show');
const fd = new FormData();
fd.append('file', file);
try {
const r = await fetch('/api/analyze', { method: 'POST', body: fd });
const data = await r.json();
showResult(data);
} catch (e) { alert('Error: ' + e.message); }
$('loading').style.display = 'none';
}
async function runMock() {
$('loading').style.display = 'block';
$('result').classList.remove('show');
try {
const r = await fetch('/api/mock', { method: 'POST' });
const data = await r.json();
showResult(data);
} catch (e) { alert('Error: ' + e.message); }
$('loading').style.display = 'none';
}
function showResult(data) {
$('scoreBadge').textContent = 'FartScore: ' + data.fartscore;
$('grid').innerHTML = `
<div class="kv"><span class="key">Duration</span><span>${data.duration_sec}s</span></div>
<div class="kv"><span class="key">Sample Rate</span><span>${data.sample_rate} Hz</span></div>
<div class="kv"><span class="key">Frames</span><span>${data.frame_count}</span></div>
<div class="kv"><span class="key">Notes</span><span>${data.note_count}</span></div>
<div class="kv"><span class="key">Solana Pub</span><span style="font-size:0.78rem;">${data.solana_public.substring(0,24)}...</span></div>
<div class="kv"><span class="key">Audio Hash</span><span style="font-size:0.78rem;">${data.audio_hash.substring(0,24)}...</span></div>
`;
$('notes').innerHTML = (data.notes || []).map(n =>
`<div class="note-row"><span>${n.start}s</span><span>${n.dur}s</span><span>MIDI ${n.note}</span><span>vel ${n.vel}</span></div>`
).join('');
$('midiLink').href = '/api/midi/' + data.audio_hash.substring(0,16);
$('result').classList.add('show');
}
async function loadLeaderboard() {
try {
const r = await fetch('/api/leaderboard');
const data = await r.json();
$('lbList').innerHTML = data.map((row, i) =>
`<div class="lb-row"><span>#${i+1} ${row.id}</span><span class="score">${row.fartscore}</span></div>`
).join('');
$('leaderboard').style.display = 'block';
} catch (e) { console.error(e); }
}
const dz = $('dropzone');
dz.addEventListener('dragover', e => { e.preventDefault(); dz.classList.add('dragover'); });
dz.addEventListener('dragleave', () => dz.classList.remove('dragover'));
dz.addEventListener('drop', e => {
e.preventDefault(); dz.classList.remove('dragover');
const f = e.dataTransfer.files[0];
if (f && f.name.endsWith('.wav')) handleFile(f);
});
</script>
</body>
</html>