Expand description
Themeable terminal UI components for ratatui, plus the runtime that makes
them interactive.
Ratatui gives you widgets that draw. ratcn adds the parts a real app needs
on top: components that can be focused, hovered, and clicked, in a
shadcn-inspired visual style you can theme.
Themes do not have to be picked from a list: Theme::adaptive solves a
whole palette — wells, surfaces, text, lines, accents — from a background and
a foreground someone else chose. ratcn::terminal (feature termina) asks
the terminal for that pair and re-solves when the user changes it.
§Preview status
This is a preview release.
- The API is unstable. Pin an exact version.
- The CLI sets up terminal apps and copies components.
cargo ratcn initconfigures terminal Cargo packages and can install a starter only over Cargo’s untouched defaultmain.rs;cargo ratcn addcopies a built-in component when you want to own its source. - Twelve components are available:
Button,List,Select,Tabs,Dialog,Toaster,BarChartWidget,Tooltip,ScrollArea,Checkbox,Cycle, andProgressWidget.
§Getting started
The recommended way to set up a terminal project is with the cargo-ratcn CLI:
cargo install cargo-ratcn
cargo new my-app
cd my-app
cargo ratcn initinit adds ratcn with its termina feature and a compatible ratatui,
writes ratcn.toml, and creates src/components/mod.rs.
See Getting started
for starter apps and copying components with cargo ratcn add.
§Up to two halves, usable alone
Components have up to two cooperating halves:
- Paint widgets (
ButtonWidget,ListWidget,TabsWidget, …) are ordinary ratatuiWidgets. Tell one what to look like and render it. - Interactive components (
Button,List,Tabs, …) add focus and event handling, and paint through the widget half. These are declared throughruntime::Ratcn.
ToasterWidget is paint-only and stops at the widget half.
BarChartWidget is paint-only too, but it is also not a component in the
sense used above: it is a themed adapter over ratatui’s own BarChart,
adding theme colors, grouping, and a value-display switch to a chart ratatui
draws. Dialog and ScrollArea are interactive composites that stop at
the component half: their frame and their viewport are geometry the
component paints itself.
The paint widgets drop straight into a plain ratatui app. They take a theme
and some bools, and frame.render_widget(...) is the whole integration —
no Ratcn, no declaration pass, no message type. If you already have focus
and event handling you are happy with, take the components’ look and leave
the runtime alone. Where both halves exist, the interactive half paints
through the same widget.
§It does not take over your app
This is a toolkit, not a framework. Your app keeps its event loop, its state, and its update function. The runtime enters at exactly two call sites, and both can be removed again:
runtime::Ratcn::render— declare and paint this frame’s components.runtime::Ratcn::handle_event— route one event, get back a message.
State stays yours throughout. Components read it and return messages asking for changes; nothing writes your state but you.
§Where things live
Components, themes, and the state types you store (ToasterState,
Theme) are at the crate root. Runtime types — the
engine, focus, events, and the traits for writing your own components — are
under runtime.
Beside them sit the copy-support modules: button_shape, color,
geometry, linear_nav, list_core, selection_indicator, and
text_width. They hold the pieces more than one component needs — the
button idiom’s cap and fill rows, the color arithmetic every focus, hover,
and disabled state derives through, area arithmetic, item-index movement,
value-keyed items and their row viewport, the radio and checkbox markers,
display-width measurement — so a component module depends on the crate root
and these, and on no sibling component. That is what lets you copy one
component module into your own project and have it compile against ratcn
alone.
§Examples
use ratatui::{Terminal, backend::TestBackend};
use ratcn::{
Button, Theme,
runtime::{Event, EventResult, FocusState, KeyCode, KeyEvent, Ratcn},
};
#[derive(Default)]
struct AppState {
focus: FocusState,
saving: bool,
}
enum Msg {
FocusChanged(FocusState),
Save,
}
let mut state = AppState::default();
let mut ratcn = Ratcn::new()
.focus(|state: &AppState| &state.focus, Msg::FocusChanged);
let theme = Theme::default_dark();
let mut terminal = Terminal::new(TestBackend::new(20, 3)).expect("terminal");
// Declare the current component surface as part of every frame.
terminal.draw(|frame| {
let area = frame.area();
ratcn.render(frame, area, &state, &theme, |ctx| {
ctx.component(
"save",
Button::new("Save")
.disabled(state.saving)
.on_press(|| Msg::Save),
area,
);
});
}).expect("draw");
// Hand backend events to the retained surface from the last successful frame.
let event = Event::Key(KeyEvent::new(KeyCode::Enter));
match ratcn.handle_event(event, &state) {
EventResult::Emit(msg) => update(&mut state, msg),
EventResult::Consumed | EventResult::Ignored => {}
}Modules§
- button_
shape - The shared pixels of the button idiom: half-block cap rows, the centered filled middle row, and the width formula.
- color
- Small color helpers for deriving state colors from a theme’s base palette.
- crossterm
- Turning on the terminal input modes ratcn’s mouse and paste handling need.
- geometry
- Generic rect geometry components share: border hit-testing, the fixed-height interaction crop, and wrapped-text height. None of it is specific to any one component.
- linear_
nav - The index arithmetic behind moving through an ordered list of things.
- list_
core - The shared substance of components built from an ordered set of value-keyed items: item identity, a uniform-row viewport, and what a pointer gesture over one of those rows asks for.
- runtime
- The interaction runtime: focus, hover, mouse routing, and the typed messages a component sends back to your app.
- selection_
indicator - Shared marker vocabulary for value-keyed selection controls.
- terminal
- Running a ratatui app on a terminal that can be asked about itself.
- text_
width - Cell-width text measurement shared by components and the runtime.
- theme
- The base palette every component derives its colors from, and the one place a declared style override is resolved against it.
- toast
- App-owned toast state:
Toast,ToastEntry, andToasterState— what your app stores for the toast stack. A component reads this state and never mutates it; the app’supdatepersists whatToasterStatereturns. The library never reads a clock — time arrives asDurations the app supplies, so pushing and pruning are driven by whatever timestamp source the app already has.
Structs§
- BarChart
Group - One group of bars for
BarChartWidget::grouped. Mirrors ratatui’sBarGroupbut keeps its bars readable, so widget-level options such asBarChartWidget::show_valuesapply to grouped bars too. - BarChart
Style - Colors for a
BarChartWidget. - BarChart
Widget - A themed adapter over ratatui’s
BarChart, adding grouping and a value display switch. - Button
- A button that can be focused and pressed, declared with
component. - Button
Style - Every color a button can paint, for each interaction state.
- Button
Widget - A button that only draws — an ordinary ratatui
Widgetwith no focus, events, or state. - Checkbox
- A boolean control: marker left, label right, the whole row one hit target.
- Checkbox
Style - A checkbox’s colors.
- Checkbox
Widget - A checkbox that only draws — an ordinary ratatui
Widgetwith no focus, events, or state. One instantiation is one checkbox. - Cycle
- A control that cycles through its options in place: the current value is all it shows, and every click, Enter, Space, Right/l, or Ctrl+N advances to the next, wrapping at the end. Left/h walks backward, as does Ctrl+P. Home and End step nothing: a ring has no ends. Shift belongs to the app, so Shift+Space passes through untouched.
- Cycle
Style - A cycle’s colors, sharing the List’s three background states.
- Cycle
Widget - A cycle that only draws — an ordinary ratatui
Widgetwith no focus, events, or state. It paints the current option acrossarea, the way the closedSelectWidgetpaints its value: the widget takes the one string it shows, and which option that is stays the caller’s business. - Dialog
- A modal dialog: a centered, bordered box with a
titlein its top border, a main content area (adescriptionparagraph, or a customcontentclosure), and a standardactionrow or a customfooter. - Dialog
Style - Every color a dialog can paint.
- List
- A scrollable list of items, declared with
component. - List
Item - One row of a list-shaped component: an identifying
valueand thelabelshown for it. - List
Item State - Everything a custom row-rendering closure —
List::paint_itemorSelect::paint_item— knows about the row it is drawing. - List
Style - Every color a list can paint.
- List
Widget - A list that only draws — an ordinary ratatui
Widgetwith no focus, events, or state. - Progress
Style - Colors for a
ProgressWidget. - Progress
Widget - A progress bar — the fill’s share of the track is the work done.
- Scroll
Area - A vertical viewport that hosts arbitrary interactive ratcn descendants.
- Scroll
Area Style - Every color a
ScrollAreascrollbar paints. - Select
- A one-row select trigger with an option panel declared as a popup layer.
- Select
Style - Every color a select can paint.
- Select
Widget - A select that only draws, with no focus, events, or app state.
- Tabs
- A focusable row of tabs, declared with
component. - Tabs
Style - Every color a tab row can paint.
- Tabs
Widget - A tab row that only draws — an ordinary ratatui
Widgetwith no focus, events, or state. The selected tab is styled as a default button and the rest as secondary buttons. - Theme
- A theme is a small color palette. Every field names a purpose, not a
component: the same
primarypaints a default button, a selected list row, and a bar; the samefieldpaints any inset “well” (lists, panels, bars). - Toast
- One toast’s content and lifetime: a title, an optional description, a
ToastKindaccent, and a duration (orpersistent). - Toast
Entry - A toast in the stack, together with when it was pushed.
- Toaster
State - The stack of toasts currently showing, owned by the app.
- Toaster
Style - Colors for the toast stack: one shared look, plus an accent per
ToastKind. - Toaster
Widget - Draws the newest toasts stacked in a corner. Paint-only by nature: toasts take no focus and handle no events, so there is no interactive half.
- Tooltip
- A wrapper that floats an explanation beside the content it describes.
- Tooltip
Style - Every color a tooltip can paint.
- Tooltip
Widget - A tooltip bubble that only draws, with no focus, events, or app state.
Enums§
- Border
Style - A border line style for component-local styling.
- Button
Size - How tall a button is drawn.
- Button
Variant - The visual weight of a button, in the shadcn sense.
- Tabs
Activation - Whether moving between tabs also switches to them.
- Tabs
Size - How tall the tab row is drawn. Matches
ButtonSize, since a tab is painted as a button. - Toast
Kind - What a toast is telling the user, which picks its accent color and icon.
- Toast
Position - Which corner or edge the toast stack sits against.
- Tooltip
Side - Which side of the trigger the tooltip bubble is placed on.