Spaces:
Sleeping
Sleeping
| import { useRef, useEffect } from 'react'; | |
| /** | |
| * Status indicator component that shows hand detection status | |
| */ | |
| export const StatusIndicator = ({ handDetected }) => { | |
| return ( | |
| <div className="absolute bottom-4 left-4 flex items-center"> | |
| <div className={`w-3 h-3 rounded-full ${handDetected ? 'bg-[#217BFE]' : 'bg-gray-400'}`}></div> | |
| <span className={`ml-2 text-xs ${handDetected ? 'text-[#217BFE] font-medium' : 'text-gray-500'}`}> | |
| {handDetected ? 'Hand detected' : 'No hand detected'} | |
| </span> | |
| </div> | |
| ); | |
| }; | |
| /** | |
| * Audio effects component that plays sounds based on app state | |
| */ | |
| export const AudioEffects = ({ isThinking }) => { | |
| const bloopSoundRef = useRef(null); | |
| // Play sound effect when thinking state changes | |
| useEffect(() => { | |
| if (isThinking && bloopSoundRef.current) { | |
| bloopSoundRef.current.currentTime = 0; | |
| bloopSoundRef.current.play().catch(err => { | |
| // Handle any autoplay restrictions | |
| console.log("Could not play sound effect:", err); | |
| }); | |
| } | |
| }, [isThinking]); | |
| return ( | |
| <audio | |
| ref={bloopSoundRef} | |
| src="/sounds/bloop.mp3" | |
| preload="auto" | |
| /> | |
| ); | |
| }; | |
| /** | |
| * Animation styles component that defines global animations | |
| */ | |
| export const AnimationStyles = () => { | |
| return ( | |
| <> | |
| <style jsx global>{` | |
| @keyframes thinking-blink { | |
| 0%, 100% { opacity: 1; transform: scale(1); } | |
| 50% { opacity: 0.7; transform: scale(0.95); } | |
| } | |
| @keyframes thinking-spin { | |
| 0% { transform: rotate(0deg); } | |
| 100% { transform: rotate(360deg); } | |
| } | |
| @keyframes spring-out { | |
| 0% { | |
| transform: scale(0) translateY(0); | |
| opacity: 0; | |
| } | |
| 20% { | |
| transform: scale(0.3) translateY(-5px); | |
| opacity: 0.7; | |
| } | |
| 40% { | |
| transform: scale(1.5) translateY(-30px); | |
| } | |
| 60% { | |
| transform: scale(0.8) translateY(15px); | |
| } | |
| 75% { | |
| transform: scale(1.2) translateY(-10px); | |
| } | |
| 90% { | |
| transform: scale(0.95) translateY(5px); | |
| } | |
| 100% { | |
| transform: scale(1) translateY(0); | |
| opacity: 1; | |
| } | |
| } | |
| @keyframes wiggle { | |
| 0% { transform: rotate(0deg); } | |
| 15% { transform: rotate(-15deg); } | |
| 30% { transform: rotate(12deg); } | |
| 45% { transform: rotate(-8deg); } | |
| 60% { transform: rotate(5deg); } | |
| 75% { transform: rotate(-2deg); } | |
| 100% { transform: rotate(0deg); } | |
| } | |
| /* Combined animation that handles both scale and rotation */ | |
| @keyframes spring-wiggle { | |
| 0% { | |
| transform: scale(0) rotate(0deg) translateY(0); | |
| opacity: 0; | |
| } | |
| 15% { | |
| transform: scale(0.2) rotate(-5deg) translateY(-5px); | |
| opacity: 0.5; | |
| } | |
| 30% { | |
| transform: scale(1.5) rotate(12deg) translateY(-30px); | |
| opacity: 1; | |
| } | |
| 45% { | |
| transform: scale(0.8) rotate(-8deg) translateY(15px); | |
| } | |
| 60% { | |
| transform: scale(1.2) rotate(5deg) translateY(-10px); | |
| } | |
| 75% { | |
| transform: scale(0.95) rotate(-2deg) translateY(5px); | |
| } | |
| 90% { | |
| transform: scale(1.05) rotate(1deg) translateY(-2px); | |
| } | |
| 100% { | |
| transform: scale(1) rotate(0deg) translateY(0); | |
| } | |
| } | |
| /* Add new animations for particles and popping */ | |
| @keyframes float-particle { | |
| 0% { | |
| transform: translate(0, 0) rotate(0deg); | |
| opacity: 1; | |
| } | |
| 100% { | |
| transform: translate(var(--tx), var(--ty)) rotate(var(--r)); | |
| opacity: 0; | |
| } | |
| } | |
| @keyframes pop-out { | |
| 0% { | |
| transform: scale(1); | |
| opacity: 1; | |
| } | |
| 50% { | |
| transform: scale(1.2); | |
| } | |
| 100% { | |
| transform: scale(0); | |
| opacity: 0; | |
| } | |
| } | |
| /* Particle styles */ | |
| .particle { | |
| position: absolute; | |
| pointer-events: none; | |
| background: linear-gradient(135deg, #217BFE, #AC87EB); | |
| border-radius: 50%; | |
| animation: float-particle 1s ease-out forwards; | |
| } | |
| .pop-particle { | |
| position: absolute; | |
| pointer-events: none; | |
| background: linear-gradient(135deg, #217BFE, #AC87EB); | |
| border-radius: 50%; | |
| animation: float-particle 0.6s ease-out forwards; | |
| } | |
| `}</style> | |
| </> | |
| ); | |
| }; |