Tuicr Input Modes: Complete Guide to the Terminal UI State Machine
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 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 at line 41, while the main event loop in 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 (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 matches against app.input_mode to route actions to mode-specific handlers:
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 (lines 140-155) through the map_key_to_action function:
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:
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:
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:
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:
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: Contains theInputModeenum definition andAppstruct holding the current mode state.src/main.rs: Houses the main event loop that dispatches actions based on the current input mode.src/input/keybindings.rs: Implements the key-to-action mapping logic for each mode variant.src/ui/status_bar.rs: Renders the current mode indicator (e.g., " COMMENT ") in the interface.src/ui/app_layout.rs: Controls conditional UI layout rendering based on the activeInputMode.
Summary
- Tuicr implements 11 distinct input modes as a finite-state machine to manage UI state and keyboard input.
- The
InputModeenum is defined insrc/app/mod.rsand stored inApp::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.rsdispatches to mode-specific handlers likehandle_comment_actionandhandle_command_action. - Keybindings are mapped per-mode in
src/input/keybindings.rsvia themap_key_to_actionfunction. - Mode transitions occur automatically (e.g., entering SubmitResolver after
:submit) or through explicit key presses (e.g.,cfor 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 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.
Have a question about this repo?
These articles cover the highlights, but your codebase questions are specific. Give your agent direct access to the source. Share this with your agent to get started:
curl -s "https://instagit.com/install.md" Maintain an open-source project? Get it listed too →