File size: 4,686 Bytes
8725cc4 0ce34cb 34f302f 8725cc4 0ce34cb 34f302f 8725cc4 0ce34cb 8725cc4 0ce34cb 8725cc4 769c038 34f302f 769c038 0ce34cb 8725cc4 34f302f 8725cc4 0ce34cb 8725cc4 34f302f 8725cc4 34f302f 8725cc4 34f302f 8725cc4 769c038 8725cc4 0ce34cb 8725cc4 |
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 |
import { Button } from "@/components/ui/button";
import { Input } from "@/components/ui/input";
import { motion } from "framer-motion";
import { KeyboardEvent, useRef, useEffect, useState } from "react";
import { useToast } from "@/hooks/use-toast";
interface SentenceBuilderProps {
currentWord: string;
successfulRounds: number;
sentence: string[];
playerInput: string;
isAiThinking: boolean;
onInputChange: (value: string) => void;
onSubmitWord: (e: React.FormEvent) => void;
onMakeGuess: () => void;
}
export const SentenceBuilder = ({
currentWord,
successfulRounds,
sentence,
playerInput,
isAiThinking,
onInputChange,
onSubmitWord,
onMakeGuess,
}: SentenceBuilderProps) => {
const inputRef = useRef<HTMLInputElement>(null);
const [imageLoaded, setImageLoaded] = useState(false);
const imagePath = `/think_in_sync_assets/${currentWord.toLowerCase()}.jpg`;
const { toast } = useToast();
useEffect(() => {
const img = new Image();
img.onload = () => setImageLoaded(true);
img.src = imagePath;
console.log("Attempting to load image:", imagePath);
}, [imagePath]);
// Focus input on initial render
useEffect(() => {
setTimeout(() => {
inputRef.current?.focus();
}, 100);
}, []);
// Focus input after AI finishes thinking
useEffect(() => {
if (!isAiThinking && sentence.length > 0 && sentence.length % 2 === 0) {
setTimeout(() => {
inputRef.current?.focus();
}, 100);
}
}, [isAiThinking, sentence.length]);
const handleKeyDown = (e: KeyboardEvent<HTMLInputElement>) => {
if (e.shiftKey && e.key === 'Enter') {
e.preventDefault();
if (playerInput.trim()) {
handleSubmit(e as any);
}
// Make the guess immediately without waiting for AI response
onMakeGuess();
}
};
const handleSubmit = (e: React.FormEvent) => {
e.preventDefault();
const input = playerInput.trim().toLowerCase();
const target = currentWord.toLowerCase();
// Check if the input contains only letters
if (!/^[a-zA-Z]+$/.test(input)) {
toast({
title: "Invalid Word",
description: "Please use only letters (no numbers or special characters)",
variant: "destructive",
});
return;
}
if (input.includes(target)) {
toast({
title: "Invalid Word",
description: `You cannot use words that contain "${currentWord}"`,
variant: "destructive",
});
return;
}
onSubmitWord(e);
};
return (
<motion.div
initial={{ opacity: 0 }}
animate={{ opacity: 1 }}
className="text-center"
>
<h2 className="mb-4 text-2xl font-semibold text-gray-900">
Build a Description
</h2>
<p className="mb-6 text-sm text-gray-600">
Take turns with AI to describe your word without using the word itself!
</p>
<div className="mb-4 overflow-hidden rounded-lg bg-secondary/10">
{imageLoaded && (
<img
src={imagePath}
alt={currentWord}
className="mx-auto h-48 w-full object-cover"
/>
)}
<p className="p-4 text-2xl font-bold tracking-wider text-secondary">
{currentWord}
</p>
</div>
<div className="mb-6 rounded-lg bg-gray-50 p-4">
<p className="text-lg text-gray-800">
{sentence.length > 0 ? sentence.join(" ") : "Start your sentence..."}
</p>
</div>
<form onSubmit={handleSubmit} className="mb-4">
<Input
ref={inputRef}
type="text"
value={playerInput}
onChange={(e) => {
// Only allow letters in the input
const value = e.target.value.replace(/[^a-zA-Z]/g, '');
onInputChange(value);
}}
onKeyDown={handleKeyDown}
placeholder="Enter your word (letters only)..."
className="mb-4"
disabled={isAiThinking}
/>
<div className="flex gap-4">
<Button
type="submit"
className="flex-1 bg-primary text-lg hover:bg-primary/90"
disabled={!playerInput.trim() || isAiThinking}
>
{isAiThinking ? "AI is thinking..." : "Add Word ⏎"}
</Button>
<Button
type="button"
onClick={onMakeGuess}
className="flex-1 bg-secondary text-lg hover:bg-secondary/90"
disabled={(!sentence.length && !playerInput.trim()) || isAiThinking}
>
{isAiThinking ? "AI is thinking..." : "Make AI Guess ⇧⏎"}
</Button>
</div>
</form>
</motion.div>
);
}; |