Spaces:
Sleeping
Sleeping
| import { ArrowRight, Mic, Brain, Shield, Zap, Volume2, Radio } from "lucide-react"; | |
| import { cn } from "@/lib/utils"; | |
| interface FlowBlockProps { | |
| icon: React.ReactNode; | |
| label: string; | |
| sublabel?: string; | |
| isActive?: boolean; | |
| variant?: "input" | "process" | "output"; | |
| testId: string; | |
| } | |
| function FlowBlock({ icon, label, sublabel, isActive = false, variant = "process", testId }: FlowBlockProps) { | |
| const variantStyles = { | |
| input: "bg-emerald-500/10 border-emerald-500/30 text-emerald-600 dark:text-emerald-400", | |
| process: "bg-primary/10 border-primary/30 text-primary", | |
| output: "bg-amber-500/10 border-amber-500/30 text-amber-600 dark:text-amber-400", | |
| }; | |
| return ( | |
| <div | |
| className={cn( | |
| "flex flex-col items-center justify-center px-3 py-2 rounded-lg border transition-all duration-300", | |
| variantStyles[variant], | |
| isActive && "ring-2 ring-offset-2 ring-primary/50" | |
| )} | |
| role="img" | |
| aria-label={`${label}${sublabel ? `: ${sublabel}` : ""}`} | |
| data-testid={testId} | |
| > | |
| <div className="mb-1">{icon}</div> | |
| <span className="text-xs font-semibold whitespace-nowrap">{label}</span> | |
| {sublabel && <span className="text-[10px] text-muted-foreground">{sublabel}</span>} | |
| </div> | |
| ); | |
| } | |
| function FlowArrow() { | |
| return ( | |
| <div className="flex items-center justify-center px-1"> | |
| <ArrowRight className="h-4 w-4 text-muted-foreground" aria-hidden="true" /> | |
| </div> | |
| ); | |
| } | |
| interface DataFlowDiagramProps { | |
| activeStep?: number; | |
| className?: string; | |
| } | |
| export function DataFlowDiagram({ activeStep = -1, className }: DataFlowDiagramProps) { | |
| const steps = [ | |
| { icon: <Mic className="h-4 w-4" />, label: "Audio In", sublabel: "Input", variant: "input" as const, testId: "flow-block-audio-in" }, | |
| { icon: <Radio className="h-4 w-4" />, label: "ASR", sublabel: "Speech-to-Text", variant: "process" as const, testId: "flow-block-asr" }, | |
| { icon: <Brain className="h-4 w-4" />, label: "Reasoning", sublabel: "LLM Layer", variant: "process" as const, testId: "flow-block-reasoning" }, | |
| { icon: <Shield className="h-4 w-4" />, label: "Policy", sublabel: "Safety", variant: "process" as const, testId: "flow-block-policy" }, | |
| { icon: <Zap className="h-4 w-4" />, label: "Action", sublabel: "Response", variant: "process" as const, testId: "flow-block-action" }, | |
| { icon: <Volume2 className="h-4 w-4" />, label: "TTS", sublabel: "Text-to-Speech", variant: "process" as const, testId: "flow-block-tts" }, | |
| { icon: <Mic className="h-4 w-4" />, label: "Audio Out", sublabel: "Output", variant: "output" as const, testId: "flow-block-audio-out" }, | |
| ]; | |
| return ( | |
| <div | |
| className={cn("flex items-center gap-1 overflow-x-auto py-2", className)} | |
| role="img" | |
| aria-label="Data flow diagram showing the AI call processing pipeline" | |
| data-testid="data-flow-diagram" | |
| > | |
| {steps.map((step, index) => ( | |
| <div key={step.label + index} className="flex items-center"> | |
| <FlowBlock | |
| icon={step.icon} | |
| label={step.label} | |
| sublabel={step.sublabel} | |
| variant={step.variant} | |
| isActive={activeStep === index} | |
| testId={step.testId} | |
| /> | |
| {index < steps.length - 1 && <FlowArrow />} | |
| </div> | |
| ))} | |
| </div> | |
| ); | |
| } | |