Spaces:
Running
Running
| import type { DetectionResult, PresetName } from '@core/types.js'; | |
| interface ResultCardProps { | |
| result: DetectionResult | null; | |
| presetUsed?: PresetName | null; | |
| loading?: boolean; | |
| } | |
| export default function ResultCard({ result, presetUsed, loading }: ResultCardProps) { | |
| if (loading) { | |
| return ( | |
| <div className="rounded-xl border border-zinc-800/50 bg-zinc-900/50 p-6"> | |
| <div className="flex items-center gap-3"> | |
| <div className="h-5 w-5 animate-spin rounded-full border-2 border-zinc-600 border-t-blue-500" /> | |
| <span className="text-sm text-zinc-400">Analyzing frames...</span> | |
| </div> | |
| </div> | |
| ); | |
| } | |
| if (!result) return null; | |
| const payloadHex = result.payload | |
| ? Array.from(result.payload).map((b) => b.toString(16).padStart(2, '0')).join('').toUpperCase() | |
| : '—'; | |
| return ( | |
| <div | |
| className={`rounded-xl border p-6 transition-colors ${ | |
| result.detected | |
| ? 'border-emerald-800/50 bg-emerald-950/30' | |
| : 'border-amber-800/50 bg-amber-950/20' | |
| }`} | |
| > | |
| <div className="flex items-start justify-between"> | |
| <div> | |
| <h3 className={`text-sm font-semibold ${result.detected ? 'text-emerald-400' : 'text-amber-400'}`}> | |
| {result.detected ? 'Watermark Detected' : 'No Watermark Found'} | |
| </h3> | |
| {result.detected && ( | |
| <div className="mt-4 space-y-3"> | |
| <div> | |
| <p className="text-[10px] uppercase tracking-wider text-zinc-500">Payload</p> | |
| <p className="mt-0.5 font-mono text-lg tracking-wide text-zinc-100">{payloadHex}</p> | |
| </div> | |
| <div className="flex gap-6"> | |
| <div> | |
| <p className="text-[10px] uppercase tracking-wider text-zinc-500">Confidence</p> | |
| <div className="mt-1 flex items-center gap-2"> | |
| <div className="h-1.5 w-24 overflow-hidden rounded-full bg-zinc-800"> | |
| <div | |
| className="h-full rounded-full bg-emerald-500 transition-all" | |
| style={{ width: `${result.confidence * 100}%` }} | |
| /> | |
| </div> | |
| <span className="text-xs tabular-nums text-zinc-400"> | |
| {(result.confidence * 100).toFixed(1)}% | |
| </span> | |
| </div> | |
| </div> | |
| <div> | |
| <p className="text-[10px] uppercase tracking-wider text-zinc-500">Tiles used</p> | |
| <p className="mt-0.5 text-sm tabular-nums text-zinc-300"> | |
| {result.tilesDecoded} of {result.tilesTotal} available | |
| </p> | |
| </div> | |
| {presetUsed && ( | |
| <div> | |
| <p className="text-[10px] uppercase tracking-wider text-zinc-500">Preset</p> | |
| <p className="mt-0.5 text-sm capitalize text-zinc-300"> | |
| {presetUsed} | |
| </p> | |
| </div> | |
| )} | |
| </div> | |
| </div> | |
| )} | |
| </div> | |
| <div | |
| className={`flex h-10 w-10 items-center justify-center rounded-full ${ | |
| result.detected ? 'bg-emerald-500/10' : 'bg-amber-500/10' | |
| }`} | |
| > | |
| {result.detected ? ( | |
| <svg className="h-5 w-5 text-emerald-400" fill="none" viewBox="0 0 24 24" stroke="currentColor" strokeWidth={2}> | |
| <path strokeLinecap="round" strokeLinejoin="round" d="M9 12l2 2 4-4m5.618-4.016A11.955 11.955 0 0112 2.944a11.955 11.955 0 01-8.618 3.04A12.02 12.02 0 003 9c0 5.591 3.824 10.29 9 11.622 5.176-1.332 9-6.03 9-11.622 0-1.042-.133-2.052-.382-3.016z" /> | |
| </svg> | |
| ) : ( | |
| <svg className="h-5 w-5 text-amber-400" fill="none" viewBox="0 0 24 24" stroke="currentColor" strokeWidth={2}> | |
| <path strokeLinecap="round" strokeLinejoin="round" d="M12 9v2m0 4h.01m-6.938 4h13.856c1.54 0 2.502-1.667 1.732-2.5L13.732 4.5c-.77-.833-2.694-.833-3.464 0L3.34 16.5c-.77.833.192 2.5 1.732 2.5z" /> | |
| </svg> | |
| )} | |
| </div> | |
| </div> | |
| </div> | |
| ); | |
| } | |