tears
A simple and elegant framework for building TUI applications using The Elm Architecture (TEA).
Built on top of ratatui, Tears provides a clean, type-safe, and functional approach to terminal user interface development.
Features
- π― Simple & Predictable: Based on The Elm Architecture - easy to reason about and test
- π Async-First: Built-in support for async operations via Commands
- π‘ Subscriptions: Handle terminal events, timers, and custom event sources
- π§ͺ Testable: Pure functions for update logic make testing straightforward
- π Powered by Ratatui: Leverage the full power of the ratatui ecosystem
- π¦ Type-Safe: Leverages Rust's type system for safer TUI applications
Installation
Add this to your Cargo.toml:
[]
= "0.10"
= "0.30"
= "0.29"
= { = "1", = ["full"] }
See the Optional Features section for information about enabling ws (WebSocket) and http (HTTP Query/Mutation) features.
Getting Started
Minimal Example
Every tears application implements the Application trait with four required methods:
use *;
use Frame;
;
To run your application, create an Runtime and call run():
use NonZeroU32;
async
Complete Example
Here's a simple counter application that increments every second:
use ;
use Result;
use ;
use ;
use *;
use ;
async
Architecture
Tears follows The Elm Architecture (TEA) pattern:
ββββββββββββββββββββββββββββββββββββββββββββββββ
β β
β βββββββββββ ββββββββββ ββββββββ β
β β Model βββββββΆβ View βββββββΆβ UI β β
β βββββββββββ ββββββββββ ββββββββ β
β β² β
β β β
β ββββββ΄ββββββ ββββββββββββββββ β
β β Update βββββββ Messages β β
β ββββββββββββ ββββββββββββββββ β
β β² β² β
β β β β
β ββββββ΄ββββββ ββββββββ΄βββββββ β
β β Commands β βSubscriptionsβ β
β ββββββββββββ βββββββββββββββ β
β β
ββββββββββββββββββββββββββββββββββββββββββββββββ
Core Concepts
- Model: Your application state
- Message: Events that trigger state changes
- Update: Pure function that processes messages and returns new state + commands
- View: Pure function that renders UI based on current state
- Subscriptions: External event sources (keyboard, timers, network, etc.)
- Commands: Asynchronous side effects that produce messages
Built-in Subscriptions
- Terminal Events (
terminal::TerminalEvents): Keyboard, mouse, and resize events - Timer (
time::Timer): Periodic tick events - Signal (
signal::Signal): OS signal handling (Unix/Windows) - WebSocket (
websocket::WebSocket, requiresws): Real-time bidirectional communication - Query (
http::Query, requireshttp): HTTP data fetching with caching - Mutation (
http::Mutation, requireshttp): HTTP data modifications - MockSource (
mock::MockSource): Controllable mock for testing
Create custom subscriptions by implementing the SubscriptionSource trait.
Examples
Check out the examples/ directory for more examples:
counter.rs- A simple counter with timer and keyboard inputpanic_hook.rs- Restoring the terminal on panic withinstall_panic_hookviews.rs- Multiple view states with navigation and conditional subscriptionsdashboard.rs- Structured state management with nested state and child messagessignals.rs- OS signal handling with graceful shutdown (SIGINT, SIGTERM, etc.)command_timeout_retry.rs- Enforcing aCommanddeadline withtimeoutand recovering from failures withretrycommand_cancellation.rs- Cancelling superseded in-flight commands withcancellable/cancellable_withandCancelPolicywebsocket.rs- WebSocket echo chat demonstrating real-time communication (requireswsfeature)http_todo.rs- HTTP Todo list with Query subscription, Mutation, and cache management (requireshttpfeature)
RetryError/RetryPolicy and CommandId/CancelPolicy are imported explicitly
from tears::command rather than from the crate root or prelude.
Run an example:
Testing Your Application
tears::testing::TestStore drives an Application's update transitions and
command effects synchronously and deterministically, with no wall-clock waiting.
A test constructs the store from the application's flags, scripts messages with
send, moves virtual time with advance, asserts effect output with
receive/receive_matching/receive_quit, and closes the run with finish
(which fails the test if any deliverable output or unfinished effect is left
unaccounted for). Assertions are exhaustive by design.
use TestStore;
let mut store = new;
store.send;
store.advance; // move a Command::timeout deadline
store.receive_matching;
store.finish;
TestStore is constructed on a plain #[test] (never #[tokio::test]; it owns
its own paused time context) and does not execute subscription sources β it
observes only the declared set via subscription_ids. See the
tears::testing module docs for
the full contract, including deterministic time without TestStore. Worked,
runnable tests ship with the command examples:
Repository-wide test conventions live in docs/testing.md.
Optional Features
Tears supports optional features that can be enabled in your Cargo.toml:
WebSocket Support
[]
= { = "0.8", = ["ws", "rustls"] }
ws: Enables WebSocket subscription support- TLS backends (choose one for
wss://support):native-tls- Platform's native TLSrustls- Pure Rust TLS with native certificatesrustls-tls-webpki-roots- Pure Rust TLS with webpki certificates
HTTP Support
[]
= { = "0.8", = ["http"] }
http: Enables HTTP Query and Mutation supportQuerysubscription for automatic data fetching with cachingMutationfor data modifications (POST, PUT, PATCH, DELETE)QueryClientfor cache management and invalidation- Design rationale and invariants: RFC 0001:
httpModule Redesign
Inspiration & Design Philosophy
Tears is inspired by battle-tested architectures:
- Elm: The original Elm Architecture
- iced: Rust GUI framework (v0.12 design)
- Bubble Tea: Go TUI framework with TEA
The framework is designed with these principles:
- Simplicity First: Minimal and easy-to-understand API
- Thin Framework: Minimal abstraction over ratatui - you have full control
- Type Safety: Leverage Rust's type system for correctness
Minimum Supported Rust Version (MSRV)
Tears requires Rust 1.88.0 or later (uses edition 2024).
License
Licensed under the Apache License, Version 2.0. See LICENSE for details.
Contributing
Contributions are welcome! Please feel free to submit issues or pull requests.
Design contracts and invariants live in docs/rfcs. If you are writing or amending an RFC, run the pre-review checklist before requesting review. Testing conventions are documented in docs/testing.md.
Built with β€οΈ using ratatui