Skip to main content

Crate ratcn

Crate ratcn 

Source
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.

§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 init

init 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:

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:

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, and ToasterState — what your app stores for the toast stack. A component reads this state and never mutates it; the app’s update persists what ToasterState returns. The library never reads a clock — time arrives as Durations the app supplies, so pushing and pruning are driven by whatever timestamp source the app already has.

Structs§

BarChartGroup
One group of bars for BarChartWidget::grouped. Mirrors ratatui’s BarGroup but keeps its bars readable, so widget-level options such as BarChartWidget::show_values apply to grouped bars too.
BarChartStyle
Colors for a BarChartWidget.
BarChartWidget
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.
ButtonStyle
Every color a button can paint, for each interaction state.
ButtonWidget
A button that only draws — an ordinary ratatui Widget with no focus, events, or state.
Checkbox
A boolean control: marker left, label right, the whole row one hit target.
CheckboxStyle
A checkbox’s colors.
CheckboxWidget
A checkbox that only draws — an ordinary ratatui Widget with 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.
CycleStyle
A cycle’s colors, sharing the List’s three background states.
CycleWidget
A cycle that only draws — an ordinary ratatui Widget with no focus, events, or state. It paints the current option across area, the way the closed SelectWidget paints 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 title in its top border, a main content area (a description paragraph, or a custom content closure), and a standard action row or a custom footer.
DialogStyle
Every color a dialog can paint.
List
A scrollable list of items, declared with component.
ListItem
One row of a list-shaped component: an identifying value and the label shown for it.
ListItemState
Everything a custom row-rendering closure — List::paint_item or Select::paint_item — knows about the row it is drawing.
ListStyle
Every color a list can paint.
ListWidget
A list that only draws — an ordinary ratatui Widget with no focus, events, or state.
ProgressStyle
Colors for a ProgressWidget.
ProgressWidget
A progress bar — the fill’s share of the track is the work done.
ScrollArea
A vertical viewport that hosts arbitrary interactive ratcn descendants.
ScrollAreaStyle
Every color a ScrollArea scrollbar paints.
Select
A one-row select trigger with an option panel declared as a popup layer.
SelectStyle
Every color a select can paint.
SelectWidget
A select that only draws, with no focus, events, or app state.
Tabs
A focusable row of tabs, declared with component.
TabsStyle
Every color a tab row can paint.
TabsWidget
A tab row that only draws — an ordinary ratatui Widget with 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 primary paints a default button, a selected list row, and a bar; the same field paints any inset “well” (lists, panels, bars).
Toast
One toast’s content and lifetime: a title, an optional description, a ToastKind accent, and a duration (or persistent).
ToastEntry
A toast in the stack, together with when it was pushed.
ToasterState
The stack of toasts currently showing, owned by the app.
ToasterStyle
Colors for the toast stack: one shared look, plus an accent per ToastKind.
ToasterWidget
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.
TooltipStyle
Every color a tooltip can paint.
TooltipWidget
A tooltip bubble that only draws, with no focus, events, or app state.

Enums§

BorderStyle
A border line style for component-local styling.
ButtonSize
How tall a button is drawn.
ButtonVariant
The visual weight of a button, in the shadcn sense.
TabsActivation
Whether moving between tabs also switches to them.
TabsSize
How tall the tab row is drawn. Matches ButtonSize, since a tab is painted as a button.
ToastKind
What a toast is telling the user, which picks its accent color and icon.
ToastPosition
Which corner or edge the toast stack sits against.
TooltipSide
Which side of the trigger the tooltip bubble is placed on.

Type Aliases§

Tab
One tab: an identifying value and the label shown on it — the same ListItem a list row is, under the name this component uses for it.