cawncade-ai / frontend /src /components /VisualLens.jsx
Nawaz-khan-droid
build: finalize stratos refactor, textarea hardening, and payload limit alignment
acf82bb
Raw
History Blame Contribute Delete
3.49 kB
import React from 'react';
import { Shield, ShieldAlert, ShieldCheck, Loader2, Image as ImageIcon } from 'lucide-react';
export default function VisualLens({ result, isLoading }) {
if (isLoading) {
return (
<div className="glass-card p-12 text-center">
<Loader2 className="mx-auto mb-4 h-8 w-8 animate-spin text-sky-400" />
<p className="text-slate-400">Analyzing image with AI vision model...</p>
</div>
);
}
if (!result) {
return (
<div className="glass-card space-y-4 p-12 text-center">
<ImageIcon className="mx-auto h-12 w-12 text-slate-600" />
<div>
<h3 className="text-lg font-medium text-slate-200">Visual Lens</h3>
<p className="mt-2 text-slate-500">Upload an image to detect AI-generated content or deepfakes.</p>
</div>
</div>
);
}
const getStatusConfig = (label) => {
const l = label?.toLowerCase() || '';
if (l.includes('real')) return { icon: ShieldCheck, color: 'text-emerald-400', bg: 'bg-emerald-500/10', label: 'Verified Real' };
if (l.includes('deepfake') || l.includes('morphed')) return { icon: ShieldAlert, color: 'text-rose-400', bg: 'bg-rose-500/10', label: 'Manipulation Detected' };
return { icon: Shield, color: 'text-sky-400', bg: 'bg-sky-500/10', label: 'AI-Generated' };
};
const status = getStatusConfig(result.label);
const confidence = (result.score || 0) * 100;
const predictions = Array.isArray(result.all_predictions) ? result.all_predictions.filter(Boolean).slice(0, 5) : [];
return (
<div className="glass-card animate-fade-in space-y-6 p-6">
<div className="flex items-start justify-between">
<div className="flex items-center gap-3">
<div className={`rounded-xl p-3 ${status.bg}`}>
<status.icon className={`h-6 w-6 ${status.color}`} />
</div>
<div>
<h3 className="text-lg font-bold text-white">{status.label}</h3>
<p className="text-xs text-slate-500">Classification</p>
</div>
</div>
<div className="text-right">
<span className={`text-xl font-bold ${status.color}`}>{confidence.toFixed(1)}%</span>
</div>
</div>
{predictions.length > 0 && (
<div className="space-y-3 border-t border-white/5 pt-4">
<h4 className="text-xs font-semibold uppercase text-slate-500">Detection Breakdown</h4>
<div className="space-y-3">
{predictions.map((pred, i) => {
const pLabel = typeof pred === 'object' ? pred.label : pred[0];
const rawScore = typeof pred === 'object' ? pred.score : pred[1];
const pScore = (Number(rawScore) || 0) * 100;
return (
<div key={i} className="space-y-1">
<div className="flex justify-between text-[11px]">
<span className="text-slate-400">{pLabel}</span>
<span className="text-slate-500">{pScore.toFixed(1)}%</span>
</div>
<div className="h-1 w-full rounded-full bg-white/5">
<div
className="h-full rounded-full bg-gradient-to-r from-sky-500 to-emerald-500 transition-all duration-700"
style={{ width: `${pScore}%` }}
/>
</div>
</div>
);
})}
</div>
</div>
)}
</div>
);
}