File size: 3,053 Bytes
af37c1f
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
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
import React from 'react'
import { motion } from 'framer-motion'
import type { PyTorchScore } from '../types'

interface Props {
  scores: PyTorchScore[]
}

// Convert 0.0 -> 1.0 urgency score into a color.
function getHeatmapColor(score: number, isFalseSos: boolean) {
  if (isFalseSos || score === 0) {
    return 'bg-purple-900/50 text-purple-300 neon-border-purple'
  }
  
  if (score > 0.75) return 'bg-red-900/50 text-red-300 neon-border-red'
  if (score > 0.4) return 'bg-orange-900/50 border-orange-500 text-orange-300'
  if (score > 0.1) return 'bg-yellow-900/50 border-yellow-500 text-yellow-300'
  
  return 'bg-green-900/30 text-green-500 neon-border-green'
}

const containerVariants = {
  hidden: { opacity: 0 },
  visible: {
    opacity: 1,
    transition: {
      staggerChildren: 0.05
    }
  }
}

const itemVariants = {
  hidden: { opacity: 0, scale: 0.8 },
  visible: { opacity: 1, scale: 1, transition: { type: 'spring' as any, stiffness: 300, damping: 24 } }
}

export function ProbabilityMatrix({ scores }: Props) {
  if (!scores || scores.length === 0) return null

  // Sort alphabetically
  const sorted = [...scores].sort((a, b) => a.zone_id.localeCompare(b.zone_id))

  return (
    // @ts-ignore
    <motion.div 
      initial={{ opacity: 0, y: 10 }}
      animate={{ opacity: 1, y: 0 }}
      className="bg-zinc-900 glass-panel rounded-xl p-4 mt-4"
    >
      <div className="flex items-center justify-between mb-3">
        <h3 className="text-xs uppercase tracking-widest text-zinc-500 font-medium">PyTorch Probability Matrix</h3>
        <span className="text-[10px] bg-zinc-800 px-2 py-0.5 rounded text-zinc-400 border border-zinc-700">inference_latency ~1ms</span>
      </div>
      
      // @ts-ignore
      <motion.div 
        variants={containerVariants}
        initial="hidden"
        animate="visible"
        className="grid grid-cols-2 sm:grid-cols-5 gap-3"
      >
        {sorted.map(s => {
          const style = getHeatmapColor(s.score, s.is_false_sos_suspect)
          return (
            // @ts-ignore
            <motion.div 
              variants={itemVariants}
              key={s.zone_id} 
              className={`flex flex-col items-center justify-center p-3 rounded-lg border transition-colors duration-300 relative overflow-hidden ${style}`}
            >
              <span className="text-xs font-bold z-10 tracking-widest uppercase mb-1">{s.zone_id}</span>
              <div className="text-[14px] mono font-semibold z-10 flex items-baseline gap-1">
                {(s.score * 100).toFixed(0)}<span className="text-[10px] opacity-70">%</span>
              </div>
              
              {s.is_false_sos_suspect && (
                <div className="absolute inset-0 bg-purple-500/10 pointer-events-none" style={{ backgroundImage: 'repeating-linear-gradient(45deg, transparent, transparent 5px, rgba(168, 85, 247, 0.2) 5px, rgba(168, 85, 247, 0.2) 10px)' }}></div>
              )}
            </motion.div>
          )
        })}
      </motion.div>
    </motion.div>
  )
}