# Tempo Typing — Product Requirements (v3.4)

Tempo Typing is a minimalist, keyboard-first typing test. People type against time, a word count, a quote or custom text, see an honest scoring breakdown, and — when signed in — keep a history, personal bests, a streak and a place on weekly and all-time leaderboards protected by server-side anti-cheat.

**Problem.** A typing score is only worth sharing if it cannot be faked and is computed consistently; every figure on the result screen and on a leaderboard must follow from what was actually typed.

## Users
- **Practising typist** — A distraction-free test with accurate wpm, raw and accuracy, and control over difficulty and confidence rules.
- **Competitor** — Personal bests and leaderboards that only contain legitimate results.
- **Guest** — Try the test without an account and keep results on the device.

## Goals
- Scoring follows one formula everywhere: the UI, the saved result and the API agree.
- Difficulty, confidence and paste rules are enforced as documented.
- Only valid, long-enough, non-zen, non-failed results are saved and ranked.

## Typing engine and scoring (ENG)

How keystrokes become a result.

- **FR-ENG-01** (Must) A test on custom text ends on the last correct letter (no trailing space needed) and reports a full breakdown.
  - Typing "The quick brown fox jumps over the lazy dog" exactly ends the test as the final "g" is typed
  - Result shows acc 100%, characters 35/0/0/0 (correct/incorrect/extra/missed), test type "custom english", and wpm equal to raw
  - A guest sees "Saved on this device. Sign in to keep your history."
- **FR-ENG-02** (Must) Wrong and extra letters are counted in separate buckets, mark the word as wrong, and lower accuracy; net wpm counts only correct letters and correct spaces, so with errors wpm is lower than raw.
  - Typing "Thx quickk brown fox jumps over the lazy dog" gives characters 34/1/1/0 and acc 91% (title "90.91% (40 correct / 44 keypresses)")
  - wpm = (correct letters + correct spaces) / 5 per minute; raw = (all typed letters + spaces) / 5 per minute; acc = correct keypresses / total keypresses
- **FR-ENG-03** (Must) Pasting into the test is blocked: clipboard text types nothing and does not start the timer.
  - Toast "Pasting is disabled"; no letters are coloured
- **FR-ENG-04** (Should) With confidence mode off, Backspace on an empty word re-opens the previous word only if it was incorrect.
  - After "Thx", space, Backspace twice and "e" the first word reads correct
  - A correct previous word cannot be re-opened
  - The wrong keypress still counts against accuracy (below 100%)
- **FR-ENG-05** (Could) Clicking away blurs the words and shows "Click here or press any key to focus"; pressing a key refocuses and types it.
- **FR-ENG-06** (Could) "Repeat test" replays the identical word list and labels the type "words 10 english (repeated)".
- **FR-ENG-07** (Could) "Practice missed words" builds a test from only the words typed wrong; its result is not saved.
  - After a run with errors in "The" and "quick" the practice test is exactly "The quick"
  - Save line "Practice tests are not saved"

## Difficulty and confidence (DIF)

Rules that make a test stricter.

- **FR-DIF-01** (Must) Expert difficulty fails the test the moment an incorrect word is submitted with space; failed tests are not saved.
  - Typing "Thx" does not fail yet; pressing space shows "test failed" and "Expert: an incorrect word was submitted"
  - Save line "Failed tests are not saved"
- **FR-DIF-02** (Should) Master difficulty fails on the first incorrect key, without waiting for space.
  - Typing "Tx" shows "test failed" and "Master: an incorrect key was pressed"
- **FR-DIF-03** (Should) Confidence "on" makes a space final: after it, even a wrong word cannot be revisited, while Backspace still works inside the current word.
  - A "confidence on" badge is shown; "Thx", space, Backspace changes nothing
  - "qa" then Backspace leaves "q"
- **FR-DIF-04** (Must) Confidence "max" disables every Backspace, including Ctrl+Backspace.
  - A "confidence max" badge is shown; after "Thx", Backspace and Ctrl+Backspace leave "Thx" with the x still incorrect

## Modes and configuration (MOD)

Time, words, quote, zen and custom tests.

- **FR-MOD-01** (Must) A time test counts the timer down and ends on its own at exactly the chosen duration, adding words as the user types.
  - Time 15 shows the timer 15 → 1, then a result with time 15s and test type "time 15 english"
- **FR-MOD-02** (Should) Zen mode has no target text: Shift+Enter finishes it, accuracy shows "-", there is no "practice missed words", and the result is never saved.
  - Hint "start typing — shift + enter to finish"
  - Save line "Zen results are not saved"
- **FR-MOD-03** (Should) Quote mode in a language without quotes shows a notice and uses English quotes; the result shows the quote source.
  - Toast "No quotes available for spanish — showing english quotes"
  - Test type "quote short english" and a "source" stat
- **FR-MOD-04** (Could) A custom time accepts whole numbers 1–3600 only.
  - 0 and 3601 show "Enter a whole number between 1 and 3600"
  - 45 closes the dialog and the button reads "45"
- **FR-MOD-05** (Could) Custom text cannot be empty and its limit is validated; text cycles up to the word limit.
  - Empty text: "Text cannot be empty"
  - Word limit 0: "Word limit must be 1–1000 words"
  - "a b c" with word limit 7 shows "a b c a b c a"

## Command line, themes and settings (CMD)

Keyboard control and personalisation.

- **FR-CMD-01** (Should) Esc opens a fuzzy-search "Command line"; highlighting a theme previews it, Esc reverts and Enter applies and saves it.
  - "thmemb" highlights "Theme: ember" and previews its colours
  - Esc restores the previous colours; "theme ember" + Enter keeps ember and settings shows it selected
- **FR-CMD-02** (Could) With quick restart set to "esc", Esc restarts the test with fresh words and Ctrl+Shift+P opens the command line.
  - Hints read "esc restart test" and "ctrl + shift + p command line"
- **FR-CMD-03** (Could) "reset settings" asks "Are you sure?" with "yes, reset" and "cancel" before restoring defaults (theme carbon, caret |).
- **FR-CMD-04** (Should) Signing in applies the account’s saved settings over the local defaults.
  - A fresh tab shows the carbon theme (dark grey, yellow accent); after sign-in the seeded account’s lagoon theme (teal) is applied and highlighted in settings

## Accounts and sign-in (AUT)

Sign-in with email or username.

- **FR-AUT-01** (Must) Email or username plus password signs in; wrong credentials and empty fields show distinct errors.
  - Wrong password: an error alert and the sign-in page stays
  - Empty fields: "Enter your email or username and password"
  - Correct credentials open the account page
- **FR-AUT-02** (Could) Signed-out results are kept on the device and listed on the guest account page, where "clear local history" empties them.
  - Heading "guest" and one row in "local result history"; after clearing "No results yet."

## Account profile and history (ACC)

Stats, personal bests and result history.

- **FR-ACC-01** (Must) The profile header reflects the seeded history: join date, tests started, tests completed, time typing and current streak.
  - Seeded account: "joined 14 Feb 2026", tests started 41, tests completed 12, time typing 00:07:12, current streak 3 days
- **FR-ACC-02** (Must) Personal best tables show the best result per exact length; untouched lengths show "-".
  - time: 15 s 93 (98%), 30 s 88, 60 s 84, 120 s 76; tooltip on 15 s starts "92.80 wpm · 94.40 raw · 98.31% acc"
  - words: 10 → 89, 25 → 91, 50 → 83, 100 → "-"
- **FR-ACC-03** (Should) History lists results newest first, ten at a time, with "load more" and a mode filter; personal-best results carry a ♛ mark.
  - "10 of 12"; first row words 10 at 89.19 wpm; "load more" shows 12 rows, the last being custom at 63.82 wpm
  - Filter "quote": one row, 77.14 wpm "quote medium"
  - Filter "words": three rows (89.19, 90.70, 83.13) each with ♛
- **FR-ACC-04** (Must) A signed-in, completed time test is saved and the first result for a configuration is announced as a new personal best.
  - A new account’s first time 15 test shows "♛ New personal best!"
  - Account page then shows tests completed 1 and the 15-second PB equal to that wpm
- **FR-ACC-05** (Should) A test shorter than 15 seconds and with fewer than 10 words is not saved.
  - Nine words typed in under 15 seconds: "Test too short — result not saved"; history total stays 12

## Leaderboards (LDB)

All-time and weekly rankings for time 15 and time 60.

- **FR-LDB-01** (Should) All-time boards rank each account’s best english result for time 15 and time 60; accounts barred from leaderboards are excluded.
  - Time 15: top entry 148.20 wpm; the signed-in account is #8 with 92.80 ("Your rank: #8"), above an 88.04 entry
  - A barred account’s 152 wpm result is not listed
  - Time 60: top entry 126.30; the signed-in account #5 with 83.80
- **FR-LDB-02** (Must) Weekly boards only include results from the last 7 days.
  - Time 15 weekly: top entry 139.65; the 21-day-old 148.20 and the other all-time leader are gone; signed-in account #5 (92.80)
  - Time 60 weekly: top entry 121.75; signed-in account #3 with 77.20 because its 83.80 best is 9 days old

## Results and settings API (API)

Server-side re-scoring and validation.

- **FR-API-01** (Must) POST /api/typing/results re-scores every submission from its breakdown and rejects values that do not follow, a duration that does not match the time mode, zen results and any wpm above 350.
  - The documented example body returns 201 with result.wpm 134.4
  - wpm altered to 180: 400 "Result failed anti-cheat validation"
  - testDuration 30 for time 15: 400 "Test duration does not match the selected time"
  - mode "zen": 400 "Zen results are never saved"
  - GET /api/typing/leaderboards/30: 404 "Leaderboards exist for time 15 and time 60 only"
- **FR-API-02** (Could) PUT /api/typing/settings validates keys and values and stores accepted ones.
  - {"theme":"neon"}: 400 Invalid value for "theme"
  - {"fontSize":3}: 400 Unknown setting "fontSize"
  - {"theme":"ember"} is returned by the next GET

## Non-functional requirements

- **NFR-01** (Must) Security: results cannot be forged — saved wpm, raw and accuracy are always recomputed on the server and paste into the test is blocked.
- **NFR-02** (Could) Accessibility: the whole test, restart and command line are operable from the keyboard alone.

## Glossary and rules

- **wpm** — Net words per minute: (correct letters + correct spaces) / 5 per minute.
- **raw** — Words per minute counting every typed letter and space, right or wrong.
- **PB** — Personal best, tracked per exact configuration (mode, length, language, modifiers).
- **Confidence mode** — Setting that limits correcting earlier input: off, on (space is final) or max (no backspace).

## Out of scope

- Sign-up and password reset
- Sound output quality
- Exact wpm of human-typed runs
