1- import React , { useEffect , useCallback , useState } from 'react' ;
1+ import React , { useEffect , useCallback , useState , useMemo } from 'react' ;
22import { motion , AnimatePresence } from 'framer-motion' ;
3- import { X , Loader2 } from 'lucide-react' ;
3+ import { X , Loader2 , Shield , Zap , HeadphonesIcon , Bot } from 'lucide-react' ;
44import { toast } from 'sonner' ;
55import aiTorAvatar from '@/assets/ai-tor-avatar.jpg' ;
66
7- const PROACTIVE_MESSAGES = [
8- "¿Puedo ayudarte a encontrar algo?" ,
9- "¿Tienes alguna pregunta sobre AlienFlowSpace?" ,
10- "¿Necesitas ayuda con los NFTs o las DAOs?" ,
11- "¿Te gustaría conocer más sobre nuestro ecosistema?" ,
12- "¿En qué puedo asistirte hoy?"
7+ // AI Personalities with unique messages and slight visual variations
8+ const AI_PERSONALITIES = [
9+ {
10+ id : 'support' ,
11+ name : 'AI Tor' ,
12+ role : 'Soporte Técnico' ,
13+ icon : HeadphonesIcon ,
14+ color : 'from-alien-green to-emerald-500' ,
15+ borderColor : 'border-alien-green/50' ,
16+ messages : [
17+ "¿Tienes algún problema técnico? Estoy aquí para ayudarte." ,
18+ "¿Necesitas ayuda con tu wallet o transacciones?" ,
19+ "¿Problemas conectando? Déjame echarte una mano." ,
20+ "¿Algo no funciona como esperabas? Cuéntame." ,
21+ ]
22+ } ,
23+ {
24+ id : 'dao' ,
25+ name : 'Voz de la DAO' ,
26+ role : 'Estrategia' ,
27+ icon : Zap ,
28+ color : 'from-alien-gold to-yellow-500' ,
29+ borderColor : 'border-alien-gold/50' ,
30+ messages : [
31+ "¿Quieres saber cómo participar en las votaciones?" ,
32+ "Hay propuestas activas esperando tu voto..." ,
33+ "¿Conoces los beneficios de ser miembro de la DAO?" ,
34+ "La comunidad está decidiendo el futuro. ¿Te unes?" ,
35+ ]
36+ } ,
37+ {
38+ id : 'sentinel' ,
39+ name : 'Alien Sentinel' ,
40+ role : 'Seguridad' ,
41+ icon : Shield ,
42+ color : 'from-purple-500 to-violet-600' ,
43+ borderColor : 'border-purple-500/50' ,
44+ messages : [
45+ "¿Preocupado por la seguridad de tus activos?" ,
46+ "Recuerda: nunca compartas tus claves privadas." ,
47+ "¿Quieres consejos para proteger tu wallet?" ,
48+ "La seguridad primero. ¿Alguna duda?" ,
49+ ]
50+ } ,
51+ {
52+ id : 'guide' ,
53+ name : 'Cosmic Guide' ,
54+ role : 'Navegador' ,
55+ icon : Bot ,
56+ color : 'from-cyan-500 to-blue-500' ,
57+ borderColor : 'border-cyan-500/50' ,
58+ messages : [
59+ "¿Primera vez en AlienFlowSpace? Te guío." ,
60+ "Hay mucho por explorar. ¿Por dónde empezamos?" ,
61+ "¿Buscas algo específico en el ecosistema?" ,
62+ "Déjame mostrarte lo que puedes hacer aquí." ,
63+ ]
64+ }
1365] ;
1466
1567const AIChatbot = ( ) => {
1668 const [ isOpen , setIsOpen ] = useState ( false ) ;
1769 const [ isLoading , setIsLoading ] = useState ( true ) ;
1870 const [ showProactive , setShowProactive ] = useState ( false ) ;
19- const [ proactiveMessage , setProactiveMessage ] = useState ( '' ) ;
2071 const [ hasInteracted , setHasInteracted ] = useState ( false ) ;
21- // Nuevo estado para cargar el iframe solo tras el primer render de la DApp
2272 const [ shouldLoadIframe , setShouldLoadIframe ] = useState ( false ) ;
73+ const [ currentPersonality , setCurrentPersonality ] = useState ( 0 ) ;
74+ const [ proactiveMessage , setProactiveMessage ] = useState ( '' ) ;
75+
76+ // Get current personality
77+ const personality = useMemo ( ( ) => AI_PERSONALITIES [ currentPersonality ] , [ currentPersonality ] ) ;
78+ const PersonalityIcon = personality . icon ;
2379
24- // EFICIENCIA: Cargamos el iframe 2 segundos después para no bloquear el hilo principal de la DApp
80+ // Lazy load iframe after initial render
2581 useEffect ( ( ) => {
2682 const timer = setTimeout ( ( ) => {
2783 setShouldLoadIframe ( true ) ;
2884 } , 2000 ) ;
2985 return ( ) => clearTimeout ( timer ) ;
3086 } , [ ] ) ;
3187
32- // Proactive engagement - show after 45 seconds of inactivity
88+ // Proactive engagement with rotating personalities
3389 useEffect ( ( ) => {
3490 if ( hasInteracted || isOpen ) return ;
3591
3692 const timer = setTimeout ( ( ) => {
37- const randomMessage = PROACTIVE_MESSAGES [ Math . floor ( Math . random ( ) * PROACTIVE_MESSAGES . length ) ] ;
93+ // Pick random personality and message
94+ const randomPersonalityIndex = Math . floor ( Math . random ( ) * AI_PERSONALITIES . length ) ;
95+ const randomPersonality = AI_PERSONALITIES [ randomPersonalityIndex ] ;
96+ const randomMessage = randomPersonality . messages [ Math . floor ( Math . random ( ) * randomPersonality . messages . length ) ] ;
97+
98+ setCurrentPersonality ( randomPersonalityIndex ) ;
3899 setProactiveMessage ( randomMessage ) ;
39100 setShowProactive ( true ) ;
40101
41- // Auto-hide after 8 seconds
42- setTimeout ( ( ) => setShowProactive ( false ) , 8000 ) ;
102+ // Auto-hide after 10 seconds
103+ setTimeout ( ( ) => setShowProactive ( false ) , 10000 ) ;
43104 } , 45000 ) ;
44105
45106 return ( ) => clearTimeout ( timer ) ;
@@ -61,10 +122,7 @@ const AIChatbot = () => {
61122 setIsLoading ( true ) ;
62123 setShowProactive ( false ) ;
63124 setHasInteracted ( true ) ;
64- setShouldLoadIframe ( true ) ; // Aseguramos carga si el usuario hace clic rápido
65- toast . info ( 'AI Assistant Loading' , {
66- description : 'Opening AI Tor Assistant...'
67- } ) ;
125+ setShouldLoadIframe ( true ) ;
68126 } , [ ] ) ;
69127
70128 const handleIframeLoad = useCallback ( ( ) => {
@@ -86,36 +144,62 @@ const AIChatbot = () => {
86144 ) }
87145 </ AnimatePresence >
88146
89- { /* Proactive Engagement Bubble */ }
147+ { /* Proactive Engagement Bubble with Personality */ }
90148 < AnimatePresence >
91149 { showProactive && ! isOpen && (
92150 < motion . div
93151 initial = { { opacity : 0 , x : 20 , scale : 0.8 } }
94152 animate = { { opacity : 1 , x : 0 , scale : 1 } }
95153 exit = { { opacity : 0 , x : 20 , scale : 0.8 } }
96154 className = "fixed bottom-36 right-4 sm:bottom-40 sm:right-8 z-40
97- max-w-[250px] p-3 rounded-xl rounded-br-sm
98- bg-gradient-to-br from-alien-gold/90 to-alien-gold-dark/90
99- backdrop-blur-md border border-alien-gold-light/50
100- shadow-lg shadow-alien-gold/20 cursor-pointer"
155+ max-w-[280px] rounded-2xl rounded-br-sm overflow-hidden
156+ backdrop-blur-xl border shadow-2xl cursor-pointer"
157+ style = { {
158+ background : 'linear-gradient(135deg, rgba(17,17,25,0.95) 0%, rgba(30,30,40,0.95) 100%)' ,
159+ borderColor : `rgba(${ personality . id === 'support' ? '57,255,20' : personality . id === 'dao' ? '240,216,130' : personality . id === 'sentinel' ? '168,85,247' : '34,211,238' } ,0.4)`
160+ } }
101161 onClick = { handleOpen }
102162 >
103- < p className = "text-alien-space-dark font-exo text-sm font-medium" >
104- { proactiveMessage }
105- </ p >
163+ { /* Personality Header */ }
164+ < div className = { `bg-gradient-to-r ${ personality . color } p-2 px-3 flex items-center gap-2` } >
165+ < PersonalityIcon className = "w-4 h-4 text-white" />
166+ < div className = "flex-1" >
167+ < p className = "text-white text-xs font-nasalization font-bold" > { personality . name } </ p >
168+ < p className = "text-white/80 text-[10px] font-exo" > { personality . role } </ p >
169+ </ div >
170+ < div className = "w-6 h-6 rounded-full overflow-hidden border border-white/30" >
171+ < img
172+ src = { aiTorAvatar }
173+ alt = { personality . name }
174+ className = "w-full h-full object-cover"
175+ style = { {
176+ filter : personality . id === 'sentinel' ? 'hue-rotate(270deg)' :
177+ personality . id === 'guide' ? 'hue-rotate(180deg)' :
178+ personality . id === 'dao' ? 'sepia(30%)' : 'none'
179+ } }
180+ />
181+ </ div >
182+ </ div >
183+ { /* Message */ }
184+ < div className = "p-3" >
185+ < p className = "text-gray-200 font-exo text-sm leading-relaxed" >
186+ { proactiveMessage }
187+ </ p >
188+ </ div >
189+ { /* Close button */ }
106190 < button
107191 onClick = { ( e ) => { e . stopPropagation ( ) ; setShowProactive ( false ) ; } }
108- className = "absolute -top-2 -right-2 w-5 h-5 bg-alien-space-dark rounded-full
109- flex items-center justify-center border border-alien-gold/50
110- hover:bg-alien-gold/20 transition-colors"
192+ className = "absolute -top-2 -right-2 w-6 h-6 bg-alien-space-dark rounded-full
193+ flex items-center justify-center border border-gray-600
194+ hover:bg-gray-700 transition-colors shadow-lg "
111195 >
112- < X className = "w-3 h-3 text-alien-gold " />
196+ < X className = "w-3 h-3 text-gray-400 " />
113197 </ button >
114198 </ motion . div >
115199 ) }
116200 </ AnimatePresence >
117201
118- { /* Floating Button */ }
202+ { /* Floating Button with pulsing ring */ }
119203 < motion . button
120204 initial = { { opacity : 0 , scale : 0.5 } }
121205 animate = { { opacity : 1 , scale : 1 } }
@@ -125,18 +209,22 @@ const AIChatbot = () => {
125209 bg-gradient-to-br from-alien-gold to-alien-gold-dark backdrop-blur-md
126210 border-2 border-alien-gold-light rounded-full
127211 hover:scale-110 transition-all duration-300
128- shadow-2xl hover:shadow-alien-gold/50 ai-button-pulse
129- overflow-hidden w-14 h-14 sm:w-16 sm:h-16"
212+ shadow-2xl hover:shadow-alien-gold/50
213+ overflow-hidden w-14 h-14 sm:w-16 sm:h-16
214+ relative"
130215 aria-label = "Open AI Assistant"
131216 >
217+ { /* Pulsing ring */ }
218+ < span className = "absolute inset-0 rounded-full animate-ping bg-alien-gold/30" style = { { animationDuration : '2s' } } />
219+ < span className = "absolute inset-0 rounded-full animate-pulse bg-gradient-to-br from-alien-green/20 to-transparent" />
132220 < img
133221 src = { aiTorAvatar }
134222 alt = "AI Tor"
135- className = "w-full h-full object-cover"
223+ className = "w-full h-full object-cover relative z-10 "
136224 />
137225 </ motion . button >
138226
139- { /* Chat Window - Fullscreen on mobile, optimized size on desktop */ }
227+ { /* Chat Window */ }
140228 < AnimatePresence >
141229 { isOpen && (
142230 < motion . div
@@ -152,24 +240,29 @@ const AIChatbot = () => {
152240 sm:max-h-[calc(100vh-10rem)]
153241 bg-alien-space-dark/98 backdrop-blur-xl
154242 border-2 border-alien-gold/40 rounded-xl
155- shadow-2xl overflow-hidden chat-glow"
243+ shadow-2xl overflow-hidden"
244+ style = { {
245+ boxShadow : '0 0 40px rgba(57,255,20,0.1), 0 0 80px rgba(240,216,130,0.05)'
246+ } }
156247 >
157248 { /* Header */ }
158249 < div className = "bg-gradient-to-r from-alien-space-dark via-alien-space/90 to-alien-space-dark
159250 border-b border-alien-gold/30 p-3 sm:p-4 flex items-center justify-between" >
160251 < div className = "flex items-center gap-3" >
161- < div className = "w-10 h-10 sm:w-11 sm:h-11 rounded-full overflow-hidden border-2 border-alien-gold/50" >
252+ < div className = "w-10 h-10 sm:w-11 sm:h-11 rounded-full overflow-hidden border-2 border-alien-gold/50 relative " >
162253 < img
163254 src = { aiTorAvatar }
164255 alt = "AI Tor"
165256 className = "w-full h-full object-cover"
166257 />
258+ { /* Online indicator */ }
259+ < span className = "absolute bottom-0 right-0 w-3 h-3 bg-alien-green rounded-full border-2 border-alien-space-dark" />
167260 </ div >
168261 < div >
169262 < h3 className = "text-alien-gold font-bold font-nasalization text-sm sm:text-base" >
170- AI Assistant
263+ AI Tor
171264 </ h3 >
172- < p className = "text-xs text-alien-green font-exo" > AI Tor </ p >
265+ < p className = "text-xs text-alien-green font-exo" > Asistente Inteligente </ p >
173266 </ div >
174267 < div className = "flex items-center gap-1.5 ml-2" >
175268 { isLoading ? (
@@ -179,10 +272,7 @@ const AIChatbot = () => {
179272 < span className = "typing-dot" style = { { animationDelay : '0.4s' } } />
180273 </ div >
181274 ) : (
182- < >
183- < span className = "w-2 h-2 bg-alien-green rounded-full animate-pulse" />
184- < span className = "text-xs text-muted-foreground hidden sm:inline" > Online</ span >
185- </ >
275+ < span className = "text-xs text-alien-green/80 hidden sm:inline font-exo" > Conectado</ span >
186276 ) }
187277 </ div >
188278 </ div >
@@ -203,24 +293,31 @@ const AIChatbot = () => {
203293 animate = { { opacity : 1 } }
204294 exit = { { opacity : 0 } }
205295 className = "absolute inset-0 top-14 sm:top-16 flex flex-col items-center justify-center
206- bg-alien-space-dark/95 z-10"
296+ bg-alien-space-dark/98 z-10"
207297 >
208298 < div className = "relative" >
209299 < div className = "w-20 h-20 rounded-full overflow-hidden border-2 border-alien-gold/50 mb-4" >
210300 < img
211301 src = { aiTorAvatar }
212302 alt = "AI Tor Loading"
213- className = "w-full h-full object-cover animate-pulse "
303+ className = "w-full h-full object-cover"
214304 />
215305 </ div >
216306 < Loader2 className = "absolute -bottom-1 -right-1 h-8 w-8 text-alien-gold animate-spin" />
217307 </ div >
218- < p className = "text-alien-gold font-nasalization text-sm mt-4 uppercase tracking-tighter" > Establishing Link...</ p >
308+ < p className = "text-alien-gold font-nasalization text-sm mt-4 uppercase tracking-wider" >
309+ Sincronizando...
310+ </ p >
311+ < div className = "flex gap-1.5 mt-3" >
312+ < span className = "w-2 h-2 bg-alien-green rounded-full animate-bounce" style = { { animationDelay : '0s' } } />
313+ < span className = "w-2 h-2 bg-alien-green rounded-full animate-bounce" style = { { animationDelay : '0.15s' } } />
314+ < span className = "w-2 h-2 bg-alien-green rounded-full animate-bounce" style = { { animationDelay : '0.3s' } } />
315+ </ div >
219316 </ motion . div >
220317 ) }
221318 </ AnimatePresence >
222319
223- { /* iframe Content con carga diferida */ }
320+ { /* iframe Content with lazy loading */ }
224321 { shouldLoadIframe && (
225322 < iframe
226323 src = "https://aitor.lovable.app/"
@@ -230,7 +327,9 @@ const AIChatbot = () => {
230327 onLoad = { handleIframeLoad }
231328 onError = { ( ) => {
232329 setIsLoading ( false ) ;
233- toast . error ( 'AI Assistant Error' ) ;
330+ toast . error ( 'Error de conexión' , {
331+ description : 'No se pudo conectar con AI Tor. Intenta de nuevo.'
332+ } ) ;
234333 } }
235334 />
236335 ) }
0 commit comments