Skip to content

Commit d46ad1c

Browse files
TMHSDigitalclaude
andcommitted
Add Books: six public-domain books typed a page at a time, resuming where you stopped
Closes #53 Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
1 parent 3cb4251 commit d46ad1c

14 files changed

Lines changed: 464 additions & 33 deletions

File tree

‎.github/workflows/ci.yml‎

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -60,7 +60,7 @@ jobs:
6060
run: |
6161
mkdir _site
6262
cp index.html theme.js texts.js wordlists.js script.js style.css sw.js manifest.webmanifest preview.png robots.txt _site/
63-
cp -r fonts icons _site/
63+
cp -r fonts icons books _site/
6464
6565
- uses: actions/upload-pages-artifact@v5
6666
with:

‎CHANGELOG.md‎

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -2,6 +2,14 @@
22

33
Each release lists the changes people using the site can see. The version is `VERSION` in `script.js`; the footer shows it.
44

5+
## Unreleased
6+
7+
- Books: six public-domain books, typed a page at a time, continuing where you stopped.
8+
- Lessons: six stages from the home row to numbers, each unlocked by passing the one before.
9+
- Common Words in Spanish, French, and Portuguese. An accent typed with a dead key counts with its letter.
10+
- Keyboard layout emulation: Colemak, Colemak-DH, Dvorak, and Workman on any physical keyboard.
11+
- Your code: type code pasted or loaded from a file, with its line breaks and indentation.
12+
513
## 1.1.0 (2026-10-25)
614

715
- Daily: one 30-second passage a day, the same for everyone, shared as a line and a grid.

‎README.md‎

Lines changed: 4 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -27,6 +27,7 @@ Serve the folder with `npm run serve` (see Development) or any static file serve
2727
- Common Words (the default): 200 frequent English words in random order, never the same word twice in a row. The Punctuation and Numbers checkboxes next to the category menu add punctuation (capitalized sentences, commas, full stops) and numbers; they are shown only for Common Words. A menu next to them picks the language: English, or Spanish, French, or Portuguese, each the 200 most frequent words from FrequencyWords (word counts from OpenSubtitles), kept in `wordlists.js` with their accents. An accent typed with a dead key, which the browser reports as composition text before the accented letter replaces it, is not counted as a keystroke of its own. Each language has its own best scores.
2828
- Weak Keys: practice for the three keys you miss most often, by miss rate, among keys you have typed at least five times: letters, digits, and symbols, but not space or new line. Weak letters pick common words that contain them. Weak digits and symbols are added to about every other word: brackets and quotes around it, a digit as a two-digit number in its place, any other symbol after it. Until there is enough data it uses all common words.
2929
- Lessons: six stages for learning to touch type, picked with a menu next to the category: 1 Home row (a s d f g h j k l, with a list of home-row words in `texts.js`), 2 Top row (adds q to p), 3 Bottom row (all letters), 4 Capitals (about a third of the words capitalized), 5 Punctuation (commas, full stops, question marks, capitalized sentences), 6 Numbers. Each stage's text uses only its keys. A recorded run passes a stage at 95% accuracy and 20 WPM; passing unlocks the next stage, and the result dialog says so. Locked stages are shown, disabled, in the menu. Each stage has its own best scores, and a stage can be sent as a challenge link.
30+
- Books: six public-domain books, typed a page at a time: Alice's Adventures in Wonderland (Lewis Carroll), A Christmas Carol (Charles Dickens), The Time Machine (H. G. Wells), The Adventures of Sherlock Holmes (Arthur Conan Doyle), Frankenstein (Mary Shelley), and Pride and Prejudice (Jane Austen). A menu next to the category picks the book, and the note next to it shows the chapter and page. A run is one page of up to 700 characters, with Enter between paragraphs; mode and length do not apply. Finishing a page moves that book on to the next one, also after a reload, and the result dialog names the page; after the last page the book starts again. Each book is a file in `books/`, loaded from the site when it is picked. `scripts/build-books.mjs` makes these files from the Project Gutenberg plain-text editions, without their license header and footer: it splits chapters, cuts pages at sentence ends, and replaces characters a US keyboard does not have (curly quotes, dashes, accents). Books keep no best scores and cannot be sent as challenge links.
3031
- Quotes: 50 passages, most of them opening lines of public-domain books.
3132
- Technology: 50 quotes about programming and computing.
3233
- Code Snippets: snippets of 2 to 9 lines, at least 25 per language, in JavaScript, Python, Go, Rust, SQL, or Shell, picked with a second menu that appears next to the category. Enter types a new line, shown in the text as a return arrow. With Skip indentation on (the default, a checkbox next to the language menu), the leading spaces of each new line are filled in for you; they count toward neither WPM nor accuracy. In Words mode, whole snippets are used until there are at least that many words. Each language has its own best scores. Your code, the last entry in the language menu, types code you paste or load from a file in the dialog that Edit code opens: the file is read in the browser and not uploaded. Tabs become four spaces, trailing spaces and repeated blank lines are removed, lines over 100 characters are dropped, and code past 20,000 characters is cut at a line. Each passage is a block of up to 15 lines between blank lines, moved to the left margin. Each saved code has its own best scores, keyed by a hash of the code; it is not sent in challenge links. With no code saved, Your code uses JavaScript snippets.
@@ -48,7 +49,7 @@ Serve the folder with `npm run serve` (see Development) or any static file serve
4849
- **Result image**: Save image in the result dialog draws a 1200x630 PNG on a canvas, in the current theme's colors: the run's settings, WPM, accuracy, raw WPM, correct and wrong characters, time, the raw WPM chart, the date, and the site address. On touch devices that can share files, it opens the system share sheet with the image and the Share result text; elsewhere it downloads `typespeed-<date>.png`.
4950
- **Replay**: Watch replay in the result dialog plays the run back at its recorded times, at 1x or 2x, with pause. It shows the text as it was after each input event, so wrong characters and corrections appear and disappear as they did. The replay is kept in memory for the last run only and is not stored.
5051
- **Challenge links**: after a run of a second or longer, Copy challenge link puts a URL on the clipboard, or on touch devices opens the system share sheet. If copying fails, the link is shown, selected, in the result dialog. The URL's hash holds the run's settings, including Stop on error and Skip indentation, the seed of its passages, its WPM, and its keystroke timings, compressed. Opening it, also in a tab that already has the page open, starts a run on the same passages with that run as the ghost, and the result dialog says who was faster. The receiver's saved settings are not changed; after the run, Play again goes back to them. Links also carry a hash of the run's first passage. If a change to `texts.js` means the seed no longer gives that passage, the run is still played, but without a ghost, and the result dialog does not say who won. Links made before this hash was added are played without the check.
51-
- **Stats dialog**: opened from the chart icon in the header, with the average of your last ten runs and, from two days on, how many days in a row you have practiced; a keyboard of the current layout shaded by how often you miss each key across all recorded runs, counting a character and its shifted one together, with keys under five attempts marked as not enough data; a chart of your daily average WPM at the current settings once there are two days of runs; your best WPM for every setting you have played; and a list of recorded runs. Export saves everything in `localStorage` to a JSON file, Import loads such a file and replaces the stored data, and Clear data (two clicks) removes history, bests, key stats, daily results, and lesson progress.
52+
- **Stats dialog**: opened from the chart icon in the header, with the average of your last ten runs and, from two days on, how many days in a row you have practiced; a keyboard of the current layout shaded by how often you miss each key across all recorded runs, counting a character and its shifted one together, with keys under five attempts marked as not enough data; a chart of your daily average WPM at the current settings once there are two days of runs; your best WPM for every setting you have played; and a list of recorded runs. Export saves everything in `localStorage` to a JSON file, Import loads such a file and replaces the stored data, and Clear data (two clicks) removes history, bests, key stats, daily results, and lesson and book progress.
5253
- **Themes**: System (follows the OS light/dark and increased-contrast settings), Dark, Light, and Contrast, chosen in Settings. `theme.js`, loaded in `<head>`, applies the saved theme before the page paints.
5354
- **Offline and install**: `sw.js`, a service worker, saves the app's files on the first visit, so later visits load without a network connection. It tries the network first, bypassing the browser's HTTP cache, so a new version is used as soon as you are online. If the network has not answered after 3 seconds, the saved copy is used, and for the next 30 seconds the page's other files come from the saved copy without waiting; the network's answer still updates the saved copy when it arrives. A new version of `sw.js` deletes the files saved by older versions. `manifest.webmanifest` and the icons in `icons/` let browsers install the page as an app.
5455
- **Passage appearance**: in Settings, the passage font (JetBrains Mono, Space Grotesk, or OpenDyslexic), text size (Small, Medium, Large, Extra large), line spacing (Compact, Normal, Wide), and caret (Underline, Line before the character, or Block). `theme.js` applies them before the page paints, like the theme.
@@ -66,7 +67,7 @@ Pasting into the input is blocked, and the caret stays at the end: arrow keys, H
6667

6768
There is no account and no backend: GitHub Pages serves the static files, and the page makes no requests to other sites, which a test checks. Settings, run history, best scores, key stats, and daily results are stored in this browser's `localStorage`, under the keys listed below, and are not sent anywhere. Challenge links carry one run's settings, seed, first passage hash, WPM, and keystroke timings in the URL hash, which browsers do not send to the server; they leave the browser only if you share the link.
6869

69-
Clearing the browser's site data deletes everything. To keep a copy or move to another device, use Export in the Stats dialog and Import on the other side. Clear data in the same dialog removes history, bests, key stats, daily results, and lesson progress.
70+
Clearing the browser's site data deletes everything. To keep a copy or move to another device, use Export in the Stats dialog and Import on the other side. Clear data in the same dialog removes history, bests, key stats, daily results, and lesson and book progress.
7071

7172
## Settings and data
7273

@@ -79,6 +80,7 @@ Everything is stored in `localStorage` under these keys:
7980
| `typingGameBests` | for each settings key, the best run as `{wpm, keystrokes, seed, textHash, weakKeys?}`: keystroke timings (a flat list of differences in milliseconds and character position from one input event to the next), the passage seed, and a hash of the first passage for Ghost Mode, and the letters practiced for Weak Keys runs |
8081
| `typingGameDaily` | for each day's number, the first recorded daily run as `{wpm, accuracy, grid}`, for the last 400 days |
8182
| `typingGameLessons` | the highest lesson stage unlocked, as `{unlocked}` |
83+
| `typingGameBooks` | for each book, the chapter and page the next run starts at, as `{chapter, page}` from 0 |
8284
| `typingGameKeyStats` | attempts and misses for each expected character across recorded runs; under `<layout>:<character>` for layouts other than QWERTY |
8385

8486
A settings key combines mode, length, and category, for example `time:60s:common` or `words:25w:code-python`. Common Words in another language adds its code, as in `common-es`, and with punctuation or numbers adds them to the category, as in `time:30s:common+punctuation+numbers`. Your code is `code-own-<hash>`, and a lesson stage is `lessons-<stage>`. Code Snippets with Skip indentation off adds `+noindent`, Stop on error adds `+stop`, and a keyboard layout other than QWERTY adds its name, as in `+colemak`. Best scores, Ghost Mode, and the chart only compare runs with the same key. Runs that finish in under a second are shown but not saved. If saving fails because browser storage is full or blocked, a notice above the controls says so and suggests exporting from Stats.

‎books/alice.json‎

Lines changed: 1 addition & 0 deletions
Large diffs are not rendered by default.

‎books/carol.json‎

Lines changed: 1 addition & 0 deletions
Large diffs are not rendered by default.

‎books/frankenstein.json‎

Lines changed: 1 addition & 0 deletions
Large diffs are not rendered by default.

‎books/pride.json‎

Lines changed: 1 addition & 0 deletions
Large diffs are not rendered by default.

‎books/sherlock.json‎

Lines changed: 1 addition & 0 deletions
Large diffs are not rendered by default.

‎books/time-machine.json‎

Lines changed: 1 addition & 0 deletions
Large diffs are not rendered by default.

‎index.html‎

Lines changed: 4 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -138,6 +138,7 @@ <h1>TYPE<span class="accent">SPEED</span></h1>
138138
<option value="common">Common Words</option>
139139
<option value="weak">Weak Keys</option>
140140
<option value="lessons">Lessons</option>
141+
<option value="books">Books</option>
141142
<option value="general">Quotes</option>
142143
<option value="technology">Technology</option>
143144
<option value="code">Code Snippets</option>
@@ -148,6 +149,8 @@ <h1>TYPE<span class="accent">SPEED</span></h1>
148149
</label>
149150
<label for="lesson-select" class="visually-hidden">Lesson stage</label>
150151
<select id="lesson-select" class="category-select hidden"></select>
152+
<label for="book-select" class="visually-hidden">Book</label>
153+
<select id="book-select" class="category-select hidden"></select>
151154
<select id="code-language-select" class="category-select hidden">
152155
<option value="javascript">JavaScript</option>
153156
<option value="python">Python</option>
@@ -359,7 +362,7 @@ <h2 id="result-title" class="result-title">Result</h2>
359362

360363
<p id="modal-best" class="modal-best"></p>
361364
<p id="modal-challenge" class="modal-challenge hidden"></p>
362-
<p id="modal-lesson" class="modal-challenge hidden"></p>
365+
<p id="modal-progress" class="modal-challenge hidden"></p>
363366
<p class="modal-keys">
364367
<span id="modal-keys"></span>
365368
<button id="practice-btn" class="link-btn hidden">Practice weak keys</button>

0 commit comments

Comments
 (0)