Spaces:
Sleeping
Sleeping
| "use client"; | |
| interface BlendshapeBarProps { | |
| name: string; | |
| value: number; | |
| color?: string; | |
| } | |
| function BlendshapeBar({ name, value, color = "#63b3ed" }: BlendshapeBarProps) { | |
| return ( | |
| <div className="flex flex-col gap-1.5"> | |
| <div className="flex items-center justify-between px-0.5"> | |
| <span className="text-[10px] text-white/40 uppercase tracking-wider font-medium">{name}</span> | |
| <span className="text-[10px] text-white/30 font-mono"> | |
| {Math.round(value * 100)}% | |
| </span> | |
| </div> | |
| <div className="h-1.5 bg-white/5 rounded-full overflow-hidden border border-white/5 shadow-inner"> | |
| <div | |
| className="h-full rounded-full transition-all duration-300 ease-out" | |
| style={{ | |
| width: `${Math.round(value * 100)}%`, | |
| background: `linear-gradient(90deg, ${color}cc, ${color})`, | |
| boxShadow: `0 0 10px ${color}40`, | |
| }} | |
| /> | |
| </div> | |
| </div> | |
| ); | |
| } | |
| interface StatsPanel { | |
| fps: number; | |
| faceDetected: boolean; | |
| blendshapes: Record<string, number>; | |
| } | |
| const KEY_BLENDSHAPES: Array<{ key: string; label: string; color: string }> = [ | |
| { key: "mouthSmileLeft", label: "Smile L", color: "#68d391" }, | |
| { key: "mouthSmileRight", label: "Smile R", color: "#68d391" }, | |
| { key: "browInnerUp", label: "Brow Up", color: "#f6ad55" }, | |
| { key: "jawOpen", label: "Jaw Open", color: "#63b3ed" }, | |
| { key: "browDownLeft", label: "Brow ↓L", color: "#fc8181" }, | |
| { key: "browDownRight", label: "Brow ↓R", color: "#fc8181" }, | |
| { key: "mouthFrownLeft", label: "Frown L", color: "#76e4f7" }, | |
| { key: "mouthFrownRight", label: "Frown R", color: "#76e4f7" }, | |
| ]; | |
| export default function StatsPanel({ fps, faceDetected, blendshapes }: StatsPanel) { | |
| return ( | |
| <div className="glass rounded-2xl p-5 space-y-6 animate-scale-up"> | |
| <div className="flex items-center justify-between"> | |
| <div className="flex items-center gap-2"> | |
| <div className="p-1.5 rounded-lg bg-blue-500/10 border border-blue-500/20"> | |
| <svg className="w-3.5 h-3.5 text-blue-400" fill="none" stroke="currentColor" viewBox="0 0 24 24"> | |
| <path strokeLinecap="round" strokeLinejoin="round" strokeWidth={2} d="M9 19v-6a2 2 0 00-2-2H5a2 2 0 00-2 2v6a2 2 0 002 2h2a2 2 0 002-2zm0 0V9a2 2 0 012-2h2a2 2 0 012 2v10m-6 0a2 2 0 002 2h2a2 2 0 002-2m0 0V5a2 2 0 012-2h2a2 2 0 012 2v14a2 2 0 01-2 2h-2a2 2 0 01-2-2z" /> | |
| </svg> | |
| </div> | |
| <h3 | |
| style={{ fontFamily: "Outfit, sans-serif" }} | |
| className="text-xs font-bold text-white/70 uppercase tracking-widest" | |
| > | |
| Analytics | |
| </h3> | |
| </div> | |
| <div className={`px-2 py-0.5 rounded-full text-[10px] font-bold uppercase tracking-tighter border transition-colors ${ | |
| faceDetected ? "bg-green-500/10 border-green-500/30 text-green-400" : "bg-red-500/10 border-red-500/30 text-red-400" | |
| }`}> | |
| {faceDetected ? "Active" : "Idle"} | |
| </div> | |
| </div> | |
| {/* Stats Grid */} | |
| <div className="grid grid-cols-2 gap-3"> | |
| <div className="glass-strong rounded-xl p-3 border border-white/5 group hover:border-white/10 transition-colors"> | |
| <p className="text-xs text-white/30 mb-1">Performance</p> | |
| <div className="flex items-baseline gap-1"> | |
| <span className="text-2xl font-bold text-white tracking-tight" style={{ fontFamily: "Outfit, sans-serif" }}> | |
| {fps} | |
| </span> | |
| <span className="text-[10px] text-white/20 font-bold uppercase">fps</span> | |
| </div> | |
| </div> | |
| <div className="glass-strong rounded-xl p-3 border border-white/5 group hover:border-white/10 transition-colors"> | |
| <p className="text-xs text-white/30 mb-1">Detection</p> | |
| <div className="flex items-baseline gap-1"> | |
| <span className="text-2xl font-bold text-white tracking-tight" style={{ fontFamily: "Outfit, sans-serif" }}> | |
| {faceDetected ? "1" : "0"} | |
| </span> | |
| <span className="text-[10px] text-white/20 font-bold uppercase">face</span> | |
| </div> | |
| </div> | |
| </div> | |
| {/* Blendshapes */} | |
| {faceDetected && ( | |
| <div className="space-y-4 pt-2"> | |
| <div className="flex items-center gap-2"> | |
| <div className="h-px flex-1 bg-gradient-to-r from-transparent via-white/10 to-transparent" /> | |
| <span className="text-[10px] text-white/20 uppercase tracking-widest font-bold">Signals</span> | |
| <div className="h-px flex-1 bg-gradient-to-r from-transparent via-white/10 to-transparent" /> | |
| </div> | |
| <div className="grid grid-cols-1 gap-4"> | |
| {KEY_BLENDSHAPES.map(({ key, label, color }) => ( | |
| <BlendshapeBar | |
| key={key} | |
| name={label} | |
| value={blendshapes[key] ?? 0} | |
| color={color} | |
| /> | |
| ))} | |
| </div> | |
| </div> | |
| )} | |
| </div> | |
| ); | |
| } | |