Skip to content

Hebrew 10-finger typing trainer - #4

Open
erimeilis wants to merge 42 commits into
mainfrom
feature/hebrew-typing-trainer
Open

Hebrew 10-finger typing trainer#4
erimeilis wants to merge 42 commits into
mainfrom
feature/hebrew-typing-trainer

Conversation

@erimeilis

Copy link
Copy Markdown
Owner

Hebrew 10-finger typing trainer

Adds a dedicated Trainer mode to the always-on-top keyboard overlay that teaches and improves Hebrew touch typing (Israeli standard layout), from beginner to fast. Built spec-first, then implemented as 29 TDD tasks with per-task review and a final whole-branch review.

Design + plan: docs/plans/2026-07-15-hebrew-typing-trainer-design.md, docs/plans/2026-07-16-hebrew-typing-trainer-plan.md.

What it does

  • Adaptive weak-key targeting — practice text is selected (never generated) from real Hebrew words/prose, ranked toward your slowest/least-accurate keys.
  • Per-key heatmap on the on-screen keyboard (red→green) + finger-zone colouring.
  • Switchable guidance / wean-off — Full → Auto-fade (per-key, confidence-driven) → Manual dim → Hidden.
  • Hebrew-accurate error model — a wrong consonant is a different word: accuracy-first gating (~98%), stop-on-error default, whole-word-correctness metric, sofit/confusable-pair curriculum, and meaning-aware "you typed ⟨X⟩ — target ⟨Y⟩" feedback.
  • Placement test → stage curriculum (home-row-first) → per-key adaptive selection within each stage.
  • Text sources: adaptive real-word drills, common-word lists, public-domain prose/quotes, custom paste. Early stages use honest finger drills (never fake words).
  • Gamification: streaks / daily goal / skill-stars, celebratory + fault animations, ghost-race vs. your personal best, WPM/accuracy progress graphs, text-theme backgrounds.
  • Two capture sources behind one interface — DOM (focused window) and Rust global tap — switchable in settings.

Architecture

  • Pure-logic engine (src/trainer/*: selection, scoring, confidence, gating, curriculum, errors, ghost, streak, stars, chart) with thin React UI; driven by a swappable KeySource.
  • Matches on physical key codes → input-source-independent; reuses the existing Keyboard/Key/keyMapping (single source of truth).
  • One native change: set_trainer_mode (focusable-window toggle, idempotent revert, main-thread dispatched) + an unthrottled key-event. The original overlay behaviour is unchanged when not training.
  • Local persistence behind a TrainerStore interface (settings/progress/stats/streak/history/ghosts).

Also included

  • TypeScript 5.9.3 → 7.0.2 upgrade (adds src/vite-env.d.ts; fixes a stale Keyboard.test.tsx assertion).
  • docs/plans/ un-ignored so design/plan docs are version-controlled.

Verification

  • 124 tests pass (npm test), tsc --noEmit clean, npm run build succeeds, and the app launches cleanly (NSPanel + CGEventTap + tray intact).
  • Every task passed spec + quality review; the final whole-branch review returned "Ready to merge: Yes" after the settings-control gap it found was fixed.

Before merge — one manual check

  • 2-cycle focus toggle (GUI-only): enter Trainer → type → exit, twice; confirm typing registers on both entries and the overlay returns to non-activating + Dock-less after each exit. If DOM-capture focus misbehaves, switch Capture → "Global tap" (no focus needed; avoids the transient Dock icon).

Non-blocking follow-ups

  • Empty/all-non-Hebrew custom paste shows an empty state (no crash); minor test-coverage gaps noted in review; UTC streak-day boundaries; live ghost-bar denominator is approximate (storage-side comparison is pace-normalized).
  • The 18 Dependabot alerts on the repo are pre-existing and unrelated to this branch.

erimeilis added 30 commits July 16, 2026 12:30
Track design plans in git (un-ignore docs/plans/) and add the design
spec for the Hebrew 10-finger typing trainer feature. Also tracks the
previously-ignored virtual-typing design docs.

Key decisions captured in the spec:
- Dedicated Trainer mode; Hebrew (Israeli standard) layout first
- Real Hebrew text is primary (no generated pseudo-words); adaptivity
  via weak-key corpus selection
- Staged home-row-first ladder + adaptive weak-key targeting
- Switchable guidance modes to wean off the visible keyboard
- Hebrew accuracy model: stop-on-error, whole-word metric, sofit and
  confusable-pair curriculum, meaning-aware error feedback
- Both DOM and Rust-tap keystroke capture behind one KeySource setting
- Bump typescript devDependency to ^7.0.2 (latest stable, native compiler).
- Add src/vite-env.d.ts (vite/client reference) so side-effect CSS
  imports type-check under TS 7's stricter module handling (TS2882).
- Fix stale Keyboard.test.tsx assertion: the component listens to
  'keyboard-state', not the old 'key-pressed'/'key-released'.

Verified: tsc --noEmit clean, 11/11 vitest pass, npm run build succeeds.
29 bite-sized TDD tasks across 4 build-order phases (foundation,
pedagogy core, real text + feedback, delight) with exact file paths,
full code, and test-first steps.
Implements KeySource interface to capture physical key events from DOM.
Emits KeyEvent objects with code, timestamp, and down/up state.
Includes Vitest test coverage for event emission and listener lifecycle.
The existing "unlistens on stop()" test awaited listen()'s promise
before calling stop(), so it never exercised the guard branch
(`if (this.stopped) { un(); return; }`) inside the .then() callback.
Add a test that calls stop() synchronously right after start(), with
no await in between, then flushes microtasks to prove the race-guard
path is what fires unlisten.
- keyboard_listener: emit an unthrottled "key-event" ({ code, down }) on
  every physical key press/release for TauriTapKeySource, independent of
  the existing 16ms-throttled "keyboard-state" emit.
- lib.rs: add set_trainer_mode(active) command, registered in the invoke
  handler. Round-trips the main window through tauri-nspanel's
  Panel::to_window()/WebviewWindowExt::to_panel() to swap it between the
  non-activating KeyboardPanel overlay and a regular focusable window,
  since can_become_key_window is baked into the panel's Objective-C class
  at compile time and has no runtime setter.
TrainerMode is a skeleton container: invokes set_trainer_mode(true/false)
on mount/unmount, renders an Exit button, a placeholder panel (PracticePanel
lands in Task 21), and the reused Keyboard. App gains isTraining state, an
early return (before the collapsed/overlay returns) that swaps to
TrainerMode, and a keyboard-icon toggle button in the window-controls bar.
…ord) # user-authorized: approved SDD plan execution
# user-authorized: approved SDD plan execution
…/onComplete to avoid stale closures # user-authorized: approved SDD plan execution
…oesn't leak to next position # user-authorized: approved SDD plan execution
Implement PracticePanel component rendering Hebrew target text right-to-left
with per-character status classes (char-correct, char-error, char-current,
char-pending). Add trainer.css with styling for practice text and character
states. Includes Vitest component test.

# user-authorized: approved SDD plan execution
erimeilis added 12 commits July 17, 2026 20:30
…dback

- Entry: blue trainer dot in the overlay control bar + a 'Typing Trainer'
  menu-bar/tray item (emits toggle-trainer).
- Common window: trainer mode makes the window focusable/activating with a
  Dock icon and drops always-on-top; custom in-window title strip with
  macOS-style traffic lights (native decorations throw NSException here) —
  red=exit, yellow=hide, green=zoom.
- Capture: default to the global tap (focus-independent); count keystrokes
  only when the window is focused AND Hebrew input is active (banner otherwise).
- Placement: shows the keyboard, stop-on-error, and a Skip button so
  beginners are not trapped.
- Feedback: red fault marking on the current char + live correct/error tally
  on placement and practice screens.
- Layout: centered keyboard + text, right-sized trainer window, clean heatmap
  (untouched keys neutral) with finger colour only on the next key.
Move the flat src/trainer files into components/ (UI + trainer.css), hooks/
(useTypingSession), engine/ (adaptive logic: confidence, gating, scoring,
errors, curriculum, textSelection, keyboardView, placement), gamification/
(ghost, streak, stars, chart), and persistence/ (storage, useTrainerState).
data/ and keySource/ unchanged; TrainerMode + types stay at root as the entry
point and shared types. Pure git-mv moves + relative-import fixes, no behavior
change: tsc clean, 124 tests pass, build succeeds.
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.

1 participant