ORA / frontend /components /chat /ThinkingIndicator.tsx
Abdalkaderdev's picture
Initial ORA deployment
5e0532d
'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>
);
}