/** * StatusPanel.jsx — Live pipeline status display. * * Subscribes to /social/orchestrator/state (std_msgs/String, JSON payload): * { * state: "idle"|"listening"|"thinking"|"speaking"|"throttled", * gpu_free_mb: number, * gpu_total_mb: number, * latency: { * wakeword_to_transcript: { mean_ms, p95_ms, n }, * transcript_to_llm: { mean_ms, p95_ms, n }, * llm_to_tts: { mean_ms, p95_ms, n }, * end_to_end: { mean_ms, p95_ms, n } * }, * persona_name: string, * active_person: string * } */ import { useEffect, useState } from 'react'; const STATE_CONFIG = { idle: { label: 'IDLE', color: 'text-gray-400', bg: 'bg-gray-800', border: 'border-gray-600', pulse: '' }, listening: { label: 'LISTENING', color: 'text-blue-300', bg: 'bg-blue-950', border: 'border-blue-600', pulse: 'pulse-blue' }, thinking: { label: 'THINKING', color: 'text-amber-300', bg: 'bg-amber-950', border: 'border-amber-600', pulse: 'pulse-amber' }, speaking: { label: 'SPEAKING', color: 'text-green-300', bg: 'bg-green-950', border: 'border-green-600', pulse: 'pulse-green' }, throttled: { label: 'THROTTLED', color: 'text-red-300', bg: 'bg-red-950', border: 'border-red-600', pulse: 'pulse-amber' }, }; function LatencyRow({ label, stat }) { if (!stat || stat.n === 0) return null; const warn = stat.mean_ms > 500; const crit = stat.mean_ms > 1500; const cls = crit ? 'text-red-400' : warn ? 'text-amber-400' : 'text-cyan-400'; return (