Spaces:
Sleeping
Sleeping
| 'use client'; | |
| import { Shield, BookOpen, Heart, Brain, Sparkles } from 'lucide-react'; | |
| interface ThinkingIndicatorProps { | |
| activeAgent?: string; | |
| stage?: 'routing' | 'thinking' | 'responding'; | |
| } | |
| const agentConfig: Record<string, { icon: typeof Shield; color: string; label: string; description: string }> = { | |
| Gatekeeper: { | |
| icon: Shield, | |
| color: 'purple', | |
| label: 'Gatekeeper', | |
| description: 'Analyzing your intent...', | |
| }, | |
| Theologian: { | |
| icon: BookOpen, | |
| color: 'blue', | |
| label: 'Theologian', | |
| description: 'Consulting Scripture...', | |
| }, | |
| Healer: { | |
| icon: Heart, | |
| color: 'rose', | |
| label: 'Healer', | |
| description: 'Preparing pastoral care...', | |
| }, | |
| Orchestrator: { | |
| icon: Brain, | |
| color: 'amber', | |
| label: 'Orchestrator', | |
| description: 'Coordinating response...', | |
| }, | |
| System: { | |
| icon: Sparkles, | |
| color: 'neutral', | |
| label: 'System', | |
| description: 'Processing...', | |
| }, | |
| }; | |
| const colorClasses: Record<string, { bg: string; border: string; text: string; glow: string; pulse: string }> = { | |
| purple: { | |
| bg: 'bg-purple-500/20', | |
| border: 'border-purple-500/40', | |
| text: 'text-purple-400', | |
| glow: 'shadow-[0_0_20px_rgba(168,85,247,0.4)]', | |
| pulse: 'bg-purple-400', | |
| }, | |
| blue: { | |
| bg: 'bg-blue-500/20', | |
| border: 'border-blue-500/40', | |
| text: 'text-blue-400', | |
| glow: 'shadow-[0_0_20px_rgba(59,130,246,0.4)]', | |
| pulse: 'bg-blue-400', | |
| }, | |
| rose: { | |
| bg: 'bg-rose-500/20', | |
| border: 'border-rose-500/40', | |
| text: 'text-rose-400', | |
| glow: 'shadow-[0_0_20px_rgba(244,63,94,0.4)]', | |
| pulse: 'bg-rose-400', | |
| }, | |
| amber: { | |
| bg: 'bg-amber-500/20', | |
| border: 'border-amber-500/40', | |
| text: 'text-amber-400', | |
| glow: 'shadow-[0_0_20px_rgba(245,158,11,0.4)]', | |
| pulse: 'bg-amber-400', | |
| }, | |
| neutral: { | |
| bg: 'bg-neutral-500/20', | |
| border: 'border-neutral-500/40', | |
| text: 'text-neutral-400', | |
| glow: 'shadow-[0_0_20px_rgba(163,163,163,0.4)]', | |
| pulse: 'bg-neutral-400', | |
| }, | |
| }; | |
| export default function ThinkingIndicator({ activeAgent = 'Gatekeeper', stage = 'thinking' }: ThinkingIndicatorProps) { | |
| const agent = agentConfig[activeAgent] || agentConfig.System; | |
| const colors = colorClasses[agent.color] || colorClasses.neutral; | |
| const Icon = agent.icon; | |
| return ( | |
| <div className="flex gap-3 items-start animate-fade-in"> | |
| {/* Agent Icon with Pulsing Rings */} | |
| <div className="relative"> | |
| <div className={`w-8 h-8 rounded-full ${colors.bg} flex items-center justify-center border ${colors.border} ${colors.glow} transition-all duration-500`}> | |
| <Icon className={`w-4 h-4 ${colors.text}`} /> | |
| </div> | |
| {/* Sonar rings */} | |
| <div className="absolute inset-0 rounded-full animate-ping-slow opacity-30"> | |
| <div className={`w-full h-full rounded-full ${colors.bg} border ${colors.border}`} /> | |
| </div> | |
| <div className="absolute inset-0 rounded-full animate-ping-slower opacity-20" style={{ animationDelay: '0.5s' }}> | |
| <div className={`w-full h-full rounded-full ${colors.bg} border ${colors.border}`} /> | |
| </div> | |
| </div> | |
| {/* Thinking Content */} | |
| <div className={`flex-1 rounded-2xl rounded-tl-sm p-4 ${colors.bg} border ${colors.border} backdrop-blur-sm`}> | |
| {/* Agent Badge */} | |
| <div className="flex items-center gap-2 mb-2"> | |
| <span className={`text-[10px] font-mono uppercase tracking-wider ${colors.text}`}> | |
| {agent.label} | |
| </span> | |
| <div className="flex items-center gap-1"> | |
| <span className={`w-1.5 h-1.5 rounded-full ${colors.pulse} animate-pulse`} /> | |
| <span className="text-[10px] text-neutral-500"> | |
| {stage === 'routing' ? 'Routing' : stage === 'responding' ? 'Generating' : 'Thinking'} | |
| </span> | |
| </div> | |
| </div> | |
| {/* Animated Thinking Dots */} | |
| <div className="flex items-center gap-3"> | |
| <div className="flex items-center gap-1.5"> | |
| <span className={`w-2 h-2 ${colors.pulse} rounded-full animate-bounce`} style={{ animationDelay: '0ms' }} /> | |
| <span className={`w-2 h-2 ${colors.pulse} rounded-full animate-bounce`} style={{ animationDelay: '150ms' }} /> | |
| <span className={`w-2 h-2 ${colors.pulse} rounded-full animate-bounce`} style={{ animationDelay: '300ms' }} /> | |
| </div> | |
| <span className="text-sm text-neutral-400 italic">{agent.description}</span> | |
| </div> | |
| {/* Swarm Activity Indicator */} | |
| <div className="mt-3 pt-3 border-t border-white/5"> | |
| <div className="flex items-center gap-2"> | |
| <span className="text-[9px] text-neutral-600 uppercase tracking-wider">Swarm Activity</span> | |
| <div className="flex-1 h-1 bg-white/5 rounded-full overflow-hidden"> | |
| <div | |
| className={`h-full ${colors.pulse} opacity-60 rounded-full animate-pulse`} | |
| style={{ width: '60%' }} | |
| /> | |
| </div> | |
| </div> | |
| <div className="flex gap-1.5 mt-2"> | |
| {Object.entries(agentConfig).slice(0, 4).map(([name, config]) => { | |
| const isActive = name === activeAgent; | |
| const agentColors = colorClasses[config.color]; | |
| return ( | |
| <div | |
| key={name} | |
| className={`flex items-center gap-1 px-1.5 py-0.5 rounded text-[8px] transition-all duration-300 ${ | |
| isActive | |
| ? `${agentColors.bg} ${agentColors.border} border ${agentColors.text}` | |
| : 'bg-white/5 text-neutral-600' | |
| }`} | |
| > | |
| <config.icon className="w-2.5 h-2.5" /> | |
| <span className="hidden sm:inline">{name.slice(0, 4)}</span> | |
| </div> | |
| ); | |
| })} | |
| </div> | |
| </div> | |
| </div> | |
| </div> | |
| ); | |
| } | |