Overview
teapot provides a functional, declarative approach to building terminal user interfaces:
- Model-Update-View - Core architecture based on The Elm Architecture
- Composable Components - Reusable widgets like spinners, inputs, and selectors
- Form System - Declarative form building with validation
- CI-Friendly - Automatic non-interactive mode detection
Installation
Run the following Cargo command in your project directory:
Quick Start
use ;
Components
Text Input
Single-line text input with cursor support, placeholder text, and optional password masking.
use TextInput;
let input = new
.placeholder
.prompt;
Text Area
Multi-line text editor with cursor navigation, scrolling, and line editing.
use TextArea;
let textarea = new
.placeholder
.height
.width;
External Editor Support
Open content in your preferred editor with Ctrl+O:
let textarea = new
.placeholder
.editor // Use VS Code (default: $VISUAL or $EDITOR)
.editor_extension; // File extension for syntax highlighting
Selection Components
Select
Single-choice selection from a list of options.
use Select;
let select = new
.options;
MultiSelect
Multiple-choice selection with checkboxes and optional min/max constraints.
use MultiSelect;
let select = new
.options
.min
.max;
Confirm
Yes/No confirmation prompt with customizable default.
use Confirm;
let confirm = new
.default;
List
Filterable, paginated list with keyboard navigation and search.
use List;
let list = new
.items
.height
.filterable;
Progress Indicators
Spinner
Animated loading indicator for indeterminate operations.
use ;
let spinner = new
.style
.message;
Progress
Progress bar for operations with known completion percentage.
use Progress;
let progress = new
.total
.current
.message;
MultiProgress
Parallel progress bars for tracking multiple concurrent tasks.
use MultiProgress;
let mp = new
.add_task
.add_task
.add_task;
Display Components
Viewport
Scrollable container for long content with keyboard navigation.
use Viewport;
let viewport = new
.content;
Table
Data table with columns, alignment options, and row selection.
use ;
let table = new
.columns
.rows
.height;
Forms
Build multi-step forms with validation, inspired by Huh.
Basic Form
use ;
let form = new
.title
.group
.group;
Form Layouts
Control how form groups are displayed:
use ;
// Default: one group at a time (wizard-style)
let wizard = new.layout;
// Stack: all groups visible at once
let stacked = new.layout;
// Columns: side-by-side layout
let columns = new.layout;
All Field Types
use ;
// Text input with validation
new
.title
.placeholder
.required
.build;
// Single selection
new
.title
.options
.build;
// Multiple selection with constraints
new
.title
.options
.min
.max
.build;
// Yes/No confirmation
new
.title
.default
.build;
// Display-only note
new
.title
.build;
// File/directory picker
new
.title
.directory
.extensions
.build;
Dynamic Content
Field titles and descriptions can update dynamically:
use Arc;
use ;
let attempt = new;
let attempt_clone = attempt.clone;
new
.title_fn
.description_fn
.build;
FilePicker Component
Browse and select files/directories:
use FilePicker;
let picker = new
.title
.directory
.extensions // Filter by extension
.show_hidden // Hide dotfiles
.height; // Visible rows
// Or for directory selection only
let dir_picker = new
.title
.dirs_only;
Styling
Teapot includes a comprehensive styling system inspired by Lip Gloss.
Basic Styling
use ;
let styled = new
.fg
.bg
.bold
.italic
.border
.render;
CSS-like Shorthand
Padding and margin support CSS-style shorthand (1, 2, 3, or 4 values):
use Style;
// All sides: 2
new.padding;
// Vertical: 1, Horizontal: 2
new.padding;
// Top: 1, Horizontal: 2, Bottom: 3
new.margin;
// Top: 1, Right: 2, Bottom: 3, Left: 4 (clockwise)
new.margin;
Block Dimensions
Control width, height, and alignment:
use ;
let box_style = new
.width
.height
.max_width
.align
.border;
Layout Utilities
Compose blocks horizontally or vertically:
use ;
// Side-by-side blocks (aligned at top)
let combined = join_horizontal_with;
// Stacked blocks (centered horizontally)
let stacked = join_vertical_with;
// Position content in a box
let centered = place;
Adaptive Colors
Colors that adapt to light/dark terminal backgrounds:
use Color;
// Different colors for light vs dark backgrounds
let adaptive = Adaptive ;
// Full color specification for all terminal types
let complete = Complete ;
Style Inheritance
Build styles incrementally:
use Style;
let base = new
.fg
.bold;
let highlight = new
.inherit // Copy unset properties from base
.bg;
// Unset specific properties
let plain = highlight.unset_bold.unset_bg;
Program Configuration
Builder Pattern
Configure the program with a fluent builder API:
use ;
use Duration;
new
.with_alt_screen // Use alternate screen buffer
.with_mouse // Enable mouse events
.with_bracketed_paste // Enable paste detection
.with_focus_change // Enable focus/blur events
.with_tick_rate // ~60 FPS
.with_accessible // Force accessible mode
.run?;
Message Filtering
Pre-process or block messages before they reach your update function:
new
.with_filter
.run?;
Commands
The cmd module provides Bubble Tea-style command functions:
use cmd;
use Duration;
// Quit the program
quit
// Batch multiple commands
batch
// Sequential execution
sequence
// Periodic tick
tick
// No-op command
none
External Process Execution
Spawn external processes (editors, etc.) with terminal teardown/restore:
use Cmd;
use Command;
let mut cmd = new;
cmd.arg;
run_process
Architecture
The framework follows The Elm Architecture:
- Model - Your application state (any Rust struct)
- Message - Events that trigger state changes
- Update - Pure function that handles messages and updates state
- View - Pure function that renders state as a string
- Commands - Side effects (timers, async operations)
flowchart TD
subgraph Runtime["Runtime Loop"]
direction LR
Model["Model<br/>(State)"]
View["View<br/>(Render)"]
Update["Update<br/>(Logic)"]
end
Model --> View
View -->|"returns String"| Terminal["Terminal Output"]
Events["User Events"] --> Update
Update -->|"New Model + Cmd"| Model
subgraph Commands["Commands (Effects)"]
Tick["Tick timers"]
Async["Async I/O"]
Quit["Quit signal"]
end
Update --> Commands
Commands --> Update
CI/Script Compatibility
The framework automatically detects non-interactive environments:
- No animations or spinners
- Clear error messages
- Appropriate exit codes
- Works with piped input/output
Accessibility
Teapot supports accessible mode for screen reader users and other assistive technologies.
Enabling Accessible Mode
Set the ACCESSIBLE environment variable:
ACCESSIBLE=1
What Changes in Accessible Mode
- Plain text output - No ANSI escape codes or visual formatting
- Numbered options - Selection components use numbers instead of arrow navigation
- Line-based input - Standard stdin reading instead of raw terminal mode
- Clear prompts - Screen reader-friendly text descriptions
Accessible Forms
Use Form::run_accessible() for a fully accessible form experience:
use ;
let mut form = new
.title
.group;
// Run in accessible mode (line-based prompts)
match form.run_accessible
Example Accessible Session
=== User Survey ===
Your name
?
> Alice
Favorite color
? Favorite color
1) Red
2) Green
* 3) Blue
Enter number (or q to cancel): 3
Subscribe to newsletter?
? Subscribe to newsletter? (y/N) y
Form completed!
Custom Accessible Components
Components can implement the Accessible trait for custom accessible rendering:
use ;
Environment Variables
| Variable | Description |
|---|---|
ACCESSIBLE=1 |
Enable accessible mode |
NO_COLOR=1 |
Disable colors (respected automatically) |
REDUCE_MOTION=1 |
Disable animations |