Spaces:
Paused
Paused
Nawaz-khan-droid
build: finalize stratos refactor, textarea hardening, and payload limit alignment
acf82bb | 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> | |
| ); | |
| } | |