Spaces:
Sleeping
Sleeping
File size: 5,925 Bytes
5e0532d |
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 |
'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>
);
}
|