import React, { useState, useEffect, useCallback, useRef } from 'react'; import { Button } from '@/components/ui/button'; import { Card } from '@/components/ui/card'; import { Alert, AlertDescription } from '@/components/ui/alert'; import { RotateCw, Volume2, Forward, HelpCircle, RefreshCw } from 'lucide-react'; function SpeakAndSpell() { const successSoundRef = useRef(null); const errorSoundRef = useRef(null); const DIFFICULT_WORDS = [ 'accommodate', 'accordance', 'acquisition', 'ambiguous', 'bureaucracy', 'characteristic', 'circumstantial', 'coincidence', 'colleague', 'commemorate', 'commissary', 'commitment', 'committee', 'competitive', 'completely', 'concurrence', 'conflagration', 'conscientious', 'consistent', 'convenient', 'correspondence', 'definitely', 'development', 'difference', 'discipline', 'dissimilar', 'efficient', 'embarrass', 'especially', 'exhilarate', 'experience', 'fascinating', 'government', 'grandiloquent', 'guarantee', 'harassment', 'hierarchy', 'humorous', 'hypocrite', 'ignominious', 'immediate', 'incandescent', 'incredible', 'independent', 'industrial', 'infinitesimal', 'influential', 'inspiration', 'intelligence', 'maintenance', 'medieval', 'millennium', 'miniature', 'necessary', 'obstinate', 'occurrence', 'opportunity', 'parliament', 'perseverance', 'personnel', 'perspective', 'phenomenon', 'preference', 'prejudice', 'prevalent' ]; const KEYBOARD_LAYOUT = [ ['Q', 'W', 'E', 'R', 'T', 'Y', 'U', 'I', 'O', 'P'], ['A', 'S', 'D', 'F', 'G', 'H', 'J', 'K', 'L'], ['Z', 'X', 'C', 'V', 'B', 'N', 'M'] ]; const [availableWords, setAvailableWords] = useState([...DIFFICULT_WORDS]); const [currentWord, setCurrentWord] = useState(''); const [userInput, setUserInput] = useState(''); const [stats, setStats] = useState({ correct: 0, attempted: 0, skipped: 0 }); const [showSuccess, setShowSuccess] = useState(false); const [showError, setShowError] = useState(false); const [showHint, setShowHint] = useState(false); const [isLoading, setIsLoading] = useState(false); // Create beep oscillator for success/error sounds const playSuccessSound = () => { if (typeof window !== 'undefined' && window.AudioContext) { const audioContext = new AudioContext(); const oscillator = audioContext.createOscillator(); const gainNode = audioContext.createGain(); oscillator.connect(gainNode); gainNode.connect(audioContext.destination); oscillator.type = 'sine'; oscillator.frequency.setValueAtTime(800, audioContext.currentTime); // Higher pitch for success gainNode.gain.setValueAtTime(0.5, audioContext.currentTime); oscillator.start(); gainNode.gain.exponentialRampToValueAtTime(0.01, audioContext.currentTime + 0.5); oscillator.stop(audioContext.currentTime + 0.5); } }; const playErrorSound = () => { if (typeof window !== 'undefined' && window.AudioContext) { const audioContext = new AudioContext(); const oscillator = audioContext.createOscillator(); const gainNode = audioContext.createGain(); oscillator.connect(gainNode); gainNode.connect(audioContext.destination); oscillator.type = 'sine'; oscillator.frequency.setValueAtTime(200, audioContext.currentTime); // Lower pitch for error gainNode.gain.setValueAtTime(0.5, audioContext.currentTime); oscillator.start(); gainNode.gain.exponentialRampToValueAtTime(0.01, audioContext.currentTime + 0.5); oscillator.stop(audioContext.currentTime + 0.5); } }; useEffect(() => { if (!currentWord) { selectNewWord(); } }, [currentWord]); const selectNewWord = useCallback(() => { if (availableWords.length === 0) { setAvailableWords([...DIFFICULT_WORDS]); return; } const index = Math.floor(Math.random() * availableWords.length); const word = availableWords[index]; setAvailableWords(prev => prev.filter((_, i) => i !== index)); setCurrentWord(word); }, [availableWords]); const speakWord = (word) => { setIsLoading(true); const utterance = new SpeechSynthesisUtterance(word); const voices = window.speechSynthesis.getVoices(); const maleVoice = voices.find( voice => voice.name.includes('Male') || voice.name.includes('male') || voice.name.includes('David') || voice.name.includes('James') || voice.name.includes('Daniel') ); if (maleVoice) { utterance.voice = maleVoice; } utterance.rate = 0.9; utterance.pitch = 0.8; utterance.volume = 1.0; utterance.onend = () => setIsLoading(false); window.speechSynthesis.speak(utterance); }; const speakLetter = (letter) => { const utterance = new SpeechSynthesisUtterance(letter.toLowerCase()); const voices = window.speechSynthesis.getVoices(); const maleVoice = voices.find( voice => voice.name.includes('Male') || voice.name.includes('male') || voice.name.includes('David') || voice.name.includes('James') || voice.name.includes('Daniel') ); if (maleVoice) { utterance.voice = maleVoice; } utterance.rate = 0.9; utterance.pitch = 0.8; utterance.volume = 1.0; window.speechSynthesis.speak(utterance); }; const handleKeyPress = (letter) => { if (userInput.length < currentWord.length) { setUserInput(prev => prev + letter); speakLetter(letter); } }; const handleBackspace = () => { setUserInput(prev => prev.slice(0, -1)); }; useEffect(() => { const handlePhysicalKeyboard = (e) => { if (e.key === 'Backspace') { e.preventDefault(); handleBackspace(); } else if (e.key === 'Enter') { e.preventDefault(); handleSubmit(); } else if (e.key.length === 1 && e.key.match(/[a-zA-Z]/i)) { e.preventDefault(); handleKeyPress(e.key.toUpperCase()); } }; window.addEventListener('keydown', handlePhysicalKeyboard); return () => window.removeEventListener('keydown', handlePhysicalKeyboard); }, [userInput, currentWord]); const handleSkip = () => { setStats(prev => ({ ...prev, skipped: prev.skipped + 1 })); setUserInput(''); setShowHint(false); selectNewWord(); }; const handleSubmit = () => { if (userInput.toLowerCase() === currentWord.toLowerCase()) { setShowSuccess(true); setShowError(false); setStats(prev => ({ ...prev, correct: prev.correct + 1, attempted: prev.attempted + 1 })); playSuccessSound(); setTimeout(() => { setShowSuccess(false); setUserInput(''); setShowHint(false); selectNewWord(); }, 1500); } else { setShowError(true); setShowSuccess(false); setStats(prev => ({ ...prev, attempted: prev.attempted + 1 })); playErrorSound(); setTimeout(() => { setShowError(false); setUserInput(''); }, 1500); } }; const handleHint = () => { setShowHint(!showHint); }; const handleReset = () => { setAvailableWords([...DIFFICULT_WORDS]); setCurrentWord(''); setUserInput(''); setStats({ correct: 0, attempted: 0, skipped: 0 }); setShowSuccess(false); setShowError(false); setShowHint(false); }; return (