Learn the keyboard shortcuts for common apps
Project description
keypal
Spaced-repetition practice for keyboard shortcuts. Build muscle memory for the shortcuts you actually use, scheduled with FSRS.
Why a desktop app?
- Browsers can't capture most shortcuts. Browsers intercept Ctrl+W, Ctrl+T, Ctrl+N, Ctrl+Q, etc. at the OS level before JavaScript sees them.
- Terminals intercept some shortcuts too. Terminal emulators eat shortcuts like Ctrl+Shift+F, and terminal protocols have byte collisions (Ctrl+H = Backspace, Ctrl+I = Tab).
- A Qt window captures keys cleanly. No byte collisions, no terminal-emulator interception, full modifier support.
Install
Linux (no Python required): download the .flatpak from GitHub Releases and install it:
flatpak install KeyPal-x.x.x-x86_64.flatpak
Any platform with uv installed:
uv tool install keypal
Or run without installing:
uvx keypal
To run from source (you may need to uv tool install rust-just first):
git clone https://github.com/treyhunner/keypal
cd keypal
uv sync
just run
Built-in packs
| Pack | What |
|---|---|
readline |
Common bash / readline shortcuts (Ctrl+A, Alt+F, Ctrl+W, etc.) |
python_repl |
Python 3.13+ REPL: F1/F2/F3 modes, Tab autocomplete, plus shared readline overlap |
tmux |
Your live tmux config, parsed from tmux list-keys at startup |
obsidian |
Your live Obsidian hotkeys, read from ~/.config/obsidian/.../hotkeys.json |
The tmux and obsidian packs are hybrid: a curated list of common shortcuts with friendly descriptions, with your actual key bindings substituted in if you have customs. Change a binding in tmux or Obsidian, restart keypal, and the pack reflects it.
A shared_id mechanism lets shortcuts in different packs share the same FSRS card. Practice Ctrl+A in readline and it counts toward python_repl too (since the Python REPL uses readline under the hood).
Using the app
Home screen
| Key | Action |
|---|---|
| Arrow keys | Navigate between packs |
| Enter | Start practicing the highlighted pack |
| P | Practice checked packs (multi-pack session) |
| X | Toggle pack checkbox for multi-pack selection |
| B | Browse the highlighted pack (read-only cheat sheet) |
| C | Settings (new cards per session, timing thresholds) |
| S | Stats (cards by FSRS state, per-pack progress) |
| D | Diagnostic screen for testing what key events are sent |
| Q | Quit |
In a quiz session
| Key | Action |
|---|---|
| (the shortcut) | Submit your answer |
| Space | "I don't know" (treated as wrong) |
| Enter | Continue after a correct answer |
| Y | After wrong: claim "I actually had it right" and save it as an alias |
| F4 | Skip this shortcut forever (won't appear in future sessions) |
| Esc | Back to home |
After a correct answer, three dots fill in left to right over three seconds, then auto-advances. Press Enter any time to skip the wait.
After a wrong answer
You'll see what you pressed (red), the expected combo (green), and a hint with your options. Press the correct combo to advance with practice credit, or use the keys above to skip / claim correct / dismiss.
Storage
Your data lives in ~/.local/share/keypal/ on Linux (platform-equivalent elsewhere via platformdirs):
cards.json: FSRS state for each shortcut you've practicedreview_log.jsonl: append-only log of every reviewaliases.json: alternative key combos you've taught keypal (via Y on a wrong answer)disabled.json: shortcuts you've dismissed with F4seen.json: tracks which shared shortcuts have been introduced in each packselected_packs.json: which packs are checked for multi-pack practicesettings.json: your settings (new cards per session, threshold overrides)
Override the location with the KEYPAL_DATA_DIR environment variable.
Limitations
- Your desktop environment may intercept a few shortcuts (e.g. Alt+F4 to close windows). Use the diagnostic screen (D) to confirm what keypal receives.
- Keys that physically exist on the keyboard but aren't in keypal (rare combos, complex chords beyond two keys) need to be added to a pack manually.
Contributing
See AGENTS.md for code conventions, project layout, and common gotchas.
just qa # format + lint + test
just test # tests only
Project details
Release history Release notifications | RSS feed
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 keypal-0.3.0.tar.gz.
File metadata
- Download URL: keypal-0.3.0.tar.gz
- Upload date:
- Size: 28.1 kB
- Tags: Source
- Uploaded using Trusted Publishing? No
- Uploaded via: uv/0.11.19 {"installer":{"name":"uv","version":"0.11.19","subcommand":["publish"]},"python":null,"implementation":{"name":null,"version":null},"distro":{"name":"Ubuntu","version":"22.04","id":"jammy","libc":null},"system":{"name":null,"release":null},"cpu":null,"openssl_version":null,"setuptools_version":null,"rustc_version":null,"ci":null}
File hashes
| Algorithm | Hash digest | |
|---|---|---|
| SHA256 |
7e6bbbc10ec0128e2b71242859c6ddd852b4b6e9c5e87c8997d58bf9758767ed
|
|
| MD5 |
124c6c64950c6e7e38718b223a866913
|
|
| BLAKE2b-256 |
de5320ddc6b798106e3ac0f3a35cd5853123bf02f496c99b4323b3d957011571
|
File details
Details for the file keypal-0.3.0-py3-none-any.whl.
File metadata
- Download URL: keypal-0.3.0-py3-none-any.whl
- Upload date:
- Size: 36.4 kB
- Tags: Python 3
- Uploaded using Trusted Publishing? No
- Uploaded via: uv/0.11.19 {"installer":{"name":"uv","version":"0.11.19","subcommand":["publish"]},"python":null,"implementation":{"name":null,"version":null},"distro":{"name":"Ubuntu","version":"22.04","id":"jammy","libc":null},"system":{"name":null,"release":null},"cpu":null,"openssl_version":null,"setuptools_version":null,"rustc_version":null,"ci":null}
File hashes
| Algorithm | Hash digest | |
|---|---|---|
| SHA256 |
dbe6bd537e7b08f68330ed2320186e90881115a720c6a6781c106d803db1bb71
|
|
| MD5 |
ac8024bfce69e3c5727da3956f0fa484
|
|
| BLAKE2b-256 |
debaad934a597dade778327fb06640f68951a88bf1d929a0b045dbbb2682eb5d
|