import { motion } from 'framer-motion' import { Info } from 'lucide-react' // Define scale and threshold logic for honest scaling const featureConfig = { 'fft hf ratio': { max: 1.0, warning: 0.3, danger: 0.6 }, 'moiré score': { max: 1.0, warning: 0.2, danger: 0.5 }, 'banding': { max: 1.0, warning: 0.2, danger: 0.5 }, 'edge density': { max: 0.5, warning: 0.1, danger: 0.2 }, 'laplacian sharpness': { max: 2000, warning: 500, danger: 1000 }, 'sobel mag': { max: 100, warning: 30, danger: 60 }, 'brightness': { max: 255, warning: 200, danger: 240 }, 'contrast': { max: 100, warning: 70, danger: 90 }, 'glare ratio': { max: 0.1, warning: 0.02, danger: 0.05 }, 'bezel score': { max: 1.0, warning: 0.2, danger: 0.5 }, 'paper texture': { max: 1.0, warning: 0.2, danger: 0.5 }, 'perspective': { max: 1.0, warning: 0.2, danger: 0.5 }, 'blockiness': { max: 1.0, warning: 0.3, danger: 0.6 }, 'compression diff': { max: 1.0, warning: 0.2, danger: 0.4 } } export function FeatureBarChart({ data, finalScore = 0 }) { if (!data || Object.keys(data).length === 0) return null const getInterpretation = (name, val) => { const key = name.toLowerCase() const conf = featureConfig[key] || { warning: 0.3, danger: 0.6, max: 1.0 } // Normalize for bar width const norm = Math.min(Math.max((val / conf.max) * 100, 0), 100) let status = 'normal' if (val >= conf.danger) status = 'suspicious' else if (val >= conf.warning) status = 'elevated' // Contextual downgrades (Honesty pass) // If model predicts real (< 0.5), high CV features are likely natural textures if (status === 'suspicious' && finalScore < 0.5) { status = 'contextual' } // Compression alone shouldn't be strongly suspicious without other cues if (key.includes('block') || key.includes('compress')) { if (status === 'suspicious' && finalScore < 0.65) status = 'elevated' } return { norm, status, confMax: conf.max } } const getStyles = (status) => { switch (status) { case 'suspicious': return { bar: 'bg-danger shadow-[0_0_8px_rgba(239,68,68,0.6)]', text: 'text-danger' } case 'elevated': return { bar: 'bg-warning shadow-[0_0_8px_rgba(245,158,11,0.4)]', text: 'text-warning' } case 'contextual': return { bar: 'bg-zinc-500 shadow-[0_0_8px_rgba(113,113,122,0.4)]', text: 'text-zinc-400' } default: return { bar: 'bg-primary/80 shadow-[0_0_8px_rgba(16,185,129,0.3)]', text: 'text-zinc-300' } } } const getLabel = (status) => { switch(status) { case 'suspicious': return 'Suspicious' case 'elevated': return 'Elevated' case 'contextual': return 'Contextual / Organic' default: return 'Normal / Low' } } return (
Raw: {value} (Scale: 0 - {confMax})
Interpretation: {getLabel(status)}
{status === 'contextual' &&High magnitude, but model context suggests this is natural texture.
} {status === 'suspicious' &&Strong signal contributing to screen probability.
}