# Tuicr Input Modes: Complete Guide to the Terminal UI State Machine

> Explore Tuicr's 11 input modes including Normal, Comment, and Command. Understand its terminal UI state machine for enhanced keyboard handling and actions.

- Repository: [Almog Gavra/tuicr](https://github.com/agavra/tuicr)
- Tags: deep-dive
- Published: 2026-08-01

---

**Tuicr implements 11 distinct input modes—including Normal, Comment, Command, Search, and specialized submit workflow modes—that function as a finite-state machine to control keyboard handling, UI rendering, and available actions in the terminal interface.**

Tuicr is a terminal-based code review tool for GitHub pull requests. At the heart of its interactive interface lies the `InputMode` enum, a finite-state machine defined in [`src/app/mod.rs`](https://github.com/agavra/tuicr/blob/main/src/app/mod.rs) that determines how keystrokes are interpreted and what UI elements are displayed. Understanding these Tuicr input modes is essential for navigating the tool efficiently and leveraging its full PR review capabilities.

## What Are Tuicr Input Modes?

The `InputMode` enum acts as the central nervous system of Tuicr's user interface. Each variant represents a distinct operational state that dictates key bindings, screen layout, and permitted actions. The current mode is stored in `App::input_mode` within [`src/app/mod.rs`](https://github.com/agavra/tuicr/blob/main/src/app/mod.rs) at line 41, while the main event loop in [`src/main.rs`](https://github.com/agavra/tuicr/blob/main/src/main.rs) (lines 750-770) dispatches actions based on this state.

## The 11 Input Modes in Tuicr

Tuicr provides eleven specialized modes ranging from standard navigation to complex submission workflows:

**Normal Mode** is the default navigation state active when the application starts. It enables standard browsing with arrow keys or Vim-style navigation for file selection and diff viewing.

**Comment Mode** activates when writing inline or file-level comments, typically entered by pressing `c`. This mode displays a text input buffer at the bottom of the screen where `Ctrl-s` saves the comment and `Esc` cancels the operation.

**Command Mode** provides a mini-command line similar to Vim's command mode, accessed via `:`. It accepts CLI-style commands such as `:w` for write or `:diff` for changing diff layouts.

**Search Mode** enables incremental search through the diff view when pressing `/`. It displays a search prompt where `Enter` jumps to the next match and `Esc` aborts the search.

**Help Mode** displays an overlay of available keybindings when pressing `?`. Any key press closes this informational popup.

**Confirm Mode** presents a simple Y/N confirmation dialog triggered by actions requiring consent, such as `:quit`. The interface shows a "y / n" prompt for final confirmation.

**CommitSelect Mode** facilitates selecting specific commits or ranges for PR review, accessed via the leader key `;` followed by `c`. The UI renders a list of commits for targeted review selection.

**VisualSelect Mode** implements Vim-style visual range selection activated by `v`. This enables selecting multiple lines or hunks for bulk commenting operations.

**SubmitResolver Mode** appears automatically after `:submit` when comments cannot be mapped to GitHub inline reviews. This modal allows users to move problematic comments to the review summary or omit them entirely.

**SubmitConfirm Mode** provides final confirmation before sending the review to the remote forge. It displays a summary including comment count and review type, requiring explicit approval to proceed.

**SubmitActionPicker Mode** opens when executing a bare `:submit` command, presenting options to choose the review type: Comment, Approve, Request changes, or Draft.

## How Input Modes Drive the Application

The implementation follows a clear architectural pattern across three core components:

**State Storage and Definition**
The `InputMode` enum is defined in [`src/app/mod.rs`](https://github.com/agavra/tuicr/blob/main/src/app/mod.rs) (lines 45-66) and stored as `App::input_mode`. This field tracks the application's current state throughout the session.

**Event Loop Dispatching**
The main event loop in [`src/main.rs`](https://github.com/agavra/tuicr/blob/main/src/main.rs) matches against `app.input_mode` to route actions to mode-specific handlers:

```rust
match app.input_mode {
    InputMode::Comment => handle_comment_action(&mut app, action),
    InputMode::Command => handle_command_action(&mut app, action),
    InputMode::Search => handle_search_action(&mut app, action),
    InputMode::CommitSelect => handle_commit_select_action(&mut app, action),
    InputMode::VisualSelect => handle_visual_action(&mut app, action),
    InputMode::SubmitResolver => handle_submit_resolver_action(&mut app, action),
    InputMode::SubmitConfirm => handle_submit_confirm_action(&mut app, action),
    InputMode::SubmitActionPicker => handle_submit_action_picker_action(&mut app, action),
    InputMode::Help => handle_help_action(app, action),
    InputMode::Confirm => handle_confirm_action(app, action),
    InputMode::Normal => /* normal navigation */,
    _ => {}
}

```

**Keybinding Resolution**
Keyboard input translation occurs in [`src/input/keybindings.rs`](https://github.com/agavra/tuicr/blob/main/src/input/keybindings.rs) (lines 140-155) through the `map_key_to_action` function:

```rust
pub fn map_key_to_action(key: KeyEvent, mode: InputMode, leader_key: char) -> Action {
    match mode {
        InputMode::Normal => map_normal_mode(key, leader_key),
        InputMode::Command => map_command_mode(key),
        InputMode::Search => map_search_mode(key),
        InputMode::Comment => map_comment_mode(key),
        InputMode::Help => map_help_mode(key),
        InputMode::Confirm => map_confirm_mode(key),
        InputMode::CommitSelect => map_commit_select_mode(key),
        InputMode::VisualSelect => map_visual_mode(key),
        InputMode::SubmitResolver => map_submit_resolver_mode(key),
        InputMode::SubmitConfirm => map_submit_confirm_mode(key),
        InputMode::SubmitActionPicker => map_submit_action_picker_mode(key),
    }
}

```

## Working with Input Modes: Practical Examples

**Adding an Inline Comment**
To enter Comment mode from Normal mode, press `c`. Internally, the handler sets:

```rust
app.input_mode = InputMode::Comment;

```

A text input appears at the bottom of the screen. Type your comment and press `Ctrl-s` to save, or `Esc` to cancel.

**Executing Commands**
Press `:` while in Normal mode to switch to Command mode:

```rust
app.input_mode = InputMode::Command;

```

The status bar displays " COMMAND ". Enter `:diff side-by-side` and press Enter to modify the diff layout.

**Submitting a Review**
The submission workflow demonstrates mode transitions. After typing `:submit`, the application checks for unmappable comments:

```rust
if has_unmappable_comments {
    app.input_mode = InputMode::SubmitResolver;
    // Resolve each comment, then press `s` to continue
}
app.input_mode = InputMode::SubmitConfirm;

```

If comments cannot map to GitHub inline positions, SubmitResolver mode appears first. After resolution (or if not needed), SubmitConfirm mode displays the final summary for approval.

**Searching Diff Content**
Press `/` in Normal mode to activate Search mode:

```rust
app.input_mode = InputMode::Search;

```

Enter your search term and press Enter to jump to the next match, or `Esc` to abort.

## Key Files in the Input Mode Architecture

Understanding these source files provides insight into how Tuicr input modes function:

- **[`src/app/mod.rs`](https://github.com/agavra/tuicr/blob/main/src/app/mod.rs)**: Contains the `InputMode` enum definition and `App` struct holding the current mode state.
- **[`src/main.rs`](https://github.com/agavra/tuicr/blob/main/src/main.rs)**: Houses the main event loop that dispatches actions based on the current input mode.
- **[`src/input/keybindings.rs`](https://github.com/agavra/tuicr/blob/main/src/input/keybindings.rs)**: Implements the key-to-action mapping logic for each mode variant.
- **[`src/ui/status_bar.rs`](https://github.com/agavra/tuicr/blob/main/src/ui/status_bar.rs)**: Renders the current mode indicator (e.g., " COMMENT ") in the interface.
- **[`src/ui/app_layout.rs`](https://github.com/agavra/tuicr/blob/main/src/ui/app_layout.rs)**: Controls conditional UI layout rendering based on the active `InputMode`.

## Summary

- Tuicr implements **11 distinct input modes** as a finite-state machine to manage UI state and keyboard input.
- The **`InputMode`** enum is defined in [`src/app/mod.rs`](https://github.com/agavra/tuicr/blob/main/src/app/mod.rs) and stored in `App::input_mode`.
- **Normal, Comment, Command, Search, Help, Confirm, CommitSelect, VisualSelect, SubmitResolver, SubmitConfirm, and SubmitActionPicker** cover navigation, editing, and PR submission workflows.
- The main event loop in [`src/main.rs`](https://github.com/agavra/tuicr/blob/main/src/main.rs) dispatches to mode-specific handlers like `handle_comment_action` and `handle_command_action`.
- Keybindings are mapped per-mode in [`src/input/keybindings.rs`](https://github.com/agavra/tuicr/blob/main/src/input/keybindings.rs) via the `map_key_to_action` function.
- Mode transitions occur automatically (e.g., entering SubmitResolver after `:submit`) or through explicit key presses (e.g., `c` for Comment mode).

## Frequently Asked Questions

### How do I exit Comment mode in Tuicr?

Press `Esc` to cancel the comment and return to Normal mode, or `Ctrl-s` to save the comment and exit.

### What happens if my comments cannot be mapped to GitHub inline reviews?

Tuicr automatically enters **SubmitResolver** mode after executing `:submit`. This modal interface allows you to move each unmappable comment to the review summary or omit it before proceeding to final confirmation.

### Can I change the key bindings for different input modes?

Key bindings are defined in [`src/input/keybindings.rs`](https://github.com/agavra/tuicr/blob/main/src/input/keybindings.rs) where the `map_key_to_action` function routes keys based on the current `InputMode`. Customization requires modifying this source file and recompiling the application.

### What is the difference between SubmitConfirm and SubmitActionPicker modes?

**SubmitActionPicker** appears when running `:submit` without arguments, allowing you to select the review type (Comment, Approve, Request changes, or Draft). **SubmitConfirm** appears after action selection (or directly if comments need resolution), displaying a summary of the review for final Y/N confirmation before sending to GitHub.