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>