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.
§Preview status
This is a preview release.
- The API will break. The public surface is still moving: recent work has renamed methods, changed signatures, and removed components. Pin an exact version and expect to edit when you upgrade.
- There is no install command. Each component module is self-contained and meant to be copied into your own project, but copying is a manual file copy today. A CLI is intended and does not exist.
- The component set is small and growing. Eight ship today:
Button,List,Select,Tabs,Dialog,Toaster,BarChartWidget, andTooltip. Text input, a multi-line text area, and a scroll area are planned next; there is no text entry component at all right now.
§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 is the opposite exception: it is an interactive composite
with no separate paint widget.
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.
§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, &state, &theme, |ctx| {
ctx.render_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.
- 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, and a uniform-row viewport.
- runtime
- The interaction runtime: focus, hover, mouse routing, and typed messages for components that would otherwise be paint-only widgets.
- selection_
indicator - Shared marker vocabulary for value-keyed selection controls.
- text_
width - Cell-width text measurement shared by components and the runtime.
- 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
render_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. - 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
render_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::render_itemorSelect::render_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. - 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.
- Tab
- One tab: an identifying
valueand thelabelshown on it. - Tabs
- A focusable row of tabs, declared with
render_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
Render Mode - Whether a button is drawn as a solid block of color or as an outline.
- 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.