handspew / components /UIUtils.js
Tina Tarighian
mobile updates
128b839
raw
history blame
4.81 kB
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>
</>
);
};