Offline terminal typing tutor with keybr-style adaptive drills and per-layout stats
Project description
Keystrike
Adaptive drills for your weakest keys — an offline terminal typing tutor inspired by keybr.com. Stats stay on your machine; nothing phones home. Design follows research-backed typing pedagogy.
Install: pipx install keystrike · PyPI · Discussions
What it does
Keystrike tracks how fast and accurately you type each key, unlocks new letters as you hit your targets, and generates practice text that overweight your current weak spot. Switch keyboard layouts (QWERTY, Dvorak, Colemak, Colemak Mod-DH, or your own TOML) and each layout keeps its own history.
Why it works
The adaptive engine applies principles from typing research and tools like Keybr — see the Typing pedagogy wiki for sources and how each maps to the code.
- Accuracy before speed — a new key unlocks only when every letter in your current set is fast and accurate; high speed with lots of errors does not advance you.
- Targets your weak keys — generated text overweight keys and bigrams you still miss, instead of generic full-alphabet drills.
- Home row first — unlock order follows keyboard rows (home → top → bottom), matching standard touch-typing curricula and reach-distance effects in keystroke timing.
- Word-level practice — Markov word chunks build sequence memory; skilled typing is controlled at the word and bigram level, not isolated letters.
- Spaced review — keys you have not practiced recently resurface in lessons before they fade.
Features
- Adaptive engine — row-weighted key unlock order, speed+accuracy confidence gates, Markov word drills with a guaranteed focus letter.
- Per-layout stats — heatmap with per-key confidence and urgency; WPM/accuracy trends; press a key on the heatmap for that key's history.
- Daily learn budget — optional cap on adaptive minutes per calendar day.
- Custom layouts — drop
*.tomlfiles into your config layouts directory; no restart needed. - Git backup sync — optional CLI to push/pull settings and sessions to a private remote (union-merge sessions, last-write-wins settings).
- Offline by default — JSONL session logs and a local stats cache under platformdirs paths; sync is opt-in.
Install
Requires Python 3.12+ and a terminal with raw-mode keyboard input (Terminal setup).
pipx install keystrike # or: uv tool install keystrike
keystrike # launch TUI (default)
keystrike --version
Or from source:
git clone https://github.com/egno/keystrike
cd keystrike
uv sync --no-dev
uv run keystrike
Usage
Home
| Key | Action |
|---|---|
Enter |
Start adaptive practice |
s |
Stats |
o |
Settings |
l |
Cycle layout |
Ctrl+Q |
Quit |
On other screens, Esc / q goes back. Settings saves with Ctrl+S.
Practice
Adaptive mode disables backspace — mistakes stay in the record so confidence scores stay honest. The HUD shows live accuracy, remaining daily learn time, and the current focus key. When a session ends, the next lesson starts automatically unless you go back. Hitting your daily goal is shown in the HUD only — practice continues.
Stats
Recent sessions are listed below the heatmap. Press any key on the heatmap to
drill into that key's confidence trend; Esc / q returns to the overview.
Settings
| Setting | Default | Notes |
|---|---|---|
| Layout | qwerty |
Any bundled or custom layout |
| Target speed | 46 WPM | Or CPM — your unlock threshold |
| Letters unlocked up front | 16 | Force-unlocked before confidence gating |
| Daily learn goal | 10 min | 0 = no goal |
Backup sync
Opt-in git sync for backing up or moving data between machines. Requires git on
PATH and a private remote (sessions contain your typing history).
Not Keystrike cloud sync — only runs when you invoke keystrike sync. Background and FAQ: discussion #6.
keystrike sync init git@github.com:you/keystrike-backup.git # one-time
keystrike sync push # local → remote
keystrike sync pull # remote → local (rebuilds stats cache)
keystrike sync status # diff summary
Sync merges sessions by session_id (union), resolves settings by updated_at
(last-write-wins), and copies layout files both ways. The stats cache is excluded —
pull triggers a rebuild.
Data locations
| Path | Contents |
|---|---|
~/.config/keystrike/settings.toml |
User settings |
~/.config/keystrike/layouts/ |
Custom layout TOML files |
~/.config/keystrike/sync.toml |
Sync remote config (after sync init) |
~/.local/share/keystrike/sessions/ |
Session JSONL logs (Linux) |
~/Library/Application Support/keystrike/ |
Same on macOS |
Windows uses %APPDATA% / %LOCALAPPDATA% equivalents via platformdirs.
Terminal setup
Keystrike captures every keystroke in raw mode via Textual.
Works out of the box in Terminal.app, iTerm2, and most Linux terminals.
Windows
Use Windows Terminal —
the legacy conhost host has unreliable raw-mode support. Run keystrike inside WT,
not cmd.exe. Raw mode is not verified on every Windows build; please
open an issue if something breaks.
Development
uv sync # runtime + dev (pytest, pyright)
uv run pytest -q
uv run pyright
uv sync --all-groups # + ruff + snapshot tests (desktop only)
uv run ruff check
Dependency groups: dev (default, pure Python — safe on Termux), lint (Ruff),
snapshot (pytest-textual-snapshot). Keep native-wheel tools out of dev; see
pyproject.toml for why.
Regenerate the demo GIF after UI snapshot changes:
uv sync --group snapshot
uv run pytest tests/presentation/test_snapshots.py --snapshot-update
uv pip install pillow cairosvg # one-off; system cairo on macOS/Linux
uv run python scripts/generate_demo_gif.py
License
MIT
Project details
Download files
Download the file for your platform. If you're not sure which to choose, learn more about installing packages.
Source Distribution
Built Distribution
Filter files by name, interpreter, ABI, and platform.
If you're not sure about the file name format, learn more about wheel file names.
Copy a direct link to the current filters
File details
Details for the file keystrike-1.2.1.tar.gz.
File metadata
- Download URL: keystrike-1.2.1.tar.gz
- Upload date:
- Size: 67.9 kB
- Tags: Source
- Uploaded using Trusted Publishing? Yes
- Uploaded via: uv/0.12.0 {"installer":{"name":"uv","version":"0.12.0","subcommand":["publish"]},"python":null,"implementation":{"name":null,"version":null},"distro":{"name":"Ubuntu","version":"24.04","id":"noble","libc":null},"system":{"name":null,"release":null},"cpu":null,"openssl_version":null,"setuptools_version":null,"rustc_version":null,"ci":true}
File hashes
| Algorithm | Hash digest | |
|---|---|---|
| SHA256 |
f0a87b77b22623da5b33e656aa714c6ae261a22fe9733835ed92788be2a07961
|
|
| MD5 |
975ce856f3f8f8b69fc255738765553e
|
|
| BLAKE2b-256 |
67ee91f3d8d82acbb6974250fe492b69fb31b4f7b13a9df0fbf1fe44ed4533a6
|
File details
Details for the file keystrike-1.2.1-py3-none-any.whl.
File metadata
- Download URL: keystrike-1.2.1-py3-none-any.whl
- Upload date:
- Size: 89.2 kB
- Tags: Python 3
- Uploaded using Trusted Publishing? Yes
- Uploaded via: uv/0.12.0 {"installer":{"name":"uv","version":"0.12.0","subcommand":["publish"]},"python":null,"implementation":{"name":null,"version":null},"distro":{"name":"Ubuntu","version":"24.04","id":"noble","libc":null},"system":{"name":null,"release":null},"cpu":null,"openssl_version":null,"setuptools_version":null,"rustc_version":null,"ci":true}
File hashes
| Algorithm | Hash digest | |
|---|---|---|
| SHA256 |
6e8f9478dfed8dba56538babf26dfc53f4340aad3231aa5c502e2be52cc77b62
|
|
| MD5 |
f2eb923b1f8e4ce16fb0cd49c28ae9da
|
|
| BLAKE2b-256 |
b6992d7e93abc1e14b8133a91fc0cb1c2d19b44b8fca3954a113db45f29c1e20
|