/blog_claude3.7_typing_test.tsx Secret
Created
March 4, 2025 10:33
Basic typing test application (generated by Claude 3.7 AI Model). This gist is for one of my blog post comparison of AI Models.
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
"use client"; | |
import { useEffect, useState, useRef, useCallback } from "react"; | |
export default function Home() { | |
const [text, setText] = useState(""); | |
const [userInput, setUserInput] = useState(""); | |
const [startTime, setStartTime] = useState<number | null>(null); | |
const [endTime, setEndTime] = useState<number | null>(null); | |
const [isActive, setIsActive] = useState(false); | |
const [rawWPM, setRawWPM] = useState(0); | |
const [adjustedWPM, setAdjustedWPM] = useState(0); | |
const [mistakes, setMistakes] = useState(0); | |
const inputRef = useRef<HTMLInputElement>(null); | |
const sampleTexts = [ | |
"The quick brown fox jumps over the lazy dog.", | |
"Coding is the art of telling a computer what to do.", | |
"Next.js is a React framework for building web applications.", | |
"TypeScript adds static type definitions to JavaScript for better tooling.", | |
"Practice makes perfect when it comes to improving your typing speed.", | |
]; | |
const getNewText = useCallback(() => { | |
const randomIndex = Math.floor(Math.random() * sampleTexts.length); | |
setText(sampleTexts[randomIndex]); | |
}, []); | |
useEffect(() => { | |
getNewText(); | |
}, [getNewText]); | |
const resetTest = () => { | |
setUserInput(""); | |
setStartTime(null); | |
setEndTime(null); | |
setIsActive(false); | |
setRawWPM(0); | |
setAdjustedWPM(0); | |
setMistakes(0); | |
getNewText(); | |
if (inputRef.current) { | |
inputRef.current.focus(); | |
} | |
}; | |
const handleInputChange = (e: React.ChangeEvent<HTMLInputElement>) => { | |
const input = e.target.value; | |
// Start the timer on the first keypress | |
if (!startTime && input.length === 1) { | |
setStartTime(Date.now()); | |
setIsActive(true); | |
} | |
// Count mistakes | |
let currentMistakes = 0; | |
for (let i = 0; i < input.length; i++) { | |
if (text[i] !== input[i]) { | |
currentMistakes++; | |
} | |
} | |
setMistakes(currentMistakes); | |
// Calculate WPM | |
if (startTime) { | |
const timeElapsed = (Date.now() - startTime) / 1000 / 60; // in minutes | |
const wordsTyped = input.length / 5; // Assuming 5 characters per word | |
const wordsCorrect = (input.length - currentMistakes) / 5; | |
setRawWPM(Math.round(wordsTyped / timeElapsed || 0)); | |
setAdjustedWPM(Math.round(wordsCorrect / timeElapsed || 0)); | |
} | |
setUserInput(input); | |
// Complete the test if user has typed the entire text | |
if (input.length === text.length) { | |
setEndTime(Date.now()); | |
setIsActive(false); | |
} | |
}; | |
// Render text with character comparisons | |
const renderText = () => { | |
return text.split("").map((char, index) => { | |
let className = ""; | |
if (index < userInput.length) { | |
// Character has been typed | |
className = | |
userInput[index] === char | |
? "text-green-500" | |
: "text-red-500 bg-red-100"; | |
} else if (index === userInput.length) { | |
// Current character to type | |
className = "bg-gray-200 text-gray-800"; | |
} | |
return ( | |
<span key={index} className={className}> | |
{char} | |
</span> | |
); | |
}); | |
}; | |
return ( | |
<div className="flex flex-col text-black items-center justify-center min-h-screen bg-gray-50 p-4"> | |
<div className="w-full max-w-2xl bg-white p-8 rounded-lg shadow-lg"> | |
<h1 className="text-3xl font-bold text-center mb-6">Typing Test</h1> | |
<div className="mb-6 p-4 bg-gray-100 rounded-lg min-h-[100px] text-lg leading-relaxed"> | |
{renderText()} | |
</div> | |
<div className="mb-6"> | |
<input | |
ref={inputRef} | |
type="text" | |
value={userInput} | |
onChange={handleInputChange} | |
className="w-full text-zinc-800 p-3 border border-gray-300 rounded text-lg" | |
placeholder="Start typing here..." | |
disabled={!isActive && userInput.length > 0} | |
autoFocus | |
/> | |
</div> | |
<div className="flex flex-col sm:flex-row gap-4 justify-between mb-6"> | |
<div className="stats flex gap-4"> | |
<div className="p-3 bg-blue-50 rounded-lg"> | |
<h2 className="font-semibold">Raw WPM</h2> | |
<p className="text-xl font-bold">{rawWPM}</p> | |
</div> | |
<div className="p-3 bg-green-50 rounded-lg"> | |
<h2 className="font-semibold">Adjusted WPM</h2> | |
<p className="text-xl font-bold">{adjustedWPM}</p> | |
</div> | |
<div className="p-3 bg-red-50 rounded-lg"> | |
<h2 className="font-semibold">Mistakes</h2> | |
<p className="text-xl font-bold">{mistakes}</p> | |
</div> | |
</div> | |
<button | |
onClick={resetTest} | |
className="bg-blue-500 text-white py-2 px-4 rounded hover:bg-blue-600" | |
> | |
{userInput.length > 0 ? "Reset Test" : "New Test"} | |
</button> | |
</div> | |
{endTime && startTime && ( | |
<div className="border-t pt-4"> | |
<h2 className="text-xl font-semibold mb-2">Test Results</h2> | |
<p> | |
Time: {((endTime - startTime) / 1000).toFixed(2)}s | Raw WPM:{" "} | |
{rawWPM} | Adjusted WPM: {adjustedWPM} | Accuracy:{" "} | |
{Math.round(((text.length - mistakes) / text.length) * 100)}% | |
</p> | |
</div> | |
)} | |
</div> | |
</div> | |
); | |
} |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment