Download frontend/src/components/ModelCard.tsx from vipan-kumar/Audio_Deepfake_Detection: direct link, hf CLI and curl.
- Browser
- Download file 8.31 kB
-
https://huggingface.co/spaces/vipan-kumar/Audio_Deepfake_Detection/resolve/main/frontend/src/components/ModelCard.tsx
- Command line
-
hf download hf://spaces/vipan-kumar/Audio_Deepfake_Detection/frontend/src/components/ModelCard.tsx
-
curl -L -o ModelCard.tsx https://huggingface.co/spaces/vipan-kumar/Audio_Deepfake_Detection/resolve/main/frontend/src/components/ModelCard.tsx
8.31 kB
| import { motion } from "framer-motion"; | |
| import { | |
| ChevronDown, | |
| ChevronUp, | |
| AlertTriangle, | |
| CheckCircle2, | |
| Clock, | |
| Cpu, | |
| Beaker, | |
| Sparkle, | |
| } from "lucide-react"; | |
| import { useState } from "react"; | |
| import type { ModelResult } from "@/types/inference"; | |
| import { | |
| classifyStatus, | |
| cn, | |
| confidenceColor, | |
| formatMs, | |
| formatPercent, | |
| formatParams, | |
| } from "@/lib/utils"; | |
| import ConfidenceGauge from "./ConfidenceGauge"; | |
| interface Props { | |
| result: ModelResult; | |
| } | |
| const PAPER_IDS = new Set([ | |
| "nes2net", | |
| "sonar", | |
| "bicrossmamba_st", | |
| "voiceradar", | |
| "holi_antispoof", | |
| ]); | |
| export default function ModelCard({ result }: Props) { | |
| const [open, setOpen] = useState(false); | |
| const isFake = result.prediction === "fake"; | |
| const status = classifyStatus(result.status); | |
| const confColor = confidenceColor(result.confidence, result.prediction); | |
| const isPaper = PAPER_IDS.has(result.model_id); | |
| const ribbon = isPaper ? "bg-warn" : "bg-cyber"; | |
| const familyLabel = isPaper ? "paper architecture" : "production"; | |
| const FamilyIcon = isPaper ? Beaker : Sparkle; | |
| return ( | |
| <motion.div | |
| layout | |
| initial={{ opacity: 0, y: 8 }} | |
| animate={{ opacity: 1, y: 0 }} | |
| transition={{ duration: 0.3 }} | |
| className={cn( | |
| "panel relative flex h-full flex-col overflow-hidden p-4 pl-5", | |
| isFake ? "shadow-danger" : "shadow-cyber" | |
| )} | |
| > | |
| {/* Family ribbon along the left edge */} | |
| <div className={cn("absolute left-0 top-0 h-full w-1.5", ribbon, "opacity-70")} /> | |
| {/* Soft accent glow */} | |
| <div | |
| className={cn( | |
| "pointer-events-none absolute -right-16 -top-16 h-36 w-36 rounded-full blur-3xl", | |
| isFake ? "bg-danger/20" : "bg-cyber/15" | |
| )} | |
| /> | |
| {/* ββ Header ββββββββββββββββββββββββββββββββββββββββββββββββββ */} | |
| <div className="flex items-start justify-between gap-2"> | |
| <div className="min-w-0 flex-1"> | |
| <span className="font-display text-base font-semibold text-ink truncate block"> | |
| {result.display_name} | |
| </span> | |
| <div className="mt-0.5 flex flex-wrap items-center gap-x-2 gap-y-0.5 text-[11px] text-ink-dim"> | |
| <span | |
| className={cn( | |
| "inline-flex items-center gap-1 font-mono uppercase", | |
| isPaper ? "text-warn" : "text-cyber" | |
| )} | |
| > | |
| <FamilyIcon className="h-3 w-3" /> {familyLabel} | |
| </span> | |
| <span className="text-ink-mute">Β·</span> | |
| <span className="truncate">{result.paradigm}</span> | |
| </div> | |
| </div> | |
| <div | |
| className={cn( | |
| "shrink-0 rounded-md border px-2 py-1 font-mono text-xs font-bold", | |
| isFake | |
| ? "border-danger/40 bg-danger/10 text-danger" | |
| : "border-cyber/40 bg-cyber/10 text-cyber" | |
| )} | |
| > | |
| {result.prediction.toUpperCase()} | |
| </div> | |
| </div> | |
| {/* ββ Status pill row βββββββββββββββββββββββββββββββββββββββββ */} | |
| <div className="mt-2 flex flex-wrap items-center gap-1.5"> | |
| <span | |
| className={cn( | |
| "inline-flex items-center gap-1 rounded-full px-2 py-0.5 font-mono text-[10px] uppercase tracking-wider", | |
| status === "live" && "bg-cyber/15 text-cyber", | |
| status === "fallback" && "bg-warn/15 text-warn", | |
| status === "error" && "bg-danger/15 text-danger", | |
| status === "loading" && "bg-surface-alt text-ink-dim" | |
| )} | |
| title={result.status} | |
| > | |
| {status === "live" && <CheckCircle2 className="h-2.5 w-2.5" />} | |
| {status === "fallback" && <AlertTriangle className="h-2.5 w-2.5" />} | |
| {status} | |
| </span> | |
| <span className="inline-flex items-center gap-1 rounded-full border border-line bg-surface-alt/60 px-2 py-0.5 font-mono text-[10px] text-ink-dim"> | |
| <Clock className="h-2.5 w-2.5" /> {formatMs(result.inference_time_ms)} | |
| </span> | |
| <span className="inline-flex items-center gap-1 rounded-full border border-line bg-surface-alt/60 px-2 py-0.5 font-mono text-[10px] text-ink-dim"> | |
| <Cpu className="h-2.5 w-2.5" /> {formatParams(result.params_k)} | |
| </span> | |
| </div> | |
| {/* ββ Gauge + numbers ββββββββββββββββββββββββββββββββββββββββ */} | |
| <div className="mt-3 grid grid-cols-[auto,1fr] gap-4"> | |
| <ConfidenceGauge value={result.confidence} prediction={result.prediction} /> | |
| <div className="flex flex-col justify-center gap-1 font-mono text-[11px]"> | |
| <div className="flex items-baseline gap-2"> | |
| <span className="text-ink-mute">confidence</span> | |
| <span className={cn("text-base font-bold", confColor)}> | |
| {formatPercent(result.confidence)} | |
| </span> | |
| </div> | |
| {result.eer_reference?.df21 != null && ( | |
| <div className="text-ink-dim"> | |
| DF21 EER:{" "} | |
| <span className="text-ink">{result.eer_reference.df21}%</span> | |
| </div> | |
| )} | |
| {result.eer_reference?.in_the_wild != null && ( | |
| <div className="text-ink-dim"> | |
| ITW EER:{" "} | |
| <span className="text-ink"> | |
| {result.eer_reference.in_the_wild}% | |
| </span> | |
| </div> | |
| )} | |
| {result.eer_reference?.la21 != null && ( | |
| <div className="text-ink-dim"> | |
| LA21 EER:{" "} | |
| <span className="text-ink">{result.eer_reference.la21}%</span> | |
| </div> | |
| )} | |
| </div> | |
| </div> | |
| {/* Spacer pushes the details block to the bottom for visual alignment */} | |
| <div className="flex-1" /> | |
| {/* ββ Status detail strip ββββββββββββββββββββββββββββββββββββ */} | |
| {status === "fallback" && ( | |
| <div className="mt-3 flex items-start gap-2 rounded-md border border-warn/30 bg-warn/5 p-2 text-[11px] text-warn"> | |
| <AlertTriangle className="mt-0.5 h-3 w-3 shrink-0" /> | |
| <span> | |
| No checkpoint β routed to{" "} | |
| <span className="font-bold">{result.fallback_to ?? "fallback"}</span>. | |
| Drop weights in{" "} | |
| <code className="font-mono">backend/checkpoints/</code> to go live. | |
| </span> | |
| </div> | |
| )} | |
| {/* ββ Why? expander βββββββββββββββββββββββββββββββββββββββββ */} | |
| <button | |
| onClick={() => setOpen((v) => !v)} | |
| className="mt-3 inline-flex items-center gap-1 self-start font-mono text-[11px] text-ink-dim transition hover:text-cyber" | |
| > | |
| {open ? "hide details" : "details"} | |
| {open ? ( | |
| <ChevronUp className="h-3 w-3" /> | |
| ) : ( | |
| <ChevronDown className="h-3 w-3" /> | |
| )} | |
| </button> | |
| {open && ( | |
| <motion.div | |
| initial={{ opacity: 0, height: 0 }} | |
| animate={{ opacity: 1, height: "auto" }} | |
| className="mt-2 overflow-hidden border-t border-line pt-2 text-[11px] leading-relaxed text-ink-dim" | |
| > | |
| {result.notes && ( | |
| <p className="mb-2 whitespace-normal">{result.notes}</p> | |
| )} | |
| <FeatureGrid features={result.features} /> | |
| </motion.div> | |
| )} | |
| </motion.div> | |
| ); | |
| } | |
| function FeatureGrid({ features }: { features: Record<string, unknown> }) { | |
| const flat = Object.entries(features).filter( | |
| ([, v]) => | |
| typeof v === "number" || typeof v === "string" || typeof v === "boolean" | |
| ); | |
| if (!flat.length) return null; | |
| return ( | |
| <div className="grid grid-cols-2 gap-x-3 gap-y-1 font-mono text-[10px]"> | |
| {flat.slice(0, 12).map(([k, v]) => ( | |
| <div key={k} className="flex justify-between gap-2 truncate"> | |
| <span className="text-ink-mute truncate">{k}</span> | |
| <span className="text-ink truncate">{String(v)}</span> | |
| </div> | |
| ))} | |
| </div> | |
| ); | |
| } | |