feat(manascore): add Lighthouse score integration

Add optional lighthouse scores (performance, accessibility, best
practices, SEO) to schema. Display as inline badges on overview
and circular gauges on detail page with average score.

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
This commit is contained in:
Till JS 2026-03-24 11:46:16 +01:00
parent e06e8cca59
commit 986f168d32
5 changed files with 98 additions and 0 deletions

View file

@ -203,6 +203,15 @@ const manascoreCollection = defineCollection({
security: z.number().min(0).max(100),
ux: z.number().min(0).max(100),
}),
// Lighthouse scores (0-100, from Google Lighthouse audit)
lighthouse: z
.object({
performance: z.number().min(0).max(100),
accessibility: z.number().min(0).max(100),
bestPractices: z.number().min(0).max(100),
seo: z.number().min(0).max(100),
})
.optional(),
// Score history for trend visualization
history: z
.array(

View file

@ -18,6 +18,11 @@ scores:
documentation: 98
security: 92
ux: 95
lighthouse:
performance: 92
accessibility: 95
bestPractices: 96
seo: 100
status: 'production'
version: '1.1.0'
stats:

View file

@ -18,6 +18,11 @@ scores:
documentation: 95
security: 90
ux: 94
lighthouse:
performance: 90
accessibility: 93
bestPractices: 96
seo: 100
status: 'production'
version: '1.1.0'
stats:

View file

@ -223,6 +223,57 @@ function getBarColor(score: number): string {
</div>
</div>
{/* Lighthouse Scores */}
{
audit.data.lighthouse &&
(() => {
const lh = audit.data.lighthouse;
const lighthouseItems = [
{ key: 'Performance', value: lh.performance, icon: '⚡' },
{ key: 'Accessibility', value: lh.accessibility, icon: '♿' },
{ key: 'Best Practices', value: lh.bestPractices, icon: '✅' },
{ key: 'SEO', value: lh.seo, icon: '🔍' },
];
const avg = Math.round(
(lh.performance + lh.accessibility + lh.bestPractices + lh.seo) / 4
);
return (
<div class="border-border/50 mb-8 rounded-xl border bg-gradient-to-br from-white/5 to-white/[0.02] p-6">
<div class="flex items-center justify-between mb-4">
<div>
<h2 class="text-foreground text-sm font-semibold">Lighthouse Scores</h2>
<p class="text-muted-foreground text-xs">
Google Lighthouse Performance Audit
</p>
</div>
<span class={`text-lg font-bold ${getScoreColor(avg)}`}>Ø {avg}</span>
</div>
<div class="grid grid-cols-2 gap-4 sm:grid-cols-4">
{lighthouseItems.map(({ key, value, icon }) => (
<div class="text-center">
<div
class={`relative mx-auto mb-2 flex h-16 w-16 items-center justify-center rounded-full border-4 ${
value >= 90
? 'border-emerald-500'
: value >= 50
? 'border-yellow-500'
: 'border-red-500'
}`}
>
<span class={`text-lg font-bold ${getScoreColor(value)}`}>{value}</span>
</div>
<span class="text-muted-foreground text-[11px]">
{icon} {key}
</span>
</div>
))}
</div>
</div>
);
})()
}
{/* Stats */}
{
audit.data.stats && (

View file

@ -172,6 +172,34 @@ const statuses = [...new Set(sortedAudits.map((a) => a.data.status))];
))}
</div>
{/* Lighthouse scores */}
{data.lighthouse && (
<div class="mt-2 flex items-center gap-3">
<span class="text-muted-foreground/50 text-[9px]">Lighthouse:</span>
{Object.entries(data.lighthouse).map(([key, value]) => {
const label =
key === 'bestPractices'
? 'BP'
: key === 'performance'
? 'Perf'
: key === 'accessibility'
? 'A11y'
: 'SEO';
const color =
value >= 90
? 'text-emerald-500'
: value >= 50
? 'text-yellow-500'
: 'text-red-500';
return (
<span class={`text-[10px] font-medium ${color}`} title={key}>
{label}: {value}
</span>
);
})}
</div>
)}
{/* Stats row */}
{data.stats && (
<div class="text-muted-foreground/60 mt-2 flex flex-wrap gap-3 text-[10px]">