oxi-tui
Terminal UI library for Rust — component framework, differential rendering, and theme system.
Overview
oxi-tui provides a lightweight, component-based terminal UI framework designed for building interactive applications. It features:
- Component trait — composable UI building blocks with focus management
- Differential rendering — only redraws changed lines for flicker-free updates
- Theme system — customizable colors, fonts, and spacing with hot-reload from TOML/JSON
- Built-in components —
Text,Input,Editor,Markdown,Completion - Overlay system — modal dialogs and popovers
- Event abstraction — unified keyboard, mouse, and resize events
Quick Start
Add to your Cargo.toml:
[]
= { = "path/to/oxi-tui" }
Basic usage:
use ;
Component Overview
The Component Trait
All UI elements implement the Component trait:
Components render into a Surface (a grid of Cells), and the TUI event loop handles diffing and flushing to the terminal.
Built-in Components
Text
Static text display:
use Text;
let text = new;
Input
Single-line text input with configurable options:
use ;
let input = new;
Editor
Multi-line text editor with mention support:
use ;
let editor = new;
Markdown
Renders Markdown content with syntax highlighting:
use ;
let md = new;
Completion
Autocomplete popup for file paths and custom suggestions:
use ;
let completer = new;
let popup = new;
Custom Components
Implement the Component trait:
use ;
Theme System
Built-in Themes
Two themes are included:
use Theme;
let dark = dark; // Catppuccin-inspired dark theme
let light = light; // Catppuccin-inspired light theme
Theme Structure
A theme consists of three parts:
Color Scheme
The ColorScheme provides semantic colors:
| Field | Purpose |
|---|---|
foreground |
Default text color |
background |
Default background |
primary |
Primary accent |
secondary |
Secondary accent |
error |
Error messages |
warning |
Warning messages |
success |
Success indicators |
muted |
Dimmed/placeholder text |
accent |
Highlight color |
border |
Borders and separators |
cursor_fg |
Cursor text color |
cursor_bg |
Cursor background |
selection_bg |
Selected text background |
Colors support multiple formats:
use Color;
Rgb // True color: #dcdfe4
Indexed // 256-color palette index
Red // Named ANSI colors
Default // Terminal default
Loading Custom Themes
Themes can be loaded from TOML or JSON files:
TOML (my-theme.toml):
= "midnight"
[]
= "#cdd6f4"
= "#1e1e2e"
= "#89b4fa"
= "#f38ba8"
= "#a6e3a1"
JSON (my-theme.json):
Color values accept: hex (#rrggbb, #rgb), named (red, bright-black), or indexed (i42).
Theme Manager with Hot-Reload
use ;
// Start with dark theme
let mut manager = dark;
// Watch a file for changes (auto-reloads on modification)
manager.watch_file?;
// In your event loop, check for reloads:
if manager.check_reload
// Get the current theme
let theme = manager.theme;
// Switch programmatically
manager.set_theme_by_name;
Rendering
Differential Rendering
The TUI uses a double-buffered, differential rendering approach:
- Each component renders into a
Surface(grid ofCells) - The renderer compares the new surface against the previous frame
- Only changed lines are written to the terminal
- Synchronized output (
CSI 2026) prevents flickering
Surface
A Surface is a 2D grid of Cells:
use ;
let mut surface = new;
// Set individual cells
surface.set;
// Get cell
let cell = surface.get;
// Track dirty regions
surface.mark_dirty; // Mark row 0 as dirty
let first = surface.first_dirty; // First dirty row
let last = surface.last_dirty; // Last dirty row
Cells
use ;
let cell = new
.character
.foreground
.background
.attributes
.build;
Overlay System
Overlays render on top of all other components and capture input first:
use ;
// Add an overlay
let id = tui.add_overlay;
// Remove an overlay
tui.remove_overlay;
// Remove all overlays
tui.clear_overlays;
// Escape key closes the top overlay by default
Event Handling
Event Types
Key Events
Mouse Events
Focus Management
Tab cycles focus between child components. Shift+Tab cycles backwards.
// Set focus programmatically
tui.set_focus;
// Get current focus index
let idx = tui.focus_index;
Custom Event Handler
tui.on_event;