HTML 77.2%
TypeScript 10.5%
Python 9.6%
JavaScript 2.5%
1import { cn } from '@/lib/cn';2import { IMPORTANCE_LABELS, OPENNESS_LABELS, STATUS_LABELS, TIER_LABELS, TYPE_COLOR_KEY, typeLabel } from '@/lib/site';34const base = 'inline-flex items-center gap-1 whitespace-nowrap rounded-[3px] px-1.5 py-[1px] text-[11px] font-medium leading-4 tracking-wide';56/** Entity-type badge: coloured text + faint tint, letter-spaced. `small` variant for dense feeds. */7export function EntityBadge({ type, className, small = false }: { type: string; className?: string; small?: boolean }) {8 const key = TYPE_COLOR_KEY[type] ?? 'tool';9 return (10 <span className={cn(base, small && 'px-1 text-[10px]', 'uppercase', className)} style={{ color: `var(--type-${key})`, background: `color-mix(in srgb, var(--type-${key}) 10%, transparent)` }}>11 {typeLabel(type)}12 </span>13 );14}1516/** Source tier 1–4 (official → unverified). */17export function TierBadge({ tier, className, withLabel = false }: { tier: number | null | undefined; className?: string; withLabel?: boolean }) {18 if (tier === null || tier === undefined) return <span className={cn(base, 'text-ink-3', className)}>tier —</span>;19 const t = Math.min(4, Math.max(1, Math.round(tier)));20 return (21 <span className={cn(base, 'mono', className)} style={{ color: `var(--tier-${t})`, background: `color-mix(in srgb, var(--tier-${t}) 12%, transparent)` }} title={TIER_LABELS[t]}>22 T{t}23 {withLabel && <span className="font-sans normal-case tracking-normal">· {TIER_LABELS[t]}</span>}24 </span>25 );26}2728const CONF: Record<string, string> = { verified: 'text-positive bg-positive-soft', high: 'text-positive bg-positive-soft', medium: 'text-accent bg-accent-soft', low: 'text-warning bg-warning-soft', conflicted: 'text-danger bg-danger-soft' };29export function ConfidenceBadge({ confidence, className }: { confidence: string | null | undefined; className?: string }) {30 if (!confidence) return null;31 return <span className={cn(base, CONF[confidence] ?? 'text-ink-3 bg-surface-2', className)}>{confidence}</span>;32}3334const STATUS: Record<string, string> = {35 active: 'text-positive bg-positive-soft',36 preview: 'text-accent bg-accent-soft',37 announced: 'text-accent bg-accent-soft',38 'limited-availability': 'text-warning bg-warning-soft',39 deprecated: 'text-warning bg-warning-soft',40 retired: 'text-danger bg-danger-soft',41};42export function StatusBadge({ status, className }: { status: string | null | undefined; className?: string }) {43 if (!status || status === 'unknown') return null;44 return <span className={cn(base, STATUS[status] ?? 'text-ink-2 bg-surface-2', className)}>{STATUS_LABELS[status] ?? status}</span>;45}4647const OPEN: Record<string, string> = { 'open-weights': 'text-positive bg-positive-soft', 'open-source': 'text-positive bg-positive-soft', proprietary: 'text-ink-2 bg-surface-2', restricted: 'text-warning bg-warning-soft' };48export function OpennessBadge({ openness, className }: { openness: string | null | undefined; className?: string }) {49 if (!openness) return null;50 return <span className={cn(base, OPEN[openness] ?? 'text-ink-2 bg-surface-2', className)}>{OPENNESS_LABELS[openness] ?? openness}</span>;51}5253/** Importance 0–3 as a small stepped meter (■■■□). */54export function ImportanceMark({ importance, className }: { importance: number; className?: string }) {55 const n = Math.min(3, Math.max(0, importance));56 return (57 <span className={cn('inline-flex items-center gap-[2px]', className)} title={`${IMPORTANCE_LABELS[n]} (importance ${n})`} aria-label={`Importance ${n} of 3`}>58 {[1, 2, 3].map((i) => (59 <span key={i} className={cn('block h-2 w-[3px] rounded-[1px]', i <= n ? (n === 3 ? 'bg-accent-2' : 'bg-ink-2') : 'bg-rule-strong')} />60 ))}61 </span>62 );63}6465/** Generic neutral chip (tags, families, modalities). */66export function Chip({ children, className, tone = 'neutral' }: { children: React.ReactNode; className?: string; tone?: 'neutral' | 'accent' | 'estimated' }) {67 return (68 <span className={cn(base, tone === 'neutral' && 'bg-surface-2 text-ink-2', tone === 'accent' && 'bg-accent-soft text-accent', tone === 'estimated' && 'border border-dashed border-warning/60 text-warning', className)}>{children}</span>69 );70}7172/** Derived/estimated label linking to methodology. */73export function Estimated({ className }: { className?: string }) {74 return (75 <a href="/methodology#estimates" className={cn(base, 'border border-dashed border-warning/60 uppercase text-warning hover:bg-warning-soft', className)}>76 Estimated77 </a>78 );79}80