import { useEffect, useRef } from 'react'; const ThoughtBubble = ({ isFirstLoad, isThinking, thought, isMouthOpen, handDetected, isLeftHand, thumbPosition, canvasWidth, isMobile, animateThinking, createSparkleParticles, createPopParticles }) => { const thoughtBubbleRef = useRef(null); // Add particle effects when thought appears useEffect(() => { if (thought && isMouthOpen && thoughtBubbleRef.current) { const bubbleRect = thoughtBubbleRef.current.getBoundingClientRect(); const x = bubbleRect.left + bubbleRect.width / 2; const y = bubbleRect.top + bubbleRect.height / 2; createSparkleParticles(x, y); } }, [thought, isMouthOpen, createSparkleParticles]); // Add pop effect when thought disappears useEffect(() => { if (!isMouthOpen && thought && thoughtBubbleRef.current) { const bubbleRect = thoughtBubbleRef.current.getBoundingClientRect(); const x = bubbleRect.left + bubbleRect.width / 2; const y = bubbleRect.top + bubbleRect.height / 2; createPopParticles(x, y); } }, [isMouthOpen, thought, createPopParticles]); // Get bubble content based on state const getBubbleContent = () => { if (isFirstLoad) { return

Open and close your hand to generate thoughts...

; } if (isThinking) { // Much larger emoji size with blinking animation return ( 🤔 ); } if (thought && isMouthOpen) { // Much larger text size return

{thought}

; } return

Waiting for hand gesture...

; }; // Determine if the bubble should be visible const shouldShowBubble = () => { // Only show during first load, when thinking, or when mouth is open return isFirstLoad || isThinking || isMouthOpen; }; // Calculate opacity based on state const getOpacity = () => { return isMouthOpen || isThinking ? 1 : 0.7; }; // Get appropriate padding based on content const getBubblePadding = () => { if (isThinking) { // More padding for larger emoji return isMobile ? '10px' : '12px'; } // More padding for larger text return isMobile ? '12px 16px' : '16px 20px'; }; // Get appropriate border radius based on content const getBubbleBorderRadius = () => { if (isThinking) { // More circular for emoji return isMobile ? '35px' : '40px'; } // Normal border radius for text content return isMobile ? '16px' : '20px'; }; // Get appropriate width based on content const getBubbleWidth = () => { if (!handDetected) { return isMobile ? '90%' : `${canvasWidth * 0.8}px`; } if (isThinking) { // Much wider to accommodate larger emoji return isMobile ? '70px' : '80px'; } // Wider for larger text content const bubbleWidth = isMobile ? Math.min(220, canvasWidth * 0.7) : Math.min(300, canvasWidth * 0.6); return `${bubbleWidth}px`; }; // Calculate thought bubble position const getBubbleStyle = () => { if (!handDetected) { // Default position when no hand is detected return { position: 'absolute', bottom: '20px', left: '50%', transform: animateThinking ? undefined : 'translateX(-50%)', width: getBubbleWidth(), }; } const offset = isMobile ? 12 : 20; // Space between thumb and bubble if (isLeftHand) { // For left hand, position to the right of thumb return { position: 'absolute', top: `${thumbPosition.y - (isMobile ? 20 : 30)}px`, left: `${thumbPosition.x + offset}px`, width: getBubbleWidth(), maxWidth: isThinking ? 'none' : `${canvasWidth - thumbPosition.x - (offset * 2)}px` // Prevent overflow }; } else { // For right hand, position to the left of thumb return { position: 'absolute', top: `${thumbPosition.y - (isMobile ? 20 : 30)}px`, right: `${canvasWidth - thumbPosition.x + offset}px`, width: getBubbleWidth(), maxWidth: isThinking ? 'none' : `${thumbPosition.x - (offset * 2)}px` // Prevent overflow }; } }; if (!shouldShowBubble()) { return null; } return (
{getBubbleContent()}
); }; export default ThoughtBubble;