1import Link from 'next/link';2import type { ReactNode } from 'react';3import { cn } from '@/lib/format';45export interface Crumb {6 label: string;7 href?: string;8}910export function Breadcrumbs({ items, className }: { items: Crumb[]; className?: string }) {11 if (!items.length) return null;12 return (13 <nav aria-label="Breadcrumb" className={cn('mb-2 text-[11px] text-subtle', className)}>14 <ol className="flex flex-wrap items-center gap-1">15 {items.map((c, i) => (16 <li key={`${c.label}-${i}`} className="flex items-center gap-1">17 {i > 0 ? <span aria-hidden>/</span> : null}18 {c.href && i < items.length - 1 ? (19 <Link href={c.href} className="hover:text-fg">20 {c.label}21 </Link>22 ) : (23 <span className={i === items.length - 1 ? 'text-muted' : undefined}>{c.label}</span>24 )}25 </li>26 ))}27 </ol>28 </nav>29 );30}3132export function PageHeader({ title, description, crumbs, actions, meta, className, compact }: { title: ReactNode; description?: ReactNode; crumbs?: Crumb[]; actions?: ReactNode; meta?: ReactNode; className?: string; compact?: boolean }) {33 return (34 <div className={cn(compact ? 'mb-4' : 'mb-6', className)}>35 {crumbs ? <Breadcrumbs items={crumbs} /> : null}36 <div className="flex flex-wrap items-end justify-between gap-3">37 <div className="min-w-0">38 <h1 className={cn('font-semibold tracking-tight text-fg', compact ? 'text-xl' : 'text-2xl sm:text-3xl')}>{title}</h1>39 {description ? <p className="mt-1 max-w-3xl text-sm leading-relaxed text-muted">{description}</p> : null}40 {meta ? <div className="mt-2 flex flex-wrap items-center gap-2 text-xs text-muted">{meta}</div> : null}41 </div>42 {actions ? <div className="flex shrink-0 items-center gap-2">{actions}</div> : null}43 </div>44 </div>45 );46}47