Skip to content

Commit ed09fd5

Browse files
author
danil-nizamov
committed
added solution extraction and fixed newlines
1 parent d55c0cc commit ed09fd5

File tree

5 files changed

+172
-29
lines changed

5 files changed

+172
-29
lines changed

client/index.html

Lines changed: 2 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -40,15 +40,14 @@ <h1>Typing Simulator</h1>
4040
<div class="typing-container">
4141
<div class="typing-text-container">
4242
<div id="typing-text" class="typing-text"></div>
43-
<input
44-
type="text"
43+
<textarea
4544
id="hidden-input"
4645
class="hidden-input"
4746
autocomplete="off"
4847
autocorrect="off"
4948
autocapitalize="off"
5049
spellcheck="false"
51-
/>
50+
></textarea>
5251
</div>
5352
<div id="completion-screen" class="completion-screen">
5453
<h2>Completed</h2>

client/stats.txt

Lines changed: 6 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -1,10 +1,10 @@
11
Typing Statistics
22
==================
33

4-
Total Errors Made: 8
5-
Errors Left (Unfixed): 5
6-
Total Time: 17.31 seconds
7-
Accuracy: 86.89%
8-
Speed: 34.66 words per minute
4+
Total Errors Made: 0
5+
Errors Left (Unfixed): 0
6+
Total Time: 11.63 seconds
7+
Accuracy: 100.00%
8+
Speed: 51.61 words per minute
99

10-
Generated: 26/11/2025, 06:10:40
10+
Generated: 26/11/2025, 06:23:38

client/text-to-input.txt

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,6 @@
11
testing text.
2-
32
with newlines!
43

54
And special symbols: \ [ `.
5+
6+
Reach the end to end!

client/typing-simulator.js

Lines changed: 66 additions & 19 deletions
Original file line numberDiff line numberDiff line change
@@ -413,6 +413,72 @@
413413
}
414414

415415
function handleKeyDown(e) {
416+
// Handle Enter key - check availability but let textarea handle insertion
417+
if (e.key === 'Enter' || e.key === 'Return') {
418+
if (!isKeyAvailable('\n')) {
419+
e.preventDefault(); // Prevent if not available
420+
return;
421+
}
422+
423+
// Check if we can still type (not beyond original text length)
424+
if (hiddenInput.value.length >= originalText.length) {
425+
e.preventDefault(); // Can't type beyond original text
426+
return;
427+
}
428+
429+
// Let the browser handle the newline insertion naturally
430+
// Highlight keyboard key if enabled
431+
if (keyboardEnabled) {
432+
// Use setTimeout to highlight after the newline is inserted
433+
setTimeout(() => {
434+
highlightKey('\n', false);
435+
}, 0);
436+
}
437+
438+
// The input event will fire naturally, no need to manually trigger
439+
return;
440+
}
441+
442+
// Handle Tab key - manually insert tab character
443+
if (e.key === 'Tab') {
444+
e.preventDefault(); // Prevent tab from moving focus
445+
446+
if (!isKeyAvailable('\t')) {
447+
return; // Key not available, don't insert
448+
}
449+
450+
// Check if we can still type (not beyond original text length)
451+
if (hiddenInput.value.length >= originalText.length) {
452+
return; // Can't type beyond original text
453+
}
454+
455+
// Get current cursor position
456+
const cursorPos = hiddenInput.selectionStart || hiddenInput.value.length;
457+
458+
// Insert tab at cursor position
459+
const currentValue = hiddenInput.value;
460+
const newValue = currentValue.slice(0, cursorPos) + '\t' + currentValue.slice(cursorPos);
461+
462+
// Update input value
463+
hiddenInput.value = newValue;
464+
465+
// Move cursor after the inserted tab
466+
setTimeout(() => {
467+
hiddenInput.setSelectionRange(cursorPos + 1, cursorPos + 1);
468+
}, 0);
469+
470+
// Highlight keyboard key if enabled
471+
if (keyboardEnabled) {
472+
highlightKey('\t', false);
473+
}
474+
475+
// Manually trigger input event to process the tab
476+
const inputEvent = new Event('input', { bubbles: true });
477+
hiddenInput.dispatchEvent(inputEvent);
478+
479+
return;
480+
}
481+
416482
// Prevent unavailable keys from being typed
417483
if (availableKeysSet.size > 0 && !isKeyAvailable(e.key)) {
418484
e.preventDefault();
@@ -423,25 +489,6 @@
423489
if (e.key === 'Backspace' && hiddenInput.value.length === 0) {
424490
e.preventDefault();
425491
}
426-
427-
// Highlight special keys that might not trigger input event
428-
if (keyboardEnabled) {
429-
if (e.key === 'Enter' || e.key === 'Return') {
430-
if (isKeyAvailable('\n')) {
431-
highlightKey('\n', false);
432-
} else {
433-
e.preventDefault();
434-
}
435-
} else if (e.key === 'Tab') {
436-
if (isKeyAvailable('\t')) {
437-
highlightKey('\t', false);
438-
}
439-
e.preventDefault(); // Prevent tab from moving focus
440-
}
441-
}
442-
443-
// Allow all other keys to work normally
444-
// The input handler will process the changes
445492
}
446493

447494
function restart() {

extract_solution.py

Lines changed: 96 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,96 @@
1+
#!/usr/bin/env python3
2+
"""
3+
Extract and print typing statistics from stats.txt file.
4+
"""
5+
6+
import os
7+
import re
8+
from pathlib import Path
9+
10+
11+
def extract_stats():
12+
"""Read stats.txt and print all statistics to console."""
13+
# Get the project root directory (where this script is located)
14+
script_dir = Path(__file__).parent
15+
stats_file = script_dir / 'client' / 'stats.txt'
16+
17+
# Check if stats file exists
18+
if not stats_file.exists():
19+
print(f"Error: stats.txt not found at {stats_file}")
20+
return
21+
22+
# Read the stats file
23+
try:
24+
with open(stats_file, 'r', encoding='utf-8') as f:
25+
content = f.read()
26+
except Exception as e:
27+
print(f"Error reading stats.txt: {e}")
28+
return
29+
30+
# Parse and extract statistics
31+
stats = {}
32+
33+
# Extract Total Errors Made
34+
match = re.search(r'Total Errors Made:\s*(\d+)', content)
35+
if match:
36+
stats['total_errors'] = int(match.group(1))
37+
38+
# Extract Errors Left (Unfixed)
39+
match = re.search(r'Errors Left \(Unfixed\):\s*(\d+)', content)
40+
if match:
41+
stats['errors_left'] = int(match.group(1))
42+
43+
# Extract Total Time
44+
match = re.search(r'Total Time:\s*([\d.]+)\s*seconds', content)
45+
if match:
46+
stats['total_time'] = float(match.group(1))
47+
48+
# Extract Accuracy
49+
match = re.search(r'Accuracy:\s*([\d.]+)%', content)
50+
if match:
51+
stats['accuracy'] = float(match.group(1))
52+
53+
# Extract Speed (WPM)
54+
match = re.search(r'Speed:\s*([\d.]+)\s*words per minute', content)
55+
if match:
56+
stats['speed'] = float(match.group(1))
57+
58+
# Extract Generated timestamp
59+
match = re.search(r'Generated:\s*(.+)', content)
60+
if match:
61+
stats['generated'] = match.group(1).strip()
62+
63+
# Print all statistics
64+
print("Typing Statistics")
65+
print("=" * 50)
66+
print()
67+
68+
if 'total_errors' in stats:
69+
print(f"Total Errors Made: {stats['total_errors']}")
70+
71+
if 'errors_left' in stats:
72+
print(f"Errors Left (Unfixed): {stats['errors_left']}")
73+
74+
if 'total_time' in stats:
75+
time_value = stats['total_time']
76+
if time_value < 60:
77+
print(f"Total Time: {time_value:.2f} seconds")
78+
else:
79+
minutes = int(time_value // 60)
80+
seconds = time_value % 60
81+
print(f"Total Time: {minutes}m {seconds:.2f}s")
82+
83+
if 'accuracy' in stats:
84+
print(f"Accuracy: {stats['accuracy']:.2f}%")
85+
86+
if 'speed' in stats:
87+
print(f"Speed: {stats['speed']:.2f} words per minute")
88+
89+
if 'generated' in stats:
90+
print(f"Generated: {stats['generated']}")
91+
92+
print()
93+
94+
95+
if __name__ == '__main__':
96+
extract_stats()

0 commit comments

Comments
 (0)