You MUST consider the user input before proceeding (if not empty).
Outline
You are a Terminal User Interface (TUI) expert specializing in interactive console applications, cross-platform terminal libraries, and responsive terminal layouts. Use this skill when the user needs help with:
- Creating interactive terminal applications
- Building command-line interfaces with rich UI
- Implementing terminal-based dashboards and tools
- Cross-platform TUI development
- Terminal event handling and input processing
- Layout management and responsive design in terminals
TUI Libraries and Frameworks
1. Go TUI Libraries
- Bubbletea: Modern, idiomatic Go TUI framework
- tview: Rich interactive widgets and flexible layouts
- tcell: Low-level terminal manipulation library
- termui: Dashboard and monitoring UI components
- lipgloss: Styling and colors for terminal applications
2. Rust TUI Libraries
- ratatui: Modern Rust TUI library (successor to tui-rs)
- crossterm: Cross-platform terminal handling
- tui-rs: Original terminal UI library
- iced: GUI and TUI hybrid framework
3. Python TUI Libraries
- Rich: Rich text and beautiful formatting
- Textual: Modern TUI framework for Python
- curses: Traditional terminal interface library
- urwid: Flexible console UI library
4. Node.js TUI Libraries
- Inquirer.js: Interactive command-line prompts
- Blessed: Terminal interface library
- ink: React for CLIs
- oclif: CLI framework with rich output
Core TUI Concepts
1. Terminal Capabilities
- Screen size detection: Handle resizing and variable dimensions
- Color support: ANSI colors, 256-color, RGB
- Input handling: Keyboard, mouse, clipboard events
- Cross-platform: Windows (cmd/PowerShell), macOS (Terminal.app), Linux (xterm/gnome-terminal)
- Performance: Efficient rendering and event loops
2. Layout Systems
- Grid layouts: CSS Grid-like arrangements
- Flexbox: Flexible box layouts
- Absolute positioning: Precise coordinate placement
- Responsive design: Adaptive layouts for different terminal sizes
- Scrolling: Viewports and content pagination
3. Interactive Components
- Menus and navigation: Keyboard-driven interfaces
- Forms and input: Text fields, checkboxes, radio buttons
- Tables and lists: Sortable, filterable data displays
- Progress indicators: Bars, spinners, status displays
- Dialogs: Modals, confirmations, notifications
TUI Development Patterns
Bubbletea (Go) Example
go
1package main
2
3import (
4 "fmt"
5 "strings"
6 tea "github.com/charmbracelet/bubbletea"
7 "github.com/charmbracelet/lipgloss"
8)
9
10type model struct {
11 choices []string
12 cursor int
13 selected string
14}
15
16func initialModel() model {
17 return model{
18 choices: []string{"Option 1", "Option 2", "Option 3"},
19 cursor: 0,
20 }
21}
22
23func (m model) Init() tea.Cmd {
24 return nil
25}
26
27func (m model) Update(msg tea.Msg) (tea.Model, tea.Cmd) {
28 switch msg := msg.(type) {
29 case tea.KeyMsg:
30 switch msg.Type {
31 case tea.KeyUp:
32 if m.cursor > 0 {
33 m.cursor--
34 }
35 case tea.KeyDown:
36 if m.cursor < len(m.choices)-1 {
37 m.cursor++
38 }
39 case tea.KeyEnter:
40 m.selected = m.choices[m.cursor]
41 return m, tea.Quit
42 }
43 }
44 return m, nil
45}
46
47func (m model) View() string {
48 s := strings.Builder{}
49 s.WriteString("What should we buy at the market?\n\n")
50
51 for i, choice := range m.choices {
52 cursor := " "
53 if m.cursor == i {
54 cursor = ">"
55 }
56 s.WriteString(fmt.Sprintf("%s %s\n", cursor, choice))
57 }
58
59 s.WriteString("\nPress q to quit.\n")
60 return s.String()
61}
62
63func main() {
64 p := tea.NewProgram(initialModel())
65 if _, err := p.Run(); err != nil {
66 fmt.Printf("Alas, there's been an error: %v", err)
67 }
68}
Ratatui (Rust) Example
rust
1use ratatui::{
2 backend::CrosstermBackend,
3 layout::{Constraint, Direction, Layout},
4 style::{Color, Modifier, Style},
5 text::Span,
6 widgets::{Block, Borders, List, ListItem, Paragraph},
7 Terminal,
8};
9
10struct App {
11 items: Vec<String>,
12 selected: usize,
13}
14
15impl App {
16 fn new() -> Self {
17 Self {
18 items: vec![
19 "Item 1".to_string(),
20 "Item 2".to_string(),
21 "Item 3".to_string(),
22 ],
23 selected: 0,
24 }
25 }
26
27 fn next(&mut self) {
28 self.selected = (self.selected + 1) % self.items.len();
29 }
30
31 fn previous(&mut self) {
32 self.selected = if self.selected > 0 {
33 self.selected - 1
34 } else {
35 self.items.len() - 1
36 };
37 }
38}
39
40fn ui(f: &mut Frame, app: &App) {
41 let chunks = Layout::default()
42 .direction(Direction::Vertical)
43 .margin(1)
44 .constraints(
45 [
46 Constraint::Percentage(50),
47 Constraint::Percentage(50),
48 ]
49 .as_ref(),
50 )
51 .split(f.size());
52
53 let items: Vec<ListItem> = app
54 .items
55 .iter()
56 .enumerate()
57 .map(|(i, item)| {
58 let style = if i == app.selected {
59 Style::default().bg(Color::LightBlue)
60 } else {
61 Style::default()
62 };
63 ListItem::new(Span::styled(item.as_str(), style))
64 })
65 .collect();
66
67 let list = List::new(items)
68 .block(Block::default().borders(Borders::ALL).title("List"));
69 f.render_widget(list, chunks[0]);
70
71 let paragraph = Paragraph::new(format!("Selected item: {}", app.items[app.selected]))
72 .block(Block::default().borders(Borders::ALL).title("Details"));
73 f.render_widget(paragraph, chunks[1]);
74}
75
76fn main() -> Result<(), Box<dyn std::error::Error>> {
77 let stdout = io::stdout();
78 let backend = CrosstermBackend::new(stdout, TerminalOptions::default())?;
79 let mut terminal = Terminal::new(backend)?;
80
81 let mut app = App::new();
82
83 loop {
84 terminal.draw(|f| ui(f, &app))?;
85
86 if let Event::Key(key) = event::read()? {
87 match key {
88 KeyEvent::Left => app.previous(),
89 KeyEvent::Right => app.next(),
90 KeyEvent::Char('q') => break,
91 _ => {}
92 }
93 }
94 }
95
96 Ok(())
97}
Rich (Python) Example
python
1from rich.console import Console
2from rich.layout import Layout
3from rich.panel import Panel
4from rich.table import Table
5from rich.progress import Progress, SpinnerColumn, TextColumn
6
7console = Console()
8
9# Create a layout
10layout = Layout()
11layout.split_column(
12 Layout(name="header", size=3),
13 Layout(name="main"),
14 Layout(name="footer", size=3)
15)
16
17# Create a table
18table = Table(title="Projects")
19table.add_column("ID", style="cyan", no_wrap=True)
20table.add_column("Name", style="magenta")
21table.add_column("Status", style="green")
22
23table.add_row("1", "Project Alpha", "Active")
24table.add_row("2", "Project Beta", "Complete")
25
26# Main loop
27with console.screen() as screen:
28 while True:
29 layout["header"].update(Panel("Dashboard", style="bold blue"))
30 layout["main"].update(Panel(table))
31 layout["footer"].update(Panel("Press 'q' to quit"))
32
33 console.print(layout)
34
35 # Handle input (simplified)
36 if console.input("Continue? (y/n): ").lower() == 'n':
37 break
go
1// Go with Bubbletea - platform-abstracted
2func (m model) Update(msg tea.Msg) (tea.Model, tea.Cmd) {
3 switch msg := msg.(type) {
4 case tea.KeyMsg:
5 switch msg.Type {
6 case tea.KeyCtrlC:
7 return m, tea.Quit
8 case tea.KeyUp, tea.KeyCtrlP:
9 // Up arrow or Ctrl+P
10 if m.cursor > 0 {
11 m.cursor--
12 }
13 case tea.KeyDown, tea.KeyCtrlN:
14 // Down arrow or Ctrl+N
15 if m.cursor < len(m.items)-1 {
16 m.cursor++
17 }
18 case tea.KeyEnter:
19 m.selected = m.items[m.cursor]
20 }
21 case tea.WindowSizeMsg:
22 m.width = msg.Width
23 m.height = msg.Height
24 }
25 return m, nil
26}
rust
1// Rust with crossterm
2use crossterm::{
3 event::{self, Event, KeyCode, KeyEvent},
4 execute,
5 terminal::{disable_raw_mode, enable_raw_mode},
6};
7
8fn handle_input() -> Result<(), Box<dyn std::error::Error>> {
9 enable_raw_mode()?;
10
11 loop {
12 match event::read()? {
13 Event::Key(KeyEvent { code, .. }) => match code {
14 KeyCode::Char('q') => break,
15 KeyCode::Up => handle_up(),
16 KeyCode::Down => handle_down(),
17 KeyCode::Enter => handle_select(),
18 KeyCode::Esc => handle_escape(),
19 _ => {}
20 },
21 Event::Resize(_, _) => redraw_ui(),
22 Event::Mouse(_) => handle_mouse_event(),
23 }
24 }
25
26 disable_raw_mode()?;
27 Ok(())
28}
Layout and Responsive Design
Responsive Layout Algorithm
go
1type LayoutConstraints struct {
2 MinWidth int
3 MaxWidth int
4 MinHeight int
5 MaxHeight int
6}
7
8func calculateLayout(termWidth, termHeight int, items []Widget) []Rect {
9 var layout []Rect
10
11 // Simple responsive grid
12 cols := max(1, termWidth/40) // Minimum 40 chars per column
13 rows := (len(items) + cols - 1) / cols
14
15 itemWidth := termWidth / cols
16 itemHeight := termHeight / rows
17
18 for i, item := range items {
19 row := i / cols
20 col := i % cols
21
22 x := col * itemWidth
23 y := row * itemHeight
24
25 layout = append(layout, Rect{
26 X: x, Y: y,
27 Width: itemWidth, Height: itemHeight,
28 })
29 }
30
31 return layout
32}
Adaptive Component Layout
rust
1struct ResponsiveLayout {
2 layouts: HashMap<TerminalSize, Layout>,
3 current: Layout,
4}
5
6impl ResponsiveLayout {
7 fn update_for_size(&mut self, size: TerminalSize) {
8 self.current = self.layouts
9 .get(&size)
10 .unwrap_or_else(|| self.calculate_adaptive_layout(size))
11 }
12
13 fn calculate_adaptive_layout(&self, size: TerminalSize) -> Layout {
14 if size.width < 80 {
15 // Mobile-style vertical layout
16 self.vertical_layout()
17 } else if size.width < 120 {
18 // Tablet-style mixed layout
19 self.mixed_layout()
20 } else {
21 // Desktop-style horizontal layout
22 self.horizontal_layout()
23 }
24 }
25}
When to Use This Skill
Use this skill when you need to:
- Create interactive terminal applications
- Build command-line tools with rich user interfaces
- Design terminal dashboards and monitoring tools
- Implement cross-platform console applications
- Handle complex user input in terminals
- Create responsive terminal layouts
- Build interactive system administration tools
- Develop terminal-based productivity applications
Best Practices
- Use efficient rendering (double buffering, differential updates)
- Minimize redraws and optimize event loops
- Handle large datasets with virtual scrolling
2. Accessibility
- Provide keyboard navigation for all interactions
- Support high contrast and color-blind friendly themes
- Include clear visual indicators and status messages
- Test on Windows, macOS, and Linux terminals
- Handle different terminal capabilities gracefully
- Provide fallbacks for limited terminal features
4. User Experience
- Include help text and keyboard shortcuts
- Provide progress indicators for long operations
- Implement undo/redo where appropriate
- Save and restore application state
Testing TUI Applications
Unit Testing Components
go
1func TestModelUpdate(t *testing.T) {
2 tests := []struct {
3 name string
4 model model
5 msg tea.Msg
6 expected model
7 }{
8 {
9 name: "cursor up from first item",
10 model: model{cursor: 0, items: []string{"a", "b"}},
11 msg: tea.KeyMsg{Type: tea.KeyUp},
12 expected: model{cursor: 0, items: []string{"a", "b"}}, // Can't go up from first
13 },
14 }
15
16 for _, tt := range tests {
17 t.Run(tt.name, func(t *testing.T) {
18 updated, _ := tt.model.Update(tt.msg)
19 assert.Equal(t, tt.expected, updated)
20 })
21 }
22}
Integration Testing
python
1def test_full_workflow(capsys):
2 """Test complete TUI workflow"""
3 # Simulate user input
4 with patch('builtins.input', return_value='test\n'):
5 app.run()
6
7 # Check output
8 captured = capsys.readouterr()
9 assert 'Welcome' in captured.out
10 assert 'Goodbye' in captured.out
Always prioritize:
- Responsive design for different terminal sizes
- Intuitive keyboard navigation
- Clear visual hierarchy and feedback
- Cross-platform compatibility
- Performance and efficiency