Changed around line 1
- buildHtml
- theme roboto
+ 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';
- Hello World my name is
+ 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 (
+
+
+ {/* Handle section */}
+
+ {/* Main device body */}
+
+ {/* Speaker grille */}
+
+ {[...Array(24)].map((_, i) => (
+ ))}
+
+
+ {/* Display section with retro LED look */}
+
+ {/* Success/Error Messages */}
+ {showSuccess && (
+
+ )}
+ {showError && (
+
+ )}
+
+
+ style={{ color: '#32CD32', textShadow: '0 0 5px #32CD32' }}>
+ {userInput.split('').map((letter, index) => (
+
+ key={index}
+ className={
+ showHint && currentWord[index]?.toLowerCase() !== letter.toLowerCase()
+ ? 'text-red-500'
+ : ''
+ }
+ >
+ {letter}
+
+ ))}
+ {userInput.length < currentWord.length &&
+ {'_'.repeat(currentWord.length - userInput.length)}
+ }
+
+
+
+ {/* Control panel with yellow inset */}
+
+ {/* Control buttons */}
+
+
updated .gitignore
Changed around line 5
+ .*
initial blank_template template
Changed around line 1
+ .DS_Store
+ *.html
+ *.txt
+ *.xml
+ *.css
+ *.js
+ *.csv
+ requests.scroll
Changed around line 1
+ buildHtml
+ theme roboto
+
+ Hello World my name is