File size: 5,610 Bytes
66b6851
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
import { motion } from 'framer-motion';
import { CheckCircle2, XCircle, TrendingUp, Info } from 'lucide-react';

interface ImageReportPanelProps {
  reasons?: string[];
  perGeneratorAccuracy?: Record<string, { accuracy: string; notes: string }>;
  verdict: string;
}

const GENERATOR_COLORS: Record<string, string> = {
  'ChatGPT': '#22c55e',
  'Adobe': '#22c55e',
  'ProGAN': '#f97316',
  'Stable': '#eab308',
  'SDXL': '#f97316',
  'Midjourney': '#ef4444',
  'FLUX': '#ef4444',
};

function getGeneratorColor(name: string): string {
  const key = Object.keys(GENERATOR_COLORS).find(k => name.includes(k));
  return key ? GENERATOR_COLORS[key] : '#94a3b8';
}

function parseAccuracyValue(accuracy: string): number {
  const match = accuracy.match(/(\d+)/);
  return match ? parseInt(match[1], 10) : 50;
}

export default function ImageReportPanel({ reasons, perGeneratorAccuracy, verdict }: ImageReportPanelProps) {
  return (
    <div className="flex flex-col gap-4">
      {/* Forensic reasons */}
      {reasons && reasons.length > 0 && (
        <div className="rounded-2xl border overflow-hidden shadow-sm" style={{ borderColor: 'var(--panel-border)', background: 'var(--panel-bg)', color: 'var(--text-primary)' }}>
          <div
            className="px-5 py-4 border-b text-xs font-bold tracking-tight uppercase bg-[var(--btn-secondary-bg)]"
            style={{ borderColor: 'var(--panel-border)', color: 'var(--text-primary)' }}
          >
            Logical Arbiter — Forensic Reasoning
          </div>
          <div className="p-3 space-y-2">
            {reasons.map((reason, i) => {
              const isPositive = reason.includes('✓') || reason.includes('○');
              const isNegative = reason.includes('✗');
              const Icon = isPositive ? CheckCircle2 : isNegative ? XCircle : Info;
              const color = isPositive ? '#10b981' : isNegative ? '#ef4444' : '#64748b';
              
              // Clean the text by removing the markers
              const cleanText = reason.replace(/[✓✗○]/g, '').trim();

              return (
                <motion.div
                  key={i}
                  initial={{ opacity: 0, x: -8 }}
                  animate={{ opacity: 1, x: 0 }}
                  transition={{ delay: i * 0.08 }}
                  className="flex items-start gap-4 px-5 py-3.5 rounded-xl text-[11px] font-medium leading-relaxed border"
                  style={{ 
                    background: isPositive ? 'rgba(16,185,129,0.06)' : isNegative ? 'rgba(239,68,68,0.06)' : 'var(--btn-secondary-bg)', 
                    borderColor: isPositive ? 'rgba(16,185,129,0.16)' : isNegative ? 'rgba(239,68,68,0.16)' : 'var(--panel-border)',
                    color: 'var(--text-primary)' 
                  }}
                >
                  <Icon className="w-4 h-4 mt-0.5 shrink-0" style={{ color }} strokeWidth={2.5} />
                  <span className="flex-1">{cleanText}</span>
                </motion.div>
              );
            })}
          </div>
        </div>
      )}

      {/* Per-generator accuracy table */}
      {perGeneratorAccuracy && Object.keys(perGeneratorAccuracy).length > 0 && (
        <div className="rounded-2xl border overflow-hidden shadow-sm" style={{ borderColor: 'var(--panel-border)', background: 'var(--panel-bg)', color: 'var(--text-primary)' }}>
          <div
            className="px-5 py-4 border-b flex items-center gap-2 bg-[var(--btn-secondary-bg)]"
            style={{ borderColor: 'var(--panel-border)' }}
          >
            <TrendingUp className="w-4 h-4 text-cyan-500" />
            <span className="text-xs font-bold tracking-tight uppercase" style={{ color: 'var(--text-primary)' }}>
              Per-Generator Detection Accuracy
            </span>
          </div>
          <div className="p-3 space-y-2">
            {Object.entries(perGeneratorAccuracy).map(([gen, data]) => {
              const pct = parseAccuracyValue(data.accuracy);
              const col = getGeneratorColor(gen);
              return (
                <div key={gen} className="space-y-1">
                  <div className="flex justify-between items-center">
                    <span className="text-[10px] font-bold text-[var(--text-primary)]">{gen}</span>
                    <span className="text-[10px] font-black" style={{ color: col }}>{data.accuracy}</span>
                  </div>
                  <div className="w-full rounded-full overflow-hidden" style={{ height: 4, background: 'rgba(255,255,255,0.06)' }}>
                    <motion.div
                      className="h-full rounded-full"
                      style={{ background: col }}
                      initial={{ width: 0 }}
                      animate={{ width: `${pct}%` }}
                      transition={{ duration: 0.8, ease: 'easeOut' }}
                    />
                  </div>
                  <div className="text-[9px] font-medium text-[var(--text-muted)] mt-1 uppercase tracking-tighter">{data.notes}</div>
                </div>
              );
            })}
          </div>
          <div
            className="px-5 py-3 border-t flex items-start gap-2 text-[10px] font-medium leading-relaxed bg-[var(--btn-secondary-bg)]"
            style={{ borderColor: 'var(--panel-border)', color: 'var(--text-secondary)' }}
          >
            <Info className="w-3.5 h-3.5 shrink-0 mt-0.5 text-amber-500" />
            Accuracy varies by compression, platform re-encoding, and steganographic post-processing artifacts.
          </div>
        </div>
      )}
    </div>
  );
}