Coding TypeGym: A Rust Terminal Typing Trainer | Part 2: Ratatui UI

Published on

In Part 2 we move past the barebones scaffolding from Part 1 and build what actually feels like a typing trainer: a proper Ratatui UI.

Instead of rendering the target text as a plain paragraph, we now render a typing view where every cell is classified as:

On top of that, we center the typing area in the terminal and place the cursor exactly where the next character should go.


What We Covered


Design Insights

Model First: UI Becomes Easy

The big shift in this episode is that we don’t “render strings” anymore: we render typed state.

Once we represent every position as a Character, the UI becomes a simple mapping:

This is one of those patterns that scales nicely: the rendering code stays clean even as the app grows.

Build a Page From Two Strings

We also start treating the target + input as two aligned streams and building a page from them. That gives us a nice place to later add:

But for now: just “turn state into renderable lines.”


Implementation Highlights

1) Character enum + classification

We introduce a tiny enum that encodes the UI truth for each cell:

pub enum Character {
    Hit(char),
    Miss(char),
    Empty(char),
}

Then classify_character(target, input) decides which variant we get. This is the heart of the UI: it converts raw characters into something we can style and render consistently.

2) Building lines and pages

To render multi-line text, State builds:

The idea is simple:

  1. iterate over target + input chars
  2. classify each position into a Character
  3. collect into a line
  4. do that for every target line → page

This makes the UI rendering phase extremely straightforward: render a Page, not raw strings.

3) Ratatui rendering with Span + Line

Instead of a single Paragraph::new(String), we now build Vec<Line> where each line contains a list of Spans (one per character), each with its own style.

That’s how we get fine-grained coloring and formatting without fighting the widget system.

We also add basic UI config for colors (ANSI indexed colors), e.g. one for misses and one for empty/untyped characters.

4) Centering the typing area

Terminal UIs look instantly better when the “main content” isn’t glued to the top-left corner.

We compute:

…then use Ratatui Layout to carve out a centered rectangle and render the paragraph there.

5) Cursor placement

Finally, we place the cursor exactly where the next input should happen:

Then we offset those coordinates into the centered render area and call frame.set_cursor_position(...).

This is small, but it’s a huge step in making the app feel “real”.


What’s Next?

Now that we have a real typing UI, the next episode(s) can focus on turning the app into a functional trainer:


Project Code

You will find the complete source code here: typegym