"use client";
import clsx from "clsx";
import type { ReactNode } from "react";
import { TYPE_COLORS } from "@/lib/format";
export function Panel({ title, sub, right, children, className, pad = true }: { title?: ReactNode; sub?: ReactNode; right?: ReactNode; children: ReactNode; className?: string; pad?: boolean }) {
return (
{(title || right) && (
{title &&
{title}
}
{sub &&
{sub}
}
{right && {right}
}
)}
{children}
);
}
export function Kpi({ label, value, hint, accent = "cyan", spark }: { label: string; value: ReactNode; hint?: ReactNode; accent?: "cyan" | "amber" | "green" | "violet" | "pink" | "red" | "fg"; spark?: number[] }) {
const color = { cyan: "text-cyan", amber: "text-amber", green: "text-green", violet: "text-violet", pink: "text-pink", red: "text-red", fg: "text-fg" }[accent];
return (
{label}
{value}
{hint &&
{hint}
}
{spark && spark.length > 1 &&
}
);
}
export function Spark({ values, className, color = "currentColor" }: { values: number[]; className?: string; color?: string }) {
const max = Math.max(1, ...values);
const w = 100;
const h = 30;
const pts = values.map((v, i) => `${(i / Math.max(1, values.length - 1)) * w},${h - (v / max) * (h - 2) - 1}`).join(" ");
return (
);
}
export function Tag({ children, color, className, title }: { children: ReactNode; color?: string; className?: string; title?: string }) {
const c = color ?? "#6b7a8c";
return (
{children}
);
}
export function TypeTag({ type }: { type: string }) {
return {type};
}
export function SurfaceChips({ provenance }: { provenance?: { surface: string; confidence: number; detail?: string }[] | null }) {
if (!provenance?.length) return —;
const best = new Map();
for (const p of provenance) if (!best.has(p.surface) || best.get(p.surface)!.confidence < p.confidence) best.set(p.surface, p);
const colors: Record = { network: "#38e1ff", dom: "#43e69a", visual: "#ff7ad9", media: "#9d7bff", accessibility: "#ffb347", navigation: "#6b7a8c" };
return (
{[...best.entries()].map(([s, p]) => (
{s} {Math.round(p.confidence * 100)}
))}
);
}
export function Bar({ value, color = "#38e1ff", className }: { value: number; color?: string; className?: string }) {
return (
);
}
export function Empty({ children }: { children: ReactNode }) {
return {children}
;
}
export function Skeleton({ rows = 4 }: { rows?: number }) {
return (
{Array.from({ length: rows }).map((_, i) => (
))}
);
}
export function Table({ head, children, dense }: { head: ReactNode[]; children: ReactNode; dense?: boolean }) {
return (
{head.map((h, i) => (
|
{h}
|
))}
{children}
);
}
export function Metric({ label, value }: { label: string; value: ReactNode }) {
return (
{label}
{value}
);
}
export function StatusPill({ status }: { status: string }) {
const map: Record = { running: "#ffb347", healthy: "#ffb347", queued: "#6b7a8c", done: "#43e69a", stopped: "#6b7a8c", failed: "#ff5c7a", auth_required: "#ff5c7a", crashed: "#ff5c7a", degraded: "#ff5c7a" };
return (
{status === "running" || status === "healthy" ? : null}
{status}
);
}