Files
speedboard/views/site.pug
Malin 280e5f133f feat: initial Speedboard implementation
sitespeed.io web UI with Express/Pug/SQLite — port 3132.
Includes job queue, SSE live log, full metrics dashboard,
site history, CO2/axe/CWV sections, and Docker support.

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-04-06 19:36:13 +02:00

79 lines
3.5 KiB
Plaintext

extends layout
block content
div(class='space-y-6')
div(class='flex items-start justify-between gap-4 flex-wrap')
div
h1(class='text-xl font-bold break-all')= url
p(class='text-sm text-gray-400 mt-1') #{jobs.length} completed test(s) on record
a(href=`/?url=${encodeURIComponent(url)}` class='bg-indigo-600 text-white text-sm px-4 py-2 rounded hover:bg-indigo-700') + Retest
if jobs.length === 0
div(class='text-center py-16 text-gray-400') No completed tests found for this URL.
else
//- LCP trend chart
div(class='bg-white border border-gray-200 rounded-xl p-5')
h2(class='text-base font-semibold mb-3') LCP Trend (ms)
canvas#lcpChart(height='80')
//- Timeline table
div(class='overflow-x-auto')
table(class='w-full bg-white border border-gray-200 rounded-xl overflow-hidden text-sm')
thead(class='bg-gray-50 text-gray-500 text-xs uppercase')
tr
th(class='px-4 py-3 text-left') Date
th(class='px-4 py-3 text-center') Browser
th(class='px-4 py-3 text-right') LCP
th(class='px-4 py-3 text-right') FCP
th(class='px-4 py-3 text-right') TBT
th(class='px-4 py-3 text-right') Speed Index
th(class='px-4 py-3 text-right') Perf Score
th(class='px-4 py-3 text-right') Transfer
th
tbody
each job in jobs
tr(class='border-t border-gray-100 hover:bg-gray-50')
td(class='px-4 py-3 text-gray-500 text-xs')= new Date(job.finished_at).toLocaleString()
td(class='px-4 py-3 text-center text-gray-600 text-xs')= job.browser + (job.mobile ? ' 📱' : '')
td(class='px-4 py-3 text-right')= job.lcp ? (job.lcp/1000).toFixed(2)+'s' : '—'
td(class='px-4 py-3 text-right')= job.fcp ? (job.fcp/1000).toFixed(2)+'s' : '—'
td(class='px-4 py-3 text-right')= job.tbt ? job.tbt.toFixed(0)+'ms' : '—'
td(class='px-4 py-3 text-right')= job.speed_index ? job.speed_index.toFixed(0) : '—'
td(class='px-4 py-3 text-right')
if job.score_performance
span(class=`font-bold ${job.score_performance >= 90 ? 'text-green-600' : job.score_performance >= 50 ? 'text-yellow-600' : 'text-red-600'}`)
= job.score_performance
else
= '—'
td(class='px-4 py-3 text-right text-xs')
= job.transfer_total ? (job.transfer_total/1024).toFixed(0)+'KB' : '—'
td(class='px-4 py-3')
a(href=`/results/${job.id}` class='text-indigo-600 hover:underline text-xs') View
script(src='https://cdn.jsdelivr.net/npm/chart.js@4/dist/chart.umd.min.js')
script.
const jobs = !{JSON.stringify(jobs.map(j => ({ date: j.finished_at, lcp: j.lcp })))};
const labels = jobs.map(j => new Date(j.date).toLocaleDateString()).reverse();
const data = jobs.map(j => j.lcp || null).reverse();
new Chart(document.getElementById('lcpChart'), {
type: 'line',
data: {
labels,
datasets: [{
label: 'LCP (ms)',
data,
borderColor: '#6366f1',
backgroundColor: 'rgba(99,102,241,0.1)',
tension: 0.3,
fill: true,
pointRadius: 4,
}]
},
options: {
responsive: true,
plugins: { legend: { display: false } },
scales: { y: { beginAtZero: true } }
}
});