File size: 4,672 Bytes
065d164 |
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 163 164 165 166 167 168 |
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 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>
</>
);
}; |