(function() { const app = document.getElementById('app'); // Router function route() { const path = window.location.pathname; if (path === '/' || path === '/sessions') { renderSessions(); } else if (path.startsWith('/sessions/')) { const sessionID = path.split('/sessions/')[1]; renderSession(sessionID); } else if (path.startsWith('/runs/')) { const runID = path.split('/runs/')[1]; renderRun(runID); } else { app.innerHTML = '
Page not found
'; } } function navigate(path) { history.pushState(null, '', path); route(); } window.addEventListener('popstate', route); // API helpers async function api(path) { const resp = await fetch('/api' + path); if (!resp.ok) throw new Error('API error'); return resp.json(); } function relativeTime(ts) { const now = Date.now(); const then = new Date(ts).getTime(); const diff = now - then; if (diff < 60000) return 'just now'; if (diff < 3600000) return Math.floor(diff / 60000) + 'm ago'; if (diff < 86400000) return Math.floor(diff / 3600000) + 'h ago'; return Math.floor(diff / 86400000) + 'd ago'; } function formatDuration(ms) { if (!ms) return '-'; if (ms < 1000) return ms + 'ms'; if (ms < 60000) return (ms / 1000).toFixed(1) + 's'; return (ms / 60000).toFixed(1) + 'm'; } function statusIcon(status) { if (status === 'success') return '✓'; if (status === 'error') return '✗'; return '●'; } // Sessions list let sessionsState = { sessions: [], cursor: null, filters: {} }; async function renderSessions() { app.innerHTML = `| Session | Framework | Host | Runs | Time |
|---|
| Run ID | Status | Spans | Duration | Started |
|---|---|---|---|---|
| ${r.run_id.substring(0, 12)}... | ${statusIcon(r.status)} ${r.status} | ${r.span_count} | ${dur} | ${new Date(r.started_at).toLocaleTimeString()} |
| No runs | ||||
| Name | Kind | Status | Duration |
|---|---|---|---|
| ${sp.name} | ${sp.kind} | ${statusIcon(sp.status)} | ${formatDuration(sp.duration_ms)} |
| No spans | |||