AI Skill Report Card

Building Tui Apps Rust

B75·Jan 16, 2026
YAML
--- name: building-tui-apps-rust description: Builds terminal user interfaces in Rust using ratatui with interactive lists and selection capabilities. Use when creating CLI tools that need rich terminal interfaces with navigation, selection, and real-time updates. --- # Building TUI Apps in Rust
Rust
// Cargo.toml [dependencies] ratatui = "0.26" crossterm = "0.27" tokio = { version = "1.0", features = ["full"] } serde = { version = "1.0", features = ["derive"] } reqwest = { version = "0.11", features = ["json"] } // src/main.rs use ratatui::{ backend::CrosstermBackend, layout::{Constraint, Direction, Layout}, style::{Color, Modifier, Style}, text::Span, widgets::{Block, Borders, List, ListItem, ListState}, Frame, Terminal, }; use crossterm::{ event::{self, DisableMouseCapture, EnableMouseCapture, Event, KeyCode}, execute, terminal::{disable_raw_mode, enable_raw_mode, EnterAlternateScreen, LeaveAlternateScreen}, }; use std::io; #[derive(Clone)] struct Repo { name: String, url: String, marked_for_deletion: bool, } struct App { repos: Vec<Repo>, selected: usize, list_state: ListState, } impl App { fn new() -> Self { let mut state = ListState::default(); state.select(Some(0)); Self { repos: vec![], selected: 0, list_state: state, } } fn toggle_selection(&mut self) { if let Some(selected) = self.list_state.selected() { self.repos[selected].marked_for_deletion = !self.repos[selected].marked_for_deletion; } } }
Recommendation
Add complete working examples showing the GitHub API integration and async handling - current examples only show UI components in isolation

Progress:

  • Set up project structure with ratatui and crossterm
  • Implement GitHub API client for fetching repositories
  • Create main app state management
  • Build interactive list component with selection
  • Add keyboard navigation (arrows, space, enter)
  • Implement deletion marking/unmarking
  • Add confirmation dialog for batch deletion
  • Handle API calls for actual deletion
  • Add error handling and status messages

Core Implementation Steps

  1. Terminal Setup
Rust
fn setup_terminal() -> Result<Terminal<CrosstermBackend<io::Stdout>>, Box<dyn std::error::Error>> { enable_raw_mode()?; let mut stdout = io::stdout(); execute!(stdout, EnterAlternateScreen, EnableMouseCapture)?; let backend = CrosstermBackend::new(stdout); let terminal = Terminal::new(backend)?; Ok(terminal) }
  1. Event Loop
Rust
fn run_app() -> Result<(), Box<dyn std::error::Error>> { let mut terminal = setup_terminal()?; let mut app = App::new(); loop { terminal.draw(|f| ui(f, &mut app))?; if let Event::Key(key) = event::read()? { match key.code { KeyCode::Char('q') => break, KeyCode::Down => app.next(), KeyCode::Up => app.previous(), KeyCode::Char(' ') => app.toggle_selection(), KeyCode::Enter => app.confirm_deletions(), _ => {} } } } restore_terminal()?; Ok(()) }
  1. UI Rendering
Rust
fn ui(f: &mut Frame, app: &mut App) { let chunks = Layout::default() .direction(Direction::Vertical) .constraints([Constraint::Min(0), Constraint::Length(3)]) .split(f.size()); let items: Vec<ListItem> = app.repos .iter() .map(|repo| { let style = if repo.marked_for_deletion { Style::default().fg(Color::Red).add_modifier(Modifier::CROSSED_OUT) } else { Style::default() }; let prefix = if repo.marked_for_deletion { "❌ " } else { " " }; ListItem::new(Span::styled(format!("{}{}", prefix, repo.name), style)) }) .collect(); let list = List::new(items) .block(Block::default().borders(Borders::ALL).title("Repositories")) .highlight_style(Style::default().bg(Color::Yellow).fg(Color::Black)); f.render_stateful_widget(list, chunks[0], &mut app.list_state); }
Recommendation
Include actual error handling patterns and recovery strategies rather than just mentioning them in best practices

Example 1: Basic Repository List Input: GitHub API response with user repositories

JSON
[ {"name": "old-project", "html_url": "https://github.com/user/old-project"}, {"name": "test-repo", "html_url": "https://github.com/user/test-repo"} ]

Output: Interactive TUI showing:

┌Repositories──────────────────┐
│   old-project                │
│ > test-repo                  │
└──────────────────────────────┘
[Space] Mark/Unmark [Enter] Delete Selected [q] Quit

Example 2: Marked for Deletion After pressing Space on selected items:

┌Repositories──────────────────┐
│ ❌ old-project               │
│ > test-repo                  │
└──────────────────────────────┘
Recommendation
Provide a complete minimal working application that can be copied and run immediately, not just fragmented code snippets
  • Use ListState for proper selection management
  • Implement proper error boundaries for API calls
  • Use tokio::spawn for non-blocking GitHub API requests
  • Store GitHub token in environment variables, not code
  • Implement confirmation dialogs for destructive actions
  • Use consistent color coding (red for deletion, yellow for selection)
  • Handle terminal resize events gracefully
  • Provide clear visual feedback for all actions
  • Blocking the UI thread - Always use async/await for API calls
  • Not handling terminal restoration - Always restore terminal state on exit
  • Forgetting rate limits - GitHub API has rate limits, implement backoff
  • No confirmation step - Deletion should require explicit confirmation
  • Poor error messages - Network errors need user-friendly display
  • Missing escape handling - Always provide quit mechanism (q key)
  • State desync - Keep list selection in bounds when filtering/removing items
Rust
// Good: Proper cleanup impl Drop for App { fn drop(&mut self) { let _ = disable_raw_mode(); let _ = execute!( io::stdout(), LeaveAlternateScreen, DisableMouseCapture ); } }
0
Grade BAI Skill Framework
Scorecard
Criteria Breakdown
Quick Start
11/15
Workflow
11/15
Examples
15/20
Completeness
15/20
Format
11/15
Conciseness
11/15