Skip to content

v0.7.0 — practice modes: missed words, stop on error, blind, pace cursor, cursor styles, idle check, custom themes - #21

Merged
withrvr merged 14 commits into
mainfrom
feature/v0.7.0
Oct 11, 2026
Merged

withrvr merged 14 commits into
mainfrom
feature/v0.7.0

Conversation

@withrvr

@withrvr withrvr commented Oct 11, 2026 •

Copy link
Copy Markdown
Owner

v0.7 — Practice modes

Every item on the roadmap's v0.7 list, one commit per step — plan and status in docs/ROADMAP.md. Closes #20.

Added

  • Practise missed words — each word records a wrong key (even if fixed); sessions save mistyped (≤ 200). missed menu row / --missed / mode = "missed": the 50 most-missed words of the last 100 sessions, shuffled, ≥ 25 words.
  • F6 on Results repeats the run (same words) as a replay; Tab restarts while typing.
  • [typing] section — config, Settings rows, typerush config keys:
    • live_wpm (hide live numbers), blind (--blind: no right/wrong colours, numbers at the end)
    • stop_on_error = letter | word (--stop-on-error); refused keys count as mistakes and tint the cursor
    • cursor = underline | block | bar (bar = the terminal's own cursor, shape restored on exit/panic)
    • sound = off | errors | keys (the terminal bell — no audio dependency)
    • pace = off | best | WPM (--pace): a ghost cursor at your mode best (replay: the original) or a target
    • idle_limit (default 10 s, 0 = off): idle runs aren't saved; Results says why
  • Custom themes from ~/.typerush/themes/*.toml (base + colour slots), picked everywhere like built-ins; --list-themes lists them and notes text under 4.5:1.

Changed

  • Footers: typing Tab / F5 restart; Results gains F6 repeat. Help lists both (still fits 80×24).
  • Settings scrolls with the selection on short terminals (13 rows now).
  • The menu's trailing blank line gives way before the banner, so the full menu still fits 80×24 with the new row.

Also (asked for after use)

  • Results chart axes: round labels (1/2/2.5/5 × 10ⁿ), ≥ 4 per axis, as many as fit; x labels drawn on their values (ratatui's own sit in equal slots, off their ticks).
  • Stats WPM trend: as many sessions as the box is wide (was 20), titled with the count.
  • Stats WPM trend stars: a ★ row under the bars marks each session holding its mode's best.
  • Help padding: a blank line above and below the text (still fits 80×24).
  • UI update: minior ui updates where this are not stuck to outerlines or something like that there is proper padding and showcase #20 — nothing against a box border: 2-cell padding on the chart, trend and Stats tables; help box wide enough; no ➤ in Settings' first cell. nothing_touches_a_box_border guards every screen.

Caught in review / E2E

  • Settings cut off its last rows on short terminals → scrolls.
  • Menu no longer fit 80×24 with snippets → trailing blank gives way first.
  • Theme files sorted by bytes → case-insensitive; --list-themes flattened TOML errors → line by line.

Final review (all fixed, each with a test)

  • Chart x labels panicked at 8–9 columns wide → skip/clamp safely; no_screen_panics_at_any_size sweeps every screen at widths 1–30.
  • F6 after a > 64 KiB run repeated one empty word → uses the run's words.
  • Stop-on-word: a refused right space counted as a Space mistake → only stops.
  • Zen showed "not saved: idle" → idle check skips zen.
  • pace = best on daily raced any day's best → today's best on today's words.
  • Settings typing rows showed file values, not --blind/--pace in use → show/change live values.
  • Tidy: one stats-path source (App::stats_file via App::history), one helper for typing rows, pace offsets only when pacing.

Test plan

  • cargo fmt --check, check, test (357 tests), clippy -D warnings, MSRV 1.88, cargo audit, cargo package
  • no_terminal_colors_anywhere extended to block/bar cursors, pace, refused key, blind, idle Results
  • Release binary in tmux (throwaway HOME): refused key + block cursor, Tab restart, F6 repeat, missed-words error then a real missed run, idle run not saved, bar cursor position/visibility + bell, custom theme listed/set/painted (0 default-background cells), block/bar on monokai (0)

Docs: CHANGELOG [0.7.0], USAGE, ARCHITECTURE, LEARNINGS, README, CONTRIBUTING, ROADMAP, contributor skill. Version 0.7.0 (not tagged).

Ordered steps for the v0.7 branch: Tab restart and F6 repeat; hide live
WPM and blind mode in a new [typing] section; stop on error; cursor
styles and a key sound; a pace cursor; practising missed words; idle
detection; custom themes from files.
- Tab while typing restarts with new words, like F5 and Ctrl+R — it is
  never in the text, which is split on whitespace. The typing footer now
  says "Tab / F5 restart".
- F6 on Results types the run just finished again, the same words: a
  replay of it (App::repeat_run), saved as +replay since the words are now
  seen, and compared with it on Results. On a replay, F6 replays the
  original again. The Results footer gains "F6 repeat" (Ctrl+C, which
  quits everywhere, makes room).
- The help overlay lists both and still fits 80x24.
A new [typing] section in config.toml (and options::TypingOptions in the
app) holds how a run behaves and looks. Its first two settings:

- live_wpm (default true): false hides WPM and accuracy while typing;
  the clock and mode still show.
- blind (default false, or --blind for one launch): typed text is one
  neutral color, right or wrong — a wrong space isn't marked either — and
  no numbers show until Results. You see where you are, not how you're
  doing.

Both are rows on the Settings screen (saved and used at once), keys for
typerush config (typing.live_wpm, typing.blind), and commented defaults
in the starter config; --blind applies even when the config file is
broken, like every other flag.
typing.stop_on_error (Settings, typerush config, --stop-on-error):

- letter: a wrong key doesn't move the cursor — it counts as a mistake
  and you type the right one;
- word: wrong letters are typed, but a word with a mistake in it can't be
  left: the space (or, for the last word, the end of the run) waits until
  it is fixed.

A refused key counts against accuracy like any mistake and turns the
cursor the mistake color until the right key or a backspace comes (not in
blind mode). typerush config gains a reusable choice-of-names kind of
value; an unknown name in the file warns and means off.
typing.cursor (Settings, typerush config): underline (as before), block
(the character in a solid block of the accent) or bar. The bar is the
terminal's own cursor, shaped to a steady bar and placed on the next
character, so it takes no room in the text and the character keeps its
style; the shape is set when the setting changes and put back to the
terminal's default on exit and in the panic hook.

typing.sound: off, errors (a wrong or refused key) or keys (every key).
It's the terminal's bell — one byte written after the key, so no audio
library and nothing to install; terminals that flash instead of beep
flash. The [typing] names share one resolver that warns on an unknown
name.
typing.pace (Settings, typerush config, --pace): off, best, or a WPM from
1 to 300 (written as a number). While typing, the character the pace has
reached — the text read as one stream, spaces included — is underlined in
the secondary color, so you can see whether you're ahead or behind; the
real cursor wins where they meet.

best is the best for the mode being played, looked up when the run
begins (App::history reads stats.json then if no screen has yet — never
per frame); on a replay it races the original run's speed. Zen keeps no
score, so it has no pace, and neither does a mode with no best yet.
Each word now remembers whether a wrong key was pressed in it or on its
space — even if it was fixed — and a saved session keeps those words
(SessionRecord::mistyped, at most 200; left out of the file when none).

A new `missed` menu row (after daily), --missed, or mode = "missed" as
the start mode builds a run from them: from the last 100 sessions, the 50
words missed most often (the most recent first on a tie), shuffled and
repeated to at least 25 words. The words come from stats.json, so they
are filtered like a custom file's. With none yet, it says so: they're
kept from TypeRush 0.7 on.

To keep the whole menu on 80x24 with a snippet library, the blank line
under the last option now gives way when it is all that doesn't fit —
after the gaps between categories, before the banner.
typing.idle_limit (Settings, typerush config): when a run has a stretch
of that many whole seconds or more without a key — 10 by default, 0 to
turn it off, up to 600 — it isn't saved, and Results' title says why
("not saved: 34 s without a key (the limit is 10 s)"). It counts from
the per-second tally, so paused time never counts as idle, and walking
away from a time run until its clock runs out does.
Each TOML file in ~/.typerush/themes/ is a theme named after the file
(ocean.toml is `ocean`): base = "<built-in>" (dark when left out) and
any of the color slots [colors] takes. They're read once at start and
then picked like the built-in ones — theme = "ocean", --theme, the
Settings screen, typerush config set theme, --list-themes (custom ones
after the built-ins).

A file is held to the config's limits (regular file, at most 1 MiB), a
name of letters, digits, - and _, and can't take a built-in's name; one
that can't be read is left out with a warning in the startup modal, and
a color that doesn't parse keeps the base's slot. --list-themes also
notes custom text colors under 4.5:1 on their background, the level the
built-in themes meet. The WCAG contrast function moves from the built-in
theme test to theme::color for that.
- Settings has thirteen rows now. On a terminal too short for all of
  them the bottom rows — reset to defaults among them — were cut off with
  no way to reach them; the list now scrolls to keep the selected row in
  view, its clickable parts follow, and the border says there's more.
- The every-screen theme test now also draws the block cursor with a
  pace cursor and a refused key, the bar cursor in blind mode, and a
  Results screen for a run left idle.
USAGE: Tab and F6, practising missed words, the typing options
(live wpm, blind, stop on error, cursor, key sound, pace, idle limit)
with their config names, your own themes, the Settings scroll; the
monokai row no longer names other products. ARCHITECTURE: options.rs and
theme/custom.rs, how each practice option is built, and the steps for a
new typing option. LEARNINGS: what was caught before shipping and why each
feature is built the way it is. README, CONTRIBUTING and the contributor
skill follow; CHANGELOG gains [0.7.0]; ROADMAP marks v0.7 done and says
nothing is planned past it yet.
…t a border

Closes #20.

- Results chart: both axes labelled on round numbers (1, 2, 2.5 or 5
  times a power of ten), at least four labels each and as many as fit —
  one per two rows up the side, one per seven cells along the bottom —
  each axis ending on its last label so every label sits on its value.
  ratatui puts middle x labels in equal slots, off their values, so the x
  labels are drawn by hand, each centered on its second's column (found
  from the axis corner ratatui drew).
- Stats WPM trend: as many of the category's latest sessions as the box
  is wide (it was the last 20), one bar per cell, titled with how many
  ("all 13", "last 74").
- #20, nothing against a box's side: two cells of padding on the chart,
  the trend and the Stats tables (the Mode column one cell narrower so the
  sessions table still fits 80 columns, click targets moved with it), a
  help box wide enough for its longest line, and no marker in the first
  cell on Settings — the selected row shows by its bold label and its
  highlighted value.

nothing_touches_a_box_border draws every screen, overlays included, at
two sizes and fails on anything beside a vertical border; it fails with
the old padding and help width. ticks and the even spacing of the chart's
labels are tested too.
- Stats WPM trend: a row under the bars with a ★ under each session that
  holds its mode's best — the same rule as the sessions list's ★ — and a
  blank under every other bar. The trend box grows a row to keep three
  rows of bars; the sessions list shows one fewer at 80x24.
- Help: a blank line of padding above and below the text. To keep the box
  whole on 80x24, the two entries that wrapped onto a second line (p n b,
  Mouse) are one line each now.

Docs: ROADMAP's v0.7 section lists everything added beyond the plan —
what was found while building it and what was asked for after using it —
and CHANGELOG, USAGE and LEARNINGS cover the stars and the help padding.
Tests: wpm_trend_stars_the_bests; help_fits_80x24 checks the padding.
- The chart's x labels panicked on a terminal 8-9 columns wide (clamp
  with min > max): a label wider than the plot isn't drawn, and placing
  one can't panic. no_screen_panics_at_any_size draws every screen at
  every width from 1 to 30 and heights from 1 to 24.
- F6 after a run over 64 KiB repeated one empty word: it built the words
  from the saved text, which is empty past the limit. It takes the run's
  own words (App::run_words, shared with session_text), and the dead
  +replay strip in repeat_run is gone.
- Stopping on words, the right space refused at the end of a wrong word
  was counted as a Space-key mistake (key heatmap, accuracy, tally): it
  now only stops. A wrong key there is still a mistake.
- Zen runs showed "not saved: idle"; zen is never saved, so the idle
  check doesn't apply to it.
- pace = best on the daily challenge raced any day's best; it races
  today's best on today's words, as Results counts it.
- The Settings typing rows showed the config file's values, not the
  --blind / --stop-on-error / --pace in use, and changed from those. They
  now show and change the values in use (SettingsView keeps no copy).

Tidy-ups: one source for the stats path — after_input, the save and
--replay use App::stats_file through App::history, and the parameter that
duplicated it is gone; the seven typing rows share one next_typing helper
(and from_name is written one way); the typing screen counts word offsets
only when there's a pace cursor.
@withrvr
withrvr merged commit b3f1e84 into main Oct 11, 2026
5 checks passed
@withrvr
withrvr deleted the feature/v0.7.0 branch October 11, 2026 18:30
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

UI update: minior ui updates where this are not stuck to outerlines or something like that there is proper padding and showcase

1 participant