Skip to content

Ricecoder TUI Design

Mo Abualruz edited this page Dec 25, 2025 · 1 revision

Ricecoder TUI Design

Status: ✅ Complete
Last Updated: December 25, 2025


Overview

The RiceCoder TUI (Terminal User Interface) provides a beautiful, responsive terminal experience built with ratatui. Following Domain-Driven Design principles, the TUI layer is architecturally isolated from business logic, focusing solely on rendering, user interaction, and terminal management.

Core Principle: Pure UI concerns with zero business logic dependencies.


Architecture Overview

Layer Position

Presentation Layer - Terminal UI rendering and user interaction handling

┌───────────────────────────────────────────────┐
│         ricecoder-tui (Presentation)          │
│  - Terminal rendering (ratatui/crossterm)    │
│  - Widget composition and layout              │
│  - Event handling and keyboard navigation     │
│  - Theme application and styling              │
│  - Accessibility features                     │
└───────────────────────────────────────────────┘
                    ▲
                    │ (injected via DI)
┌───────────────────────────────────────────────┐
│       ricecoder-application (Use Cases)       │
│  - Session management (ricecoder-sessions)    │
│  - Provider integration (ricecoder-providers) │
│  - Command execution (ricecoder-commands)     │
└───────────────────────────────────────────────┘

What TUI Does (✅)

  • Terminal UI widgets and components
  • Layout management and responsive design
  • User input handling and keybindings
  • Theme rendering and styling
  • Screen reader accessibility
  • Clipboard operations
  • File picker interface
  • Status bar and information display

What TUI Does NOT Do (❌)

  • Session management → ricecoder-sessions
  • AI provider integration → ricecoder-providers
  • LSP functionality → ricecoder-lsp
  • VCS operations → ricecoder-vcs
  • File operations → ricecoder-files

Component System

Segregated Trait Design (ISP Compliance)

RiceCoder TUI follows Interface Segregation Principle with focused trait definitions:

// Core component lifecycle
pub trait Component {
    fn render(&mut self, frame: &mut Frame, area: Rect);
    fn handle_event(&mut self, event: &Event) -> EventResult;
}

// Optional traits for specific capabilities
pub trait Focusable {
    fn focus(&mut self);
    fn unfocus(&mut self);
    fn is_focused(&self) -> bool;
}

pub trait Scrollable {
    fn scroll_up(&mut self, lines: u16);
    fn scroll_down(&mut self, lines: u16);
    fn scroll_to_top(&mut self);
    fn scroll_to_bottom(&mut self);
}

pub trait Themeable {
    fn apply_theme(&mut self, theme: &Theme);
}

Why Segregated?

  • Widgets implement only needed traits
  • No "fat interfaces" forcing unnecessary methods
  • Clear separation of concerns
  • Easy to compose new widgets

Key Components

Component Purpose Traits Dependencies
App Main TUI application Component Event loop, layout manager
ChatInputWidget Message input area Component, Focusable Input analyzer, intent detection
MessageAreaWidget Conversation display Component, Scrollable, Themeable Markdown rendering, syntax highlighting
StatusBar Status information Component, Themeable Session info, provider status
FilePickerWidget File selection Component, Focusable, Scrollable File system access
CommandPaletteWidget Command launcher Component, Focusable Command registry
DiffWidget Code diff display Component, Scrollable, Themeable Diff parsing, syntax highlighting
ImageWidget Image rendering Component Image format detection, terminal rendering

Layout Management

Responsive Layout System

The TUI uses constraint-based layout with automatic adaptation:

pub struct LayoutConfig {
    pub sidebar_width: u16,           // 30 by default
    pub min_width: u16,               // 80 minimum
    pub max_scrollback: usize,        // 10,000 messages
    pub split_ratio: f32,             // 70/30 split
}

// Layout adapts to terminal size
fn calculate_layout(area: Rect) -> Vec<Rect> {
    Layout::default()
        .direction(Direction::Vertical)
        .constraints([
            Constraint::Length(1),      // Header
            Constraint::Min(10),        // Main area (flex)
            Constraint::Length(3),      // Input
            Constraint::Length(1),      // Status bar
        ])
        .split(area)
}

Layout Types

  1. Chat Layout: Header + Messages + Input + Status
  2. Diff View: Side-by-side or unified diff rendering
  3. File Picker: Tree view with preview pane
  4. Command Palette: Overlay with fuzzy search
  5. Help Dialog: Modal overlay with keybind reference

Adaptive Rendering

  • < 80 cols: Compact mode, hide sidebar
  • 80-120 cols: Standard mode
  • > 120 cols: Wide mode, show additional panels
  • < 24 rows: Minimal UI, essential elements only

Theme Integration

Theme System Architecture

Themes are managed by ricecoder-themes crate, applied by TUI:

pub struct Theme {
    pub name: String,
    pub colors: ThemeColors,
    pub styles: ThemeStyles,
}

pub struct ThemeColors {
    pub foreground: Color,
    pub background: Color,
    pub accent: Color,
    pub border: Color,
    pub syntax: SyntaxColors,
}

Built-in Themes

30+ themes including:

  • tokyo-night (default)
  • dracula
  • nord
  • gruvbox
  • solarized-dark/light
  • monokai

Theme Application

// Themes applied at runtime via ThemeManager
impl Themeable for MessageAreaWidget {
    fn apply_theme(&mut self, theme: &Theme) {
        self.background = theme.colors.background;
        self.text_color = theme.colors.foreground;
        self.syntax_theme = theme.colors.syntax;
    }
}

Hot-Reload Support

Themes can be switched without restarting:

# In chat, press `:` for command mode
:theme tokyo-night
:theme dracula

Accessibility Features

Screen Reader Support

pub struct ScreenReaderAnnouncer {
    pub enabled: bool,
    pub priority: AnnouncementPriority,
}

pub enum AnnouncementPriority {
    Low,      // Background updates
    Medium,   // User actions
    High,     // Errors, warnings
    Critical, // System failures
}

impl ScreenReaderAnnouncer {
    pub fn announce(&self, message: &str, priority: AnnouncementPriority) {
        // Platform-specific screen reader integration
    }
}

Keyboard Navigation

Full keyboard-driven interface:

Action Default Key Vim Mode Key
Send message Enter Enter
Scroll up / PgUp k / Ctrl+u
Scroll down / PgDn j / Ctrl+d
Focus input i i
Command palette Ctrl+p :
Copy output Ctrl+c yy
Exit Ctrl+q ZZ

High Contrast Mode

Automatic high contrast theme for accessibility:

accessibility:
  high_contrast: true        # Enhanced visibility
  focus_indicators: true     # Clear focus outlines
  reduced_motion: true       # Disable animations

Focus Management

pub struct FocusManager {
    focused: Option<ComponentId>,
    focus_ring: Vec<ComponentId>,
}

impl FocusManager {
    pub fn next(&mut self) -> Option<ComponentId> {
        // Tab/Shift+Tab navigation
    }
    
    pub fn set_focus(&mut self, id: ComponentId) {
        // Announce focus change to screen reader
    }
}

Event System

Event Loop Architecture

pub enum Event {
    Key(KeyEvent),
    Mouse(MouseEvent),
    Resize(u16, u16),
    Tick,
    Quit,
}

pub struct EventLoop {
    rx: Receiver<Event>,
    tick_rate: Duration,
}

impl EventLoop {
    pub async fn next(&mut self) -> Option<Event> {
        // Non-blocking event polling with timeout
        select! {
            event = self.rx.recv() => event.ok(),
            _ = tokio::time::sleep(self.tick_rate) => Some(Event::Tick),
        }
    }
}

Event Handling

Components handle events via trait methods:

pub enum EventResult {
    Consumed,           // Event handled, stop propagation
    Ignored,            // Event not handled, continue
    Propagate(Event),   // Transform and continue
}

impl Component for ChatInputWidget {
    fn handle_event(&mut self, event: &Event) -> EventResult {
        match event {
            Event::Key(key) if key.code == KeyCode::Enter => {
                self.submit_message();
                EventResult::Consumed
            }
            _ => EventResult::Ignored
        }
    }
}

Event Phases

  1. Capture: Top-down (App → Focused widget)
  2. Target: Focused widget processes event
  3. Bubble: Bottom-up (Widget → App)

Performance Characteristics

Rendering Performance

  • Target: 60 FPS with <16ms frame time
  • Actual: 60 FPS in typical usage
  • Strategy: Diff-based rendering (only changed areas)

Memory Usage

  • Typical: <50 MB for TUI components
  • Large sessions: <200 MB with 10,000 messages
  • Strategy: Streaming large content, virtual scrolling

Optimization Techniques

  1. Diff-based Rendering: Only re-render changed widgets
  2. Virtual Scrolling: Render visible area only
  3. Layout Caching: Cache layout calculations
  4. Theme Precompilation: Compile styles at load time
  5. Event Debouncing: Batch rapid events (resize, scroll)

Benchmarks

# Run TUI performance benchmarks
cargo bench -p ricecoder-tui

# Results (typical hardware):
# - Render frame: ~2-5ms
# - Handle keypress: <1ms
# - Apply theme: ~10ms
# - Layout calculation: ~0.5ms

Dependencies

Core Dependencies

[dependencies]
ratatui = "0.29"            # Terminal rendering
crossterm = "0.27"          # Cross-platform I/O
tokio = "1.0"               # Async runtime

# RiceCoder infrastructure (no business logic)
ricecoder-storage = "0.1"   # Config persistence
ricecoder-themes = "0.1"    # Theme management
ricecoder-keybinds = "0.1"  # Keybind handling
ricecoder-images = "0.1"    # Image display
ricecoder-help = "0.1"      # Help system

Dependency Rules

Allowed: Infrastructure crates (storage, themes, images)
Forbidden: Business logic crates (sessions, providers, agents)

Business logic injected via:

  • Dependency Injection container
  • Event callbacks
  • Interface traits

Testing Strategy

Test Organization

ricecoder-tui/
├── src/
│   └── **/*.rs          # Inline unit tests
└── tests/
    ├── integration/     # Integration tests
    ├── accessibility/   # Accessibility tests
    └── layout/          # Layout tests

Test Coverage

Area Tests Coverage
Component rendering 45+ 85%
Event handling 38+ 88%
Layout management 25+ 90%
Theme application 20+ 92%
Accessibility 15+ 80%
Total 143+ 87%

Key Test Scenarios

  • Component lifecycle (mount, update, unmount)
  • Event propagation and handling
  • Theme switching without state loss
  • Layout adaptation to terminal size
  • Keyboard navigation completeness
  • Screen reader announcements
  • Cross-platform compatibility (Windows, Linux, macOS)

See Also

Clone this wiki locally