2 releases
| 0.1.2 | Feb 7, 2026 |
|---|---|
| 0.1.1 | Feb 7, 2026 |
#2938 in Command line utilities
Used in agentflow-cli
210KB
4K
SLoC
flow-tui
Beautiful terminal-based user interface for feature management, built with Ratatui and Crossterm.
What it does
flow-tui is a full-screen terminal application that turns your command line into an interactive dashboard. Think of it like Spotify's terminal interface or htop, but for managing features and tasks.
Instead of opening a web browser, you get a native terminal experience with:
- Kanban board with drag-and-drop feel (keyboard navigation)
- Dependency graph visualization (ASCII art)
- Live agent status showing what multiple AI agents are working on
- Event logs showing real-time updates
- Responsive layout that adapts to your terminal size
It's perfect for developers who live in the terminal and want to stay in their flow state.
Architecture
flow-tui/
├── main.rs - Entry point and event loop
├── lib.rs - Module exports
├── app.rs - Application state and logic
├── theme.rs - Color schemes and styling
├── input.rs - Keyboard event handling
└── views/
├── mod.rs - View organization
├── kanban.rs - Kanban board view
├── agents.rs - Agent status view
├── logs.rs - Event log view
├── graph.rs - Dependency graph view
└── help.rs - Help screen
View Architecture
Terminal Screen
↓
Current View (Kanban, Agents, Logs, Graph)
↓
Ratatui Widgets (Table, Block, Paragraph)
↓
Theme Colors (Dark, Light, Solarized)
Usage
Running the TUI
# Build and run
cargo run -p flow-tui
# Or install and run
cargo install --path crates/flow-tui
flow-tui
Keyboard Controls
Global Controls:
?- Toggle help screenqorCtrl+C- Quit applicationTab- Cycle through views1- Switch to Kanban view2- Switch to Agents view3- Switch to Logs view4- Switch to Graph viewt- Cycle through themes
Navigation:
jorDown- Move selection downkorUp- Move selection upPageDown- Scroll down one pagePageUp- Scroll up one pageHome- Go to topEnd- Go to bottom
Kanban View:
Enter- View feature detailsSpace- Toggle feature statusd- Mark as donei- Mark in progressf- Mark as failing
Graph View:
+- Zoom in-- Zoom out- Arrow keys - Pan around
Views
Kanban Board
┌─ Kanban View ────────────────────────────────────┐
│ │
│ BACKLOG IN PROGRESS DONE │
│ ┌──────────┐ ┌──────────┐ ┌──────────┐ │
│ │ Feature 1│ │ Feature 3│ │ Feature 5│ │
│ │ ⚡ High │ │ 🔄 50% │ │ ✓ │ │
│ └──────────┘ └──────────┘ └──────────┘ │
│ ┌──────────┐ ┌──────────┐ ┌──────────┐ │
│ │ Feature 2│ │ Feature 4│ │ Feature 6│ │
│ │ ⬇️ 1 dep │ │ ⬇️ 2 dep│ │ ✓ │ │
│ └──────────┘ └──────────┘ └──────────┘ │
│ │
└───────────────────────────────────────────────────┘
Features organized by status:
- Backlog: Not started
- In Progress: Currently being worked on
- Done: Completed and passing
Agent Status
┌─ Agent Status ───────────────────────────────────┐
│ │
│ AGENT STATUS TASK UPTIME │
│ ─────────────────────────────────────────────── │
│ 🤖 claude Working OAuth impl 2h 15m │
│ 🤖 codex Idle - 1h 45m │
│ 🤖 gemini Working UI tests 0h 30m │
│ │
│ Throughput: 3.2 tasks/hour │
│ Active: 2/3 agents │
│ │
└───────────────────────────────────────────────────┘
Real-time monitoring of AI agent activity.
Logs View
┌─ Event Logs ─────────────────────────────────────┐
│ │
│ 14:23:45 [INFO] Feature #42 marked as passing │
│ 14:23:40 [WARN] Feature #12 has failing tests │
│ 14:23:35 [INFO] Agent 'claude' claimed task 15 │
│ 14:23:30 [INFO] 3 new features created │
│ 14:23:25 [DEBUG] Database backup completed │
│ │
│ [Auto-scroll: ON] [Filter: ALL] │
│ │
└───────────────────────────────────────────────────┘
Scrollable event history with filtering.
Dependency Graph
┌─ Dependency Graph ───────────────────────────────┐
│ │
│ [1] Auth │
│ ├─> [2] Login UI │
│ │ └─> [5] Dashboard │
│ └─> [3] Rate Limiting │
│ └─> [4] Tests │
│ │
│ Legend: ✓ Done 🔄 In Progress ⚠️ Blocked │
│ │
└───────────────────────────────────────────────────┘
ASCII visualization of feature dependencies.
Themes
Built-in Themes
pub enum Theme {
Dark, // Dark background, light text
Light, // Light background, dark text
Solarized, // Solarized color scheme
Monokai, // Monokai editor theme
Nord, // Nord color palette
}
Theme Cycling
Press t to cycle through themes:
- Dark → Light → Solarized → Monokai → Nord → Dark
Custom Theme Colors
pub struct TuiTheme {
pub bg: Color,
pub fg: Color,
pub border: Color,
pub highlight: Color,
pub success: Color,
pub warning: Color,
pub error: Color,
pub info: Color,
}
// Example: Create custom theme
let custom_theme = TuiTheme {
bg: Color::Rgb(0x1e, 0x1e, 0x2e),
fg: Color::Rgb(0xcd, 0xdc, 0xfe),
border: Color::Rgb(0x6c, 0x71, 0xc4),
highlight: Color::Rgb(0xf3, 0x8b, 0xa8),
success: Color::Rgb(0xa3, 0xbe, 0x8c),
warning: Color::Rgb(0xeb, 0xcb, 0x8b),
error: Color::Rgb(0xbf, 0x61, 0x6a),
info: Color::Rgb(0x88, 0xc0, 0xd0),
};
Responsive Layout
The TUI automatically adapts to your terminal size:
Full Layout (≥120 columns)
┌───────────────────────────────────────────────────┐
│ Header with stats and navigation tabs │
├───────────────────────────────────────────────────┤
│ Sidebar │ Main Content Area (Kanban/Logs) │
│ - Stats │ │
│ - Filters │ │
│ - Actions │ │
└───────────────────────────────────────────────────┘
Compact Layout (80-119 columns)
┌────────────────────────────────┐
│ Header │
├────────────────────────────────┤
│ Main Content (full width) │
│ │
│ Stats shown in header │
└────────────────────────────────┘
Mobile Layout (<80 columns)
┌──────────────┐
│ Header │
├──────────────┤
│ Content │
│ (minimal) │
│ │
└──────────────┘
Integration with Database
use flow_tui::app::App;
use flow_db::Database;
#[tokio::main]
async fn main() -> Result<()> {
// Open database
let db = Database::open("features.db")?;
// Create TUI app
let mut app = App::new();
// Load features from database
let conn = db.writer().lock().unwrap();
app.features = FeatureStore::get_all(&conn)?;
// Run TUI event loop
run_tui(&mut app).await?;
Ok(())
}
Code Example: Custom View
use ratatui::{Frame, layout::{Constraint, Direction, Layout}, widgets::{Block, Borders, Paragraph}};
use crate::app::App;
pub fn render_custom_view(frame: &mut Frame, app: &App) {
let area = frame.area();
// Create layout
let chunks = Layout::default()
.direction(Direction::Vertical)
.constraints([
Constraint::Length(3), // Header
Constraint::Min(0), // Content
Constraint::Length(3), // Footer
])
.split(area);
// Header
let header = Block::default()
.borders(Borders::ALL)
.border_style(app.tui_theme.border)
.title("My Custom View");
frame.render_widget(header, chunks[0]);
// Content
let content = Paragraph::new("Hello from custom view!")
.style(app.tui_theme.fg);
frame.render_widget(content, chunks[1]);
// Footer
let footer = Paragraph::new("Press ? for help")
.style(app.tui_theme.info);
frame.render_widget(footer, chunks[2]);
}
Event Loop
The TUI uses a hybrid event-driven architecture:
use crossterm::event::{self, Event, KeyCode};
use ratatui::Terminal;
use std::time::Duration;
pub async fn run_tui(app: &mut App) -> Result<()> {
let mut terminal = setup_terminal()?;
loop {
// Render current frame
terminal.draw(|f| app.render(f))?;
// Wait for event with timeout (for animation/updates)
if event::poll(Duration::from_millis(100))? {
if let Event::Key(key) = event::read()? {
if app.handle_key(key) {
break; // Quit requested
}
} else if let Event::Resize(w, h) = event::read()? {
app.resize(w, h);
}
}
// Update state (e.g., check for DB changes)
app.tick().await?;
}
cleanup_terminal()?;
Ok(())
}
Performance
- Render time: ~2-5ms per frame
- Input latency: <10ms
- Memory usage: ~5MB for 1000 features
- Supports: Up to 10,000 features before noticeable lag
Optimization Tips
// Use double-buffering (built into Ratatui)
// Only redraw on changes
if app.needs_redraw() {
terminal.draw(|f| app.render(f))?;
}
// Limit log history
if app.log_messages.len() > 100 {
app.log_messages.drain(0..10); // Remove oldest 10
}
// Virtualize large lists (only render visible items)
let visible_range = app.scroll_offset..(app.scroll_offset + terminal_height);
let visible_features = &app.features[visible_range];
Testing
# Run TUI tests
cargo test -p flow-tui
# Run with test TUI backend (doesn't require terminal)
cargo test -p flow-tui -- --nocapture
# Manual testing in isolated terminal
cargo run -p flow-tui
Integration Testing
use ratatui::backend::TestBackend;
use ratatui::Terminal;
#[test]
fn test_kanban_render() {
let backend = TestBackend::new(80, 24);
let mut terminal = Terminal::new(backend).unwrap();
let mut app = App::new();
terminal.draw(|f| app.render(f)).unwrap();
let buffer = terminal.backend().buffer();
// Assert buffer contains expected content
assert!(buffer.content().contains("Kanban"));
}
API Reference
App Methods
| Method | Description |
|---|---|
App::new() |
Create new app with default state |
app.render(frame) |
Render current view to terminal |
app.handle_key(key) |
Process keyboard input, returns true if quitting |
app.resize(width, height) |
Handle terminal resize |
app.add_log(message) |
Add message to event log |
app.load_demo_data() |
Load sample features for testing |
View Enum
pub enum View {
Kanban, // Kanban board
Agents, // Agent status
Logs, // Event logs
Graph, // Dependency graph
}
Layout Modes
pub enum LayoutMode {
Full, // ≥120 columns: sidebar + main
Compact, // 80-119 columns: main only
Mobile, // <80 columns: minimal
}
Troubleshooting
Terminal rendering issues
# Set TERM variable
export TERM=xterm-256color
# Clear screen
clear
# Reset terminal state
reset
Colors not working
// Check terminal color support
if crossterm::tty::IsTty::is_tty(&std::io::stdout()) {
println!("Terminal supports colors");
} else {
println!("Terminal does not support colors");
}
Cursor remains visible
The TUI automatically hides the cursor on startup and restores it on exit. If it doesn't, run:
tput cnorm # Make cursor visible
Related Crates
- flow-core: Feature and Task types
- flow-db: Database backend for features
- flow-server: Web server alternative
- Ratatui: Terminal UI library (docs)
- Crossterm: Cross-platform terminal manipulation (docs)
Dependencies
~37–53MB
~790K SLoC