ltmarx / web /src /components /ResultCard.tsx
harelcain's picture
Upload 37 files
f2f99a3 verified
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>
);
}