Skip to content

Instantly share code, notes, and snippets.

@shricodev
Created March 4, 2025 10:33
Show Gist options
  • Select an option

  • Save shricodev/6b0bcf7c05034ec1bcc37f9b8258f68c to your computer and use it in GitHub Desktop.

Select an option

Save shricodev/6b0bcf7c05034ec1bcc37f9b8258f68c to your computer and use it in GitHub Desktop.
Basic typing test application (generated by Claude 3.7 AI Model). This gist is for one of my blog post comparison of AI Models.
"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