Coding TypeGym: A Rust Terminal Typing Trainer | Part 1: Main Loop

Published on

Welcome to Part 1 of TypeGym: a terminal based typing trainer we’re building from scratch in Rust using Ratatui (and Crossterm under the hood).

This episode is all about getting the boring but critical stuff in place: a solid main loop, a clean terminal UI lifecycle and the first pass at input handling.


What We Covered

At the end of Part 1 we have a real interactive terminal program that can react to keys and exit cleanly, which is exactly what we need before we start adding “typing trainer” features.


Design Insights

Start With the Loop, Not the Features

It is tempting to jump straight into WPM counters, colored text and accuracy tracking. But terminal apps are unforgiving: if your setup/teardown is messy, you’ll constantly fight broken terminal states.

So the goal here is simple: a reliable foundation.

Separate “UI App” from “Typing State”

We keep:

Right now State is mostly scaffolding (some methods are todo!()), but the boundary is already in place.


Implementation Highlights

The Main Practice Loop

The program is structured around a session loop: fetch text → create state → run UI → decide whether to repeat.

loop {
    let text = get_text(&config)?;
    let state = State::new(text);
    let should_loop = run_ui(state, &config)?;
    if !should_loop {
        break;
    }
}

This makes it easy to support “restart with new text” later, without restarting the whole binary.

Terminal UI Event Loop (Ratatui + Crossterm)

Inside run_ui() we:

let mut terminal = ratatui::init();
execute!(stdout(), EnableMouseCapture)?;

let mut app = App::new(state, config);

loop {
    terminal.draw(|frame| app.draw(frame))?;

    if app.should_quit {
        break;
    }

    if event::poll(Duration::from_millis(100))? {
        if let Event::Key(key_code) = event::read()? {
            app.handle_key_event(key_code.code, key_code.modifiers);
        }
    }
}

ratatui::restore();
execute!(stdout(), DisableMouseCapture)?;

First Keybindings (Early Version)

We already sketch the behavior we want:

This is the skeleton that later episodes will “fill in” with actual typing logic.


What’s Next?

In Part 2 we will focus on rendering the typing text properly: turning the raw target/input into something that feels great to use in a terminal UI:

Once that is in place, we will have a solid UI foundation to layer in typing logic and real-time stats in the following episodes.


Project Code

You will find the complete source code here: typegym