Replace the 'known remaining inefficiency' section with the layout that
now exists, plus the measured before/after (clean release build: 2m14s
-> 1m46s wall, keydr units 61.0s -> 32.4s, peak ~2.0 GB unchanged).
Add a maintenance note: new modules go in src/lib.rs, not src/main.rs.
Re-declaring them in the binary would silently restore the double
compile this refactor removed.
src/main.rs re-declared the entire module tree (`mod app; mod config;
...`), which made the binary a second independent crate. Every module --
and the whole rust-i18n translation table generated by `i18n!` -- was
compiled twice, and 281 unit tests were executed twice.
Move all application code to src/run.rs inside the library, exposed as
`keydr::run()`. main.rs is now a 10-line entry point.
Mechanical changes only; no logic was altered:
- src/run.rs is src/main.rs minus the crate-root preamble
- `fn main()` -> `pub fn run()`; `struct Cli` -> `pub struct Cli`
- bare `use app::...` / inline `ui::theme::Theme` references are now
qualified with `crate::` (19 imports, 342 inline references), since
they are no longer resolving from the crate root
Clean release build, measured with --timings:
before after
keydr (bin) 33.7s 0.3s
keydr (lib) 26.0s 31.0s
keydr total 61.0s 32.4s (-28.6s)
total unit-seconds 292.2s 264.5s
wall clock 2m14s 1m46s (-28s, -21%)
Test coverage is unchanged: 341 unique tests before and after (verified
by diffing `--list` output, not just counts). The apparent drop from
622 to 340 executions is the duplicate run disappearing -- 281 tests
previously ran once per crate copy.
Verified the binary still works: --help/--version intact, TUI renders
its welcome screen under tmux, and translations resolve (checked both
English and German).
Note: `cargo check --benches` fails with 3 errors, but those are
pre-existing on main (NgramStat field renames) and unrelated.
The previous text claimed restructuring main.rs to use the lib crate
would 'roughly halve' build time. Measured with --timings on a clean
release build, that was wrong:
33.7s keydr (bin)
26.0s keydr (lib)
1.4s generate_test_profiles (bin)
61.0s of 292.2s total unit-seconds (21%)
The duplicated units are indeed the two slowest in the graph, but the
saving is ~26s of unit-work (less in wall time, since they overlap with
dependency compilation across 3 jobs), not half the build. The other
~79% is one-time dependency compilation that incremental builds skip.
Replace the estimate with the measurement.
systemd-oomd is desktop-only on Ubuntu (reverse-deps are ubuntu-desktop*);
'systemctl is-enabled systemd-oomd' returns not-found on this server, so
the previous advice to enable it would simply have failed. Recommend
earlyoom instead, with --avoid/--prefer tuned to protect the shell and
target rustc.
Also document the machine-wide alternatives to per-project tuning:
- sudo systemctl set-property user-1000.slice MemoryHigh=/MemoryMax=
(covers every shell and build; sshd lives in system.slice so it can
never lock you out)
- global ~/.cargo/config.toml, which does support [profile.*]
Clarify that the MemoryHigh warning applies to short-lived build scopes,
not to slice-level limits where gradual throttling is appropriate.
Release builds consumed 5+ GB and never completed on a 4-core / 7.6 GB
VM, thrashing the machine badly enough to lose SSH access.
Root cause was a codegen bug in rust-i18n v3, not machine size. Its
macro emitted one HashMap::from([...]) and one add_translations() call
per translation string, all in a single function body -- 8,652 of each
for our 21 locales / ~9,000 keys. LLVM scales superlinearly on function
size, so optimising that one initialiser took ~4.5 GB.
Verified via cargo expand (lib target):
v3.1.5 v4.2.1
HashMap::from 8,652 0
add_translations 8,652 21 (one per locale)
expanded lines 93,026 73,444
Fixed upstream in v4.0.0.
Clean release builds, same machine:
v3, opt-level=3, thin LTO 5.2 GB stalled >18m, never finished
v3, opt-level=1, no LTO 1.8 GB 15m30s (previous workaround)
v4, opt-level=3, thin LTO 2.08 GB 3m25s
v4, stock cargo defaults 1.92 GB 2m17s
Also:
- Remove the opt-level=1 / lto=false workaround; cargo's release
defaults are correct now. No shipped code is de-optimised.
- Keep only genuine small-machine hygiene in .cargo/config.toml:
jobs=3 (keeps the box interactive), lld linker, and trimmed debug
info on dev/test profiles.
- Document the diagnosis, the cargo expand / llvm-lines workflow that
found it, and the systemd-based safety nets in docs/BUILDING.md.
- Note in main.rs why i18n! must appear in both crate roots (t! expands
to crate::_rust_i18n_t, and main.rs re-declares the module tree
rather than depending on the lib target).
Test suite passes; release binary verified working.
Adds rust-i18n and refactors all of the text copy in the app to use the
translation function so that the UI language can be dynamically updated
in the settings.
1. Start in Adaptive Drill by default: App launches directly into a
typing lesson instead of the menu screen.
2. Fix error tracking for backspaced corrections: Add typo_flags
HashSet to LessonState that persists error positions through
backspace. Errors at a position are counted even if corrected,
matching keybr.com behavior. Multiple errors at the same position
count as one.
3. Fix keyboard visualization with depressed keys: Enable crossterm
keyboard enhancement flags for key Release events. Track depressed
keys in a HashSet with 150ms fallback clearing. Depressed keys
render with bright/bold styling at highest priority. Add compact
keyboard mode for medium-width terminals.
4. Responsive UI for small terminals: Add LayoutTier enum (Wide >=100,
Medium 60-99, Narrow <60 cols). Medium hides sidebar and shows
compact stats header and compact keyboard. Narrow hides keyboard
and progress bar entirely. Short terminals (<20 rows) also hide
keyboard/progress.
5. Delete sessions from history: Add j/k row navigation in history
tab, x/Delete to initiate deletion with y/n confirmation dialog.
Full chronological replay rebuilds key_stats, letter_unlock,
profile scoring, and streak tracking. Only adaptive sessions update
key_stats/letter_unlock during rebuild. LessonResult now persists
lesson_mode for correct replay gating.
6. Improved statistics display: Bordered summary table on dashboard,
WPM bar graph using block characters (green above goal, red below),
accuracy Braille trend chart, bordered history table with WPM goal
indicators and selected-row highlighting, character speed
distribution with time labels, keyboard accuracy heatmap with
percentage text per key, worst accuracy keys panel, new 7-month
activity calendar heatmap widget with theme-derived intensity
colors, side-by-side panel layout for terminals >170 cols wide.
Also: ignore KeyEventKind::Repeat for typing input, clamp history
selection to visible 20-row range, and suppress dead_code warnings
on now-unused WpmChart.
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>