Skip to main content

Crate intuituive

Crate intuituive 

Source
Expand description

intuiTUIve: a reactive, retained terminal UI framework built on rs-rich.

You describe the screen once, as a tree of nodes, and keep your state in signals. When a signal changes, the nodes that read it draw again, and only the cells that changed are sent to the terminal. There is no draw loop to write, no layout pass to call, and no “what changed” to track.

use intuituive::prelude::*;

fn main() -> std::io::Result<()> {
    App::new(|| {
        let count = signal(0);
        column([
            text!("[b]Count:[/] {count}").panel("Counter"),
            label("[dim]+ adds one · q quits"),
        ])
        .on_key("+", move |_| count.update(|c| *c += 1))
        .on_key("q", |cx| cx.quit())
    })
    .run()
}
  • Nodes (text, label, renderable, column, row, grid, each, switch, Node::panel) are built once and kept. Any rich renderable is a node: a Table, Markdown, Syntax, a chart.
  • Layout sizes children by Size (fixed, percentage, flexible or their content), within minimums and maximums, with gaps and padding.
  • State is signals and memos. Reading one while drawing subscribes the node; writing one redraws exactly its readers.
  • Input: Node::on_key bindings bubble from the focused node up; Tab moves the focus; Node::on_click gets clicks, routed by the layout the last frame kept.
  • Screens: Ctx::push, Ctx::modal and Ctx::pop keep a stack of screens, each with its own state and focus.
  • Background work: spawn, spawn_future and resource run slow work off the app’s thread and write signals with the result; a Proxy lets any thread do the same.
  • Themes: Theme styles the framework’s parts and names styles for markup; Ctx::set_theme switches at run time.
  • Inline: App::inline runs in a few rows below the prompt instead of the alternate screen.
  • Responsive: Node::hide_below and a stylesheet’s @media rules follow the terminal’s size; viewport reads it.
  • The outside world, behind off-by-default features (plugins: the core stays generic): audio::pcm reads raw PCM a buffer at a time, audio::play plays it at its rate and audio::Capture::simulated stands in for a sound card in tests (pcm), audio::input captures a sound card into a signal (audio, with PulseAudio sources under pulse), audio::wav reads and records WAV files (wav), watch_path wakes the app when files change (watch), image draws an image file in half blocks (art), meter, sparkline, spectrogram and bar_spectrum show levels and spectra (viz), and files has a file manager’s components: Miller columns, file lists, previews, background file jobs and a streaming finder (files).
  • Generic building blocks: miller_of (Miller columns over any MillerSource tree), jobs and jobs_panel (cancellable work with progress), fuzzy_list and finder_of (a fuzzy list over any stream of items), preview_of (a value loaded off the app’s thread for the current key), multi_select (a selection by key with a visual range), list_search (find next and previous over a list), tab_set (tabs’ states with one active), facts (a description list), digits (big digits in any build), and state’s files and FrecencyStore (an app’s state between sessions). The core is generic only; the domain plugins above build on these.
  • Sprites and pixels: sprite::stage draws sprite::Sprites (cell art with transparent cells, animated on the app’s clock) over a backdrop, with clicks, hovers and collisions by their cells; pixels::pixels is an RGBA framebuffer drawn in half blocks, quadrants or Braille, with blits, rectangles and lines. Plugins cut an image into sprites (sprite_sheet, behind art) and make rs-rich-micro’s micro assets sprites (micro_sprite, behind micro).
  • Tests: App::render_with and App::run_on with rich-interact’s headless driver run an app without a terminal; App::driver steps one, and assert_screen! compares its screen with a snapshot (see testing).

The design, and how it compares with ratatui, is in docs/design/intuituive.md.

Re-exports§

pub use app::DEFAULT_MODE;
pub use app::Anchor;
pub use app::App;
pub use app::Ctx;
pub use app::Driver;
pub use app::Easing;
pub use app::FrameStats;
pub use app::Handoff;
pub use app::Holds;
pub use app::ModalOptions;
pub use app::Placement;
pub use app::StatsHandle;
pub use app::Theme;
pub use app::Timer;
pub use app::after;
pub use app::every;
pub use app::fixed_step;
pub use app::held_keys;
pub use app::plain_key;
pub use app::rate;
pub use border::Border;
pub use border::BorderSet;
pub use border::Edge;
pub use border::Shadow;
pub use border::Sides;
pub use command_line::CommandLine;
pub use command_line::CommandLineOptions;
pub use command_line::command_line;
pub use command_line::command_line_with;
pub use complete::CompleteOptions;
pub use complete::Completion;
pub use complete::InputHistory;
pub use complete::complete_words;
pub use complete::history_file;
pub use complete::history_in;
pub use complete::history_session;
pub use finder::FuzzyOptions;
pub use finder::FuzzySource;
pub use finder::finder_of;
pub use finder::fuzzy_list;
pub use finder::fuzzy_list_with;
pub use focus::Direction;
pub use fx::Fx;
pub use jobs::Job;
pub use jobs::JobOutcome;
pub use jobs::JobProgress;
pub use jobs::JobState;
pub use jobs::Jobs;
pub use jobs::JobsPanelOptions;
pub use jobs::ProcessOutcome;
pub use jobs::ProcessOutput;
pub use jobs::jobs;
pub use jobs::jobs_panel;
pub use jobs::jobs_panel_with;
pub use layout::Align;
pub use layout::AlignContent;
pub use layout::Justify;
pub use layout::Size;
pub use layout::StackLayout;
pub use layout::TextAlign;
pub use log::Log;
pub use miller::MillerSource;
pub use miller::miller_in;
pub use miller::miller_of;
pub use node::Node;
pub use node::column;
pub use node::component;
pub use node::each;
pub use node::fit_text;
pub use node::grid;
pub use node::keyed;
pub use node::label;
pub use node::layers;
pub use node::leaf;
pub use node::list;
pub use node::renderable;
pub use node::repeating;
pub use node::row;
pub use node::scroll;
pub use node::scroll_both;
pub use node::scroll_both_with;
pub use node::scroll_with;
pub use node::scroll_x;
pub use node::switch;
pub use node::text;
pub use node::described;
pub use node::drawn;
pub use node::drawn_described;
pub use preview::PreviewOfOptions;
pub use preview::preview_of;
pub use preview::preview_of_with;
pub use reactive::Memo;
pub use reactive::Proxy;
pub use reactive::Signal;
pub use reactive::memo;
pub use reactive::on_write;
pub use reactive::on_write_ref;
pub use reactive::signal;
pub use reactive::viewport;
pub use reactive::watch;
pub use select::ListSearch;
pub use select::Mark;
pub use select::MultiSelect;
pub use select::multi_select;
pub use state::FrecencyStore;
pub use state::frecency_in;
pub use state::frecency_session;
pub use state::frecency_store;
pub use state::read_state;
pub use state::state_dir;
pub use state::state_file;
pub use state::update_file;
pub use state::update_state;
pub use state::write_atomic;
pub use state::write_state;
pub use tab_set::Scoped;
pub use tab_set::TabSet;
pub use tab_set::tab_set;
pub use tab_set::tabs_for;
pub use tab_set::tabs_for_indexed;
pub use task::Latest;
pub use task::Load;
pub use task::Notifier;
pub use task::Resource;
pub use task::Stream;
pub use task::Task;
pub use task::latest;
pub use task::latest_every;
pub use task::resource;
pub use task::resource_for;
pub use task::spawn;
pub use task::spawn_future;
pub use task::spawn_stream;
pub use task::spawn_then;
pub use text_edit::TextSearch;
pub use text_edit::search_bar;
pub use transport::Transport;
pub use transport::transport;
pub use widget::Widget;
pub use widget::widget;
pub use widgets::text_area;
pub use rich;
pub use rich_interact as interact;

Modules§

a11y
Accessibility: what each node is (its Role) and is called, the tree a screen reader bridge or a browser reads (Driver::accessibility), announcements (Announcer), and the text mode an accessible app draws in.
app
The app: the tree, its runtime, and the loop that runs them.
audio
Sound in and out: a sound card captured into a signal, raw PCM and WAV files read a buffer at a time, and what is shown recorded to a WAV. A plugin, not core: the module is there only with the off-by-default pcm feature, which audio and wav turn on.
banner
An animated banner: rs-rich-art’s AnimatedBanner played on the app’s clock.
border
Borders: the kinds of box a panel draws, the sides it draws, titles on its edges and its shadow; and the table of glyphs that join lines where they meet, for collapsed borders.
command_line
A command line: command_line, a bar of one or two rows that opens in a Mode (vim’s : command, a / search, a filter that follows each key), asks yes or no in place (CommandLine::confirm), and says what went wrong in red. Its input completes and keeps a history as any input’s. Generic: taskwarrior-tui’s command bar, Yazi’s and lazygit’s prompts are modes of it.
complete
Help for an input as it is typed: completion (a pop-up list of candidates and the top one’s rest as ghost text), from Node::complete, and a history of what was entered (Up and Down), from Node::history, kept for the session or in the app’s state between sessions. Generic: the candidates are the app’s (a command’s arguments, field names, dates, paths).
dates
Dates as people type them (tomorrow, eow, 3d): rs-rich-interact’s. Dates as people type them: tomorrow, eow, monday, 3d, 2026-11-01, read relative to a given now. Generic: a todo list’s due dates, a log viewer’s since, a calendar’s jump; the names are Taskwarrior’s, which most such tools share.
files
A file manager’s components, behind the off-by-default files feature: what rebuilding Yazi by hand (examples/files.rs) took a few hundred lines each to write. A plugin: each component with a generic shape underneath is that core component with a file system plugged in (miller on miller_of, tasks on jobs, finder on fuzzy_list, preview on preview_of, tabs on tab_set, selection on multi_select, path_crumb on breadcrumb).
finder
A fuzzy list over any source of items, whose results stream in: fuzzy_list, and finder_of on it over an iterator.
focus
Where the focus is, as state, and where it goes next by geometry: the reactive reads focused and has_focus, Direction for Ctx::focus_direction, and the pure neighbour search under it, for widgets that place parts of their own.
fx
Ready-made effects, timed and composable: fades, dissolves, sweeps, slides, colour shifts, a glitch, digital rain, a shimmer over text and a pulsing skeleton for what is loading.
inspect
The inspector: a panel docked on the right of a running app that shows the node tree as it is, which nodes drew in the last frame, where the focus is, what each frame and each node cost, and what happened in the app.
jobs
Cancellable background jobs with progress, and a panel listing them: a task manager for any work (copies, downloads, builds, exports).
layout
Layout: how a container shares its space among its children.
log
A streaming log: lines arrive at the bottom, older ones scroll up.
menu
Menus: a menu_bar, the drop-down menus it opens, and context menus (context_menu) at the mouse pointer. All are built on the public Widget trait and pop-ups.
micro_sprite
rs-rich-micro’s micro assets as sprites: an emoji-sized image or animation, fitted to its cells in half blocks, for a stage. Behind the off-by-default micro feature.
miller
Miller columns over any tree: the level above, the level the cursor is in and a preview of the hovered item, over one cursor.
node
Nodes: the retained tree an app is made of.
pixels
Pixel buffers: an RGBA framebuffer drawn in terminal cells.
prelude
Everything an app usually needs.
preview
A pane that loads a value off the app’s thread for whatever key is current, drops the results for keys left behind, and draws the value.
query
A query language over records: what a filter box, a search line or a command bar reads. parse reads a text by a Grammar (the field names, their aliases and value types, how bare words match) into a Query, and Query::matches tests any Record. Generic: a todo list’s project:home +next due.before:eow, a process list’s cpu > 5 and name:fire, a log’s level:error or "timed out" are each a grammar, not core code.
reactive
Reactive state: signals, memos, and the per-app runtime behind them.
screen
The cell screen: what the terminal shows, kept between frames.
select
Picking items of a list: multi_select, a selection by key with a visual range, and list_search, find next and previous over a list’s items.
source
Rows from a source: TableSource, what a table asks for its rows by index (a file indexed in the background, a database cursor, a log), a source_table over one, and view_of, a source filtered, sorted and searched on a background task, whose result is a source itself.
sprite
Sprites and stages: cell art placed, moved and animated over a backdrop.
sprite_sheet
Sprite sheets: an image cut into tiles, each a PixelSprite (for a PixelBuf) or a half-block Sprite (for a stage). Behind the off-by-default art feature: the image is decoded by rs-rich-art’s image.
state
State an app keeps between sessions, in small files of its own: state_dir (where, by platform), write_state and read_state (written whole and renamed into place, so a crash never leaves half of one), update_state (read, change and write under a lock, so two sessions do not clobber each other), and a FrecencyStore of keys ranked by how often and how lately they were used. Generic: keyed by an app’s name and a file name, holding bytes or strings.
tab_set
A set of tabs’ states with one active: tab_set, the model a tabs strip shows.
task
Background work: run something slow off the app’s thread, and write signals with its result when it is done.
testing
Snapshot assertions for headless app tests.
text_edit
The text editors’ editing options: rebindable keys and presets, a modal preset’s hook, search with a find bar, word wrap, the cursor line, the caret’s shape, tabs, the undo history and an input’s validation message. Builders on Node for a text_area or an input; on any other node they do nothing.
timedemo
A timedemo: replay input against an app as fast as it can draw, and report what each frame cost.
transport
A play head on the app’s clock: transport, what a media player, a slideshow, an animation’s timeline or a replay moves through.
vim
Vim’s keys for the text editors (the off-by-default vim feature): Node::vim gives a text_area or an input rs-rich-interact’s Vim preset, a key mode with normal, insert and visual modes, motions, operators, counts and ..
widget
Widgets: what every node is made of, and how to write your own with the same powers the built-in nodes have.
widgets
Ready-made widgets, built on the public Widget trait as an app’s own would be: a table with a selectable row and sized columns, a virtual_list of any length, a tabs strip, a tree, split panes, a calendar, a time_picker, a datetime_picker, a hex_view and an interactive diff_view.

Macros§

assert_screen
Assert that a screen matches its snapshot: tests/snapshots/<name>.txt under the crate being tested, the screen’s plain rows. The screen is a Driver, a Screen or the rows App::render_with returns. Set UPDATE_SNAPSHOTS=1 to write the file instead. See testing.
assert_screen_styled
assert_screen!, and the screen’s styled rows against tests/snapshots/<name>.styled.txt: each row as console markup, so a change of colour or text style fails too. The screen is a Driver or a Screen.
assert_screen_svg
Assert that a screen’s styled rows match tests/snapshots/<name>.styled.txt, keeping its SVG picture as <name>.svg beside it; on a difference, an HTML report shows both pictures (see check_svg_snapshot). The screen is a Driver or a Screen. Set UPDATE_SNAPSHOTS=1 to write both files.
text
Console markup with format! arguments, read again whenever a signal it names changes: text!("{count} items"). Signals format as their value.

Structs§

Action
An action an action_overlay lists: what identifies it (id), what it says (label), the keys shown as its shortcut, and why it cannot run now, when it cannot.
ConfirmOptions
How a confirm_with dialog looks: its size (50 cells wide and as tall as its content by default) and its title’s style (the theme’s popup.title by default).
EditorTheme
A text area’s look: each part a style, or None for the app theme’s.
Feed
A feed as one value: the newest value a thread sent (value) and how the thread is doing (state), as latest_with_state gives them apart. Copy; made by latest_feed or Play::feed, or from the pair (Feed::from((value, state))). One type for every source an app switches between: a device, a file played, a generator, a socket.
FeedTeller
Tells a feed’s state from its thread, in order with its values: the second argument of a latest_feed thread.
HelpOptions
How Ctx::help_with shows the help: its title, how it says keys (KeyFormat), and whether it lists every binding in one list under its search line (Yazi’s layout) rather than under a heading for each section. Rows too long for the help wrap, whatever the options.
History
Back and forward through the values a signal has had, as a browser’s history, a pager’s or a file manager’s: history makes one. A Copy handle, as a signal is.
Hit
One result of a palette search: what identifies it, what it says, its help line, its shortcut, its score (higher first) and the characters of its label that matched (highlighted), and what choosing it does.
HitSink
Where a Provider sends its hits for one search. It may be cloned and sent to another thread.
ImageView
How a picture is drawn in its cells.
KeyHintsOptions
How key_hints_with shows the keys that work now: each hint’s text, how keys are said, whether bindings that do not apply now are shown greyed, the separator, and a “more” that expands the footer onto as many rows as it needs.
Lockstep
How stepped_worker_with paces its worker.
MarkdownHeading
A heading of the document shown: its level (1 for #), its text, its anchor (#install) and the block it starts.
MarkdownStream
A handle that appends to a markdown node’s document from anywhere: a task, a thread reading an agent’s reply, a Proxy. Clone and Send. Fragments that arrive faster than the app turns its loop are joined, and reach the document together, once a turn: the node then splits again only its last block, and renders and draws only the blocks that changed.
MarkdownViewer
A Markdown viewer: build its node with view, and steer it from anywhere on the app’s thread with go, back and forward. Clone: each clone is the same viewer.
Offset
How a pop-up is moved from its Origin, and its size: x and y cells (negative: left and up), w x h as a modal sizes itself (Size::Fixed cells, a Size::Percent of the screen, Size::Auto the content’s, Size::Flex the whole screen). Whatever the offset, the pop-up stays on the screen: it is moved back inside, and made smaller if it is larger.
PaintStats
What the last Painter::paint did, counted as notcurses counts a render: the cells it looked at and wrote, and its bytes by kind. The bytes add up to the length of what it returned.
Palette
The semantic colours of a theme, as Textual’s themes give them: from these a Theme generates its shades (Theme::from_palette). Only primary is needed; secondary, warning and accent default to it, error and success to secondary, background and surface to Textual’s dark (#121212, #1e1e1e) or light (#efefef, #f5f5f5) ones, and foreground to the background’s inverse.
PathChange
One changed path in a PathEvent.
PathEvent
The changes a watcher gathered over one debounce interval: each path once, in the order it first changed, with what last happened to it (a path created and then written is still Created).
PathWatch
A running watcher (see watch_path). It runs as long as the app; stop ends it sooner. Dropping the handle does not stop it, so an app may ignore the handle. Two handles are equal when they are handles of the same watcher, so they can be kept in a signal.
PickOptions
The size and title style of a pick_with menu.
Picture
A decoded picture, with the id terminal graphics know it by.
Play
An iterator to play onto a signal: latest_iter makes it, and signal, with_state, then or then_with starts it; then_at adds a tail at a pace of its own.
PromptOptions
Where a prompt_with opens, its caret, its title’s style, and what hears each edit and Esc.
RectHandle
Where a node is on the screen, as the last frame laid it out: Node::rect_handle, or RectHandle::new given to a node later with Node::rect_into. Reading it with get subscribes, so what reads it draws again when the node moves. Two handles are equal when they are the same handle, so one can be kept in a signal.
SelectOption
One option of a select_field_keyed or a radio_keyed: what identifies it (key), what it says (label, console markup), and whether it may be chosen. A disabled option is shown, greyed, with its reason, and keys, clicks and typing pass it by.
SheetError
Where a stylesheet stopped parsing, and why.
Steps
The worker’s side of a stepped_worker: wait for ticks, publish output. Dropping it (the worker’s function returning, or panicking) tells the app the worker has ended.
Stylesheet
A parsed stylesheet: give it to App::stylesheet.
SystemCommands
The commands the palette offers on its own, after Textual’s App.get_system_commands: Theme (a picker of Themes), Keys (shows or hides the key_panel, or opens the help when there is none), Maximize or Minimize (the focused pane), Screenshot and Quit. All are on unless turned off:
Themes
The built-in themes (Theme::builtin_names) as a provider: choosing one switches the app to it. The palette’s Theme command opens a palette of it.
TreemapOptions
How a treemap_with lays its tiles out: the smallest tile, a cell’s shape, and the node for the items too small for a tile of their own.
WatchOptions
How watch_path_with watches.

Enums§

ButtonVariant
A button’s look: which theme styles its face comes from.
Caret
Where the caret of an input or a text_area goes when its text is set (when it is made, and when the signal is set from elsewhere): Node::caret. Positions count characters (grapheme clusters), a line break as one.
ChangeKind
What happened to a path.
CheckState
A check box’s state: ticked, clear, or mixed, the state of a parent whose children are partly ticked (a folder of files, a group of permissions). Drawn [x], [ ] and [-]; assistive technology reads checked, not checked and mixed (ARIA’s aria-checked="mixed").
Corner
The corner of the screen toasts stack in, newest nearest the corner: App::toasts_at. Bottom right by default; Yazi’s are top right.
FeedState
How a thread feeding a signal is doing: latest_with_state’s second signal.
ImageAnchor
Which part of the image to retain when using ImageFit::Cover.
ImageFit
How an image fills an explicit rectangle of terminal cells.
ImageMode
Which backend renders the image.
KeyFormat
How the help says a binding’s keys.
Level
How much a notification matters: its title and border are in the theme’s notify.info, notify.warn or notify.error style, else its accent, warn or bad.
Origin
The point of the screen a pop-up is placed from, before its Offset moves it: a corner, the middle of an edge, the centre, or a rectangle (a node’s, from Node::rect_handle), below which it goes, or above when there is no room below. Yazi’s Origin.
Overflow
What a text does with what does not fit a line: rich’s. What to do with text that is wider than the space available. Mirrors rich.console.OverflowMethod.
Pace
How fast latest_iter plays its values.
PaintStrategy
How a Painter turns a frame’s changes into bytes.
Pixels
How a picture reaches the terminal.
PlaceholderVariant
What a placeholder shows.
Solver
How a column or row shares its length among its children: Node::solver.
Status
What a collapsible shows beside its title, at the right of the header: Node::status.

Traits§

Count
How many: a number, or a signal or memo of one that may change.
FeedEnd
What a latest_with_state thread’s end means: () (or Ok(())) is FeedState::Ended, an Err FeedState::Failed with its message.
Provider
Something that offers the command palette Hits: Textual’s Provider. Register one with App::provider, or open a palette of it alone with Ctx::palette_of.
Scaled
A variant of a bind_scaled binding: the modifiers that make its keys, its value, and how the help says it. ("shift", 10.0) is listed shift ×10; ("alt", 0.2, "⅕") is listed alt ⅕.
Stepped
A number bind_step can move: the integer and float types.

Functions§

action_overlay
A palette of the actions actions returns, read again whenever a signal it reads changes, so actions come, go and change their labels while it is open, the focus staying on the same id. Choosing one (Enter or a click) closes it and calls run with the action’s id, after reading actions once more: one gone, or unavailable, by then is not run. Escape closes it. Open it as a modal, as Ctx::action_palette does, so the focus comes back where it was and no key reaches the screen beneath.
bar_spectrum
A bar spectrum of bands, a closure that may read signals: rs-rich-ext’s BarSpectrum, a column per band rising from the bottom, as tall as the node, as cava draws. Feed it rs-rich-ext’s dsp::bands of a spectrum, through a dsp::Falloff for cava’s falling bars, or the core’s smoothed for each. Its accessible name says the bands’ range and the loudest.
bar_spectrum_with
A bar_spectrum whose BarSpectrum configure sets up (.mirror(true), .range(1.0), .bar_width(2), .gap(1), .style("green")). For two channels, make the node with described and BarSpectrum::stereo(left, right).
big_text
Console markup in FIGlet letters (rs-rich-art’s bundled standard font, six rows a line), from a closure that may read signals: a large clock, a level readout, a title. Each run of the markup keeps its style ("[green]12[/]:[red]34"), so letters of different styles do not smush into each other as FIGlet’s do within a run; each line of the markup is a line of letters. What does not fit the width is cut. Its accessible name is the text itself.
button
A button labelled label (console markup) that runs on_press when it is pressed: Enter or Space while it has the focus, or a click (a press let go over it). Its face is the theme’s button style, or another variant’s (Node::variant); it looks picked while it has the focus or the pointer, and pressed ([Save]) while the mouse holds it. Three rows tall, it draws lit top and shaded bottom edges when the theme names their colours. Node::disabled_when greys it out and stops it. Its role is Button, named by its label.
button_of
A button whose label is read from label, a closure that may read signals: Play that becomes Pause, a count, a job’s state. A change draws it again, and lays it out again when its width changes. It stays the same node: it keeps the focus, and a press the mouse holds, while its label changes, and its accessible name follows the label.
checkbox
A check box labelled label (console markup), ticked while on holds: [x] Wrap lines. Space, Enter or a click ticks it or clears it. The tick is drawn in the theme’s checkbox.on style (else accent). Its role is CheckBox, named by its label, with its checked state. checkbox_tri has a third, mixed state, and checkbox_of a label that changes.
checkbox_of
A checkbox_tri whose label is read from label, a closure that may read signals (3 of 5 chosen). The node, its focus and its state stay as the label changes, and its accessible name follows it.
checkbox_tri
A check box of three states, labelled label (console markup), in the state state holds: [x] on, [ ] off, [-] mixed. Space, Enter or a click turn it off from on and on from off; from mixed they turn it on, or what Node::on_mixed says. Only code sets it mixed: a parent that sums up its children. Read as checked, not checked, or mixed.
collapsible
A section titled title (console markup) over child, shown while open holds: a header with a disclosure marker, ▾ Advanced open and ▸ Advanced closed ([-] and [+] in text mode). Enter, Space or a click on the header opens or closes it; → opens it and ← closes it. Closed, the child takes no room and leaves the Tab order. The title is drawn in the theme’s collapsible.title style (else bold), with control.focus over it while the header has the focus. The header’s role is Button, named by the title, with its expanded state. The section is as tall as what it shows. collapsible_with takes a title that follows state; both take a status, a summary and their own markers.
collapsible_with
collapsible, titled by title, read again whenever a signal it reads changes: only the header draws again. With a status at the right of the header and a summary under it while it is closed, it is a tool call’s card: the tool and its argument, running then done or failed, and the output’s last line.
digits
Console markup drawn three rows tall in box-drawing glyphs, from a closure that may read signals: a clock, a counter, a readout, as Textual’s Digits. It has glyphs for 0-9, A-G (and a-g: hex digits, and the notes with #), + - = / % ( ) : . , # and the space; any other character is drawn as it is, on the middle row. Each run of the markup keeps its style ("[green]12[/]:[red]34"); each line of the markup is three rows. It needs no font, so it works in every build, where big_text needs the art feature. A glyph that does not fit the width is left out. Its accessible name is the text.
effect
A node that shows child, then runs f over what it drew: a highlight, a badge, a transition. f gets the canvas, which holds what child drew (read it with Canvas::cell), and the drawing context, as Widget::draw_over does. It runs whenever the effect or anything inside it drew, and always over what child drew afresh, so an effect that moves cells or changes them by what they are comes out the same each time. Signals f reads draw the whole effect again when they change.
facts
A description list: each key (console markup) in a column as wide as the widest (at most half the width), its value (console markup) beside it, wrapped under itself when it is long: a file’s details, a process’s, a record’s fields (Yazi’s spotter, a DataTable of two columns in Textual). The keys are in the theme’s facts.key style (bold), or key_style’s, under their own markup; the values in the theme’s facts.value (none), or value_style’s, a row’s own from value_style_for over it. Sized Size::Auto, it is as tall as its rows and as wide as its widest. Read aloud as key: value pairs.
facts_of
facts from a closure, which may read signals: the details of whatever is hovered.
falling
A number that follows source up at once and falls back with gravity (in units a second, a second): it falls slowly at first and faster and faster, as a level meter’s bar or peak, cava’s bars, or a badge’s glow fading. It never falls below source, and stops once it is there. Steps a frame at a time on the app’s clock while it falls.
fill
A node that fills its area with symbol (one grapheme; a wide one is fine) in style (a style or a theme style’s name): ratatui’s Fill. It takes all the room it is given.
header
The app’s title and the top screen’s subtitle centred on a row, as Textual’s Header: Title — subtitle, in the theme’s header style (reverse by default). clock adds the time (UTC, HH:MM:SS, moving with the app’s clock) on the right; tall makes it three rows tall. A click toggles tall, as Textual’s does.
history
Keep track of the values source takes, so a History can go back to an earlier one and forward again. Each change of source (as a watch sees it: an equal value is no change) after the one shown drops those after it, as a browser does after going back; a history’s own moves are not recorded.
image
The image file at path, drawn in half blocks to fit the node. Shows loading… while the file is decoded, and the error if it cannot be. Call it while the app is built or running.
image_bytes
The image in bytes (a PNG, a JPEG, any format rs-rich-art reads), drawn as view says: a cover embedded in a song, a picture from a network or an archive. Decoded on a worker thread, as a file is.
image_bytes_of
An image node following bytes, a closure that may read signals (the cover of the track playing): a new picture is decoded when it returns other bytes (another Arc), None draws nothing.
image_graphics
The graphics source that draws pictures’ pixels (ImageView::pixels): give it to App::graphics. After each frame’s cells it finds the pictures’ tagged cells (image nodes’, picture_of’s, and any drawn with Picture::lines) and draws them over the cells (Sixel, iTerm2) or uploads them for their placeholders (Kitty), only when one is new, moved, a new frame, or had cells written under it. The Kitty pictures are freed when the app ends.
image_of
An image node following path, a closure that may read signals: when the path it returns changes, the new file is decoded in the background (a result for a path already left behind is dropped). None draws nothing.
image_with
image drawn as view says: another cell mode, a fit, a background.
input
A single-line text box bound to text, both ways: what is typed is set on the signal, and text set on the signal from elsewhere replaces what the box holds. It is a text_area in a single-line mode: the same editing keys, undo, selection and mouse, but Enter submits rather than starting a line (to on_submit, else to the app’s bindings), Tab moves the focus on, pasted line breaks become spaces, and a long line scrolls sideways to the caret.
key_panel
A panel of the keys that work now, one a row (key description), following the focus as key_hints does: each described binding of the focused node and its ancestors, innermost first, those whose condition does not hold greyed. They are grouped by the help’s sections (a binding’s section, else its node’s name) under a heading each, in the theme’s key_panel.section style. Textual’s KeyPanel, for the right of a layout; 30 cells wide unless sized. Ctx::toggle_key_panel hides and shows it.
latest_feed
latest_with_state as one Feed, its thread also given a FeedTeller to say how it is doing before it ends: Ended when a file has run out but silence plays on, Failed on a read error it pads over, Running again once a device is back.
latest_iter
Play values onto a signal on a thread of its own, at pace: a recording played back, a demo’s script, frames of a file. Then something after: another iterator at the same pace (then: a fallback, silence forever) or at its own (then_at), or a closure with the thread’s Latest (then_with); or nothing (signal), and the signal keeps the last value. with_state also says when values ran out.
latest_with_state
latest, with a second signal saying how its thread is doing (FeedState): Running; Stalled once no value has come for stall_after (None: never), until the next comes; Ended when run returns () or Ok(()); Failed when it returns an Err (its message) or panics. Any thread: a sensor, a log’s tail, a socket, a capture. Stalls are timed on the app’s clock.
link
Text that can take the focus and opens url (through the system’s opener, rs-rich-ext’s open::system_opener) on Enter or a click, as Textual’s Link does. It is drawn in the theme’s link style (underlined by default) and is an OSC 8 hyperlink where the terminal has them. on_activate runs an action instead; open_with opens the URL another way (a test’s stub).
list_of
A list of len() rows, each the node build_row(i) makes (any nodes: a card, a row with a label and a button), with the cursor on selected. ↑/↓, Home/End and PgUp/PgDn move it, skipping disabled rows, and keep it in view (scroll_padding rows either side); Enter or a click on a row selects it, unless a node inside the row took the click (a button in a card runs the button). The wheel scrolls the view.
list_of_virtual
A list_of that builds only the rows near the view, on scroll_virtual: for long lists. Row i is estimated height_of(i) lines tall (a separator adds one) until it is drawn and measured. It takes the same keys and options as list_of (item_key, separator_after, item_disabled_when, on_select, on_highlight, on_context, scroll_padding); a row’s state goes with it once it leaves the view, as in scroll_virtual.
list_optional
A list whose selection may be empty, as ratatui’s ListState::select(None): None shows no highlight, ↓ (or Home) then selects the first item and ↑ (or End) the last. Setting the signal to None clears the selection.
markdown
Markdown doc holds (CommonMark, as rs-rich’s Markdown reads it), drawn at the node’s width and as tall as it needs: headings, paragraphs, lists, quotes, tables and fenced code, highlighted. Kept as blocks, so a document that grows at its end (an answer streaming in, through a MarkdownStream) renders and draws again only its last block. A block with links takes the focus: Tab and Shift+Tab go through its links (then on), the focused link is drawn reversed, and Enter or a click on one calls on_link with its URL.
markdown_viewer
A MarkdownViewer whose documents load reads: given a location (a path relative to the documents’ root, as links name them, without its #anchor), the text, or an error, which the viewer shows in place of the document. It runs off the app’s thread. Call it while the app is built or running.
masked_input
A text box that keeps to template, as Textual’s MaskedInput: 9 a digit, 0 an optional digit, A/a a letter (required, optional), N/n a letter or digit, X/x any character, > and < upper or lower case for what follows (! neither), \ a template character as it is, anything else a fixed separator, and ;c at the end the blank shown in an empty slot (a space by default). The caret skips the separators, typing fills the next slot that takes the character, and Backspace clears the slot before it. value holds the text as shown, separators included, up to the last slot typed. The node is in error (:invalid) while some but not all required slots are filled.
meter
A level meter: rs-rich-ext’s LevelMeter showing level with a mark at peak, both closures that may read signals, as a VU or peak meter per channel beside a scope. Across, one row: a bar green to red, the peak’s mark, and with hold a second mark that keeps the highest peak for a while. Its accessible name says the level, the peaks and the band (Level meter L: 0.5, 50 percent, …).
meter_with
A meter whose LevelMeter configure sets up (.label("L"), .readout(true), .scale(MeterScale::Decibels { floor: -60.0 }), .orientation(Orientation::Vertical)): it is given one showing the level and peak, and returns it changed.
number_input
A whole number in range, bound to value both ways. ↑ and ↓ (or + and - after a digit) step it by step, PgUp and PgDn by ten steps, Home and End go to the ends, and the wheel steps it. Digits type a new number (a - first for a negative one), Backspace takes one off, and Esc puts back the value. What is typed is set on the signal at once, kept in the range, and shown as typed until Enter or the focus leaves, when it shows the value.
persistence
A trace’s last frames drawn together, each older one fainter, as an analogue scope’s phosphor (xoscope’s persistence): rs-rich-ext’s Persistence on a Chart, from frames, a closure that may read signals and returns the frames, oldest first, each a trace’s points. Keep them with the core’s recent (recent(trace, 8)) or recent_of. The axes span the points (-1 to 1 up the side while they are all within it); the newest frame is drawn on top.
persistence_with
A persistence whose Persistence trace sets up (.style(..), .fade(..), .background(..), .marker(..)) and whose Chart chart sets up (its axes’ bounds and labels, a legend), each given one made from the frames.
picture_bytes
The bytes the terminal has been sent to draw picture id (every frame of it) so far, as the painter counted them (count_picture_bytes): what a moving picture costs. A picture drawn in cells costs its cells, which the painter sends as text, and counts nothing here.
picture_of
A node showing the picture picture returns, a closure that may read signals: each new frame (a video’s, an emulator’s screen) is drawn at once, with no worker and no “loading” step, and a frame of the same picture (Picture::next) keeps its id, so Kitty replaces the image it holds. None draws nothing.
placeholder
A block for laying out a prototype, as Textual’s Placeholder: filled with the next of twelve colours (in the order placeholders are built on a thread), its label centred on it. placeholder_variant shows its size or a paragraph instead, and a click moves to the next variant.
progress_bar
A bar of done out of total, as Textual’s ProgressBar: with show_percentage its share (50%), with show_eta the time left at the rate so far (counted from when it first drew, on the app’s clock). With no total it pulses, a block sweeping across on the app’s clock. The bar is in the theme’s progress.bar style (magenta by default) over progress.track (grey23). One row tall.
radio
A group of radio buttons, one per row: options (console markup; may read signals), with the one selected holds chosen: (•) Medium. ↑/↓ (or ←/→) move the choice within the group, Home and End go to the first and the last, and a click chooses an option. The dot is drawn in the theme’s radio.on style (else accent). Its role is RadioGroup: name it with label; its value is the chosen option, checked, and its place among them. radio_keyed knows its options by key, and may disable some.
radio_keyed
A radio group whose options are known by key, as select_field_keyed’s are: selected holds the chosen option’s key (None: no dot), which stays with its option through a reorder and becomes None when that option goes. A disabled option is drawn greyed with its reason; the arrows, Home, End, a click and typing pass it by. Typing letters jumps to the option they start, as in a select field.
recent
The last n values written to source, oldest first: each write, an equal value too, as on_write sees them. An average over the last frames, a meter’s peak, an oscilloscope’s persistence. n may be a signal (or memo): when it shrinks, the oldest go.
recent_of
recent, keeping keep of each value written rather than a clone of it: a part (a channel of a frame, its peak), a summary, or a shared handle. Nothing else of the value is copied.
recent_timed
recent, each value with the time it was written on the app’s clock (Ctx::now gives the time to measure its age against): a trace whose old frames fade by age, toasts or log lines that go after a while.
rule
A line across, one row tall, in the theme’s border style: a divider between the parts of a column. Directly inside a panel whose sides it reaches, it is drawn in the panel’s border style and kind and joins its sides: ├────┤. Padding between the two (the panel’s, or a padding round the column) does not keep it from them: the rule runs across it. Node::border picks the kind of a rule on its own (Border::Rounded’s light line by default).
scroll_virtual
A scroll view over len() items that builds only those in view, plus a screen either side: item i is row(i), estimated height_of(i) rows tall (at least one) until it is drawn and measured (its fixed size, else its content’s height). A million items cost a screenful. Items are kept while they stay near the view, and dropped (their state with them) once they leave it.
scroll_virtual_with
scroll_virtual, with the first row in view in offset: read it, or set it to scroll from code (u64::MAX for the end).
select_field
A drop-down field showing the option selected holds, of options (console markup; may read signals): Dark ▾. Enter, Space or a click opens a menu of the options under it, the chosen one selected: ↑/↓ move, Enter or a click chooses, Esc closes it. Closed, ↑/↓ choose the option before or after; in either, typing letters jumps to the option they start. The field is drawn in the theme’s select style, with control.focus over it while it has the focus; the menu is a menu’s. Its role is ComboBox: name it with label; its value is the choice, with its place among the options and whether the menu is open. select_field_keyed knows its options by key, and may disable some.
select_field_keyed
A select_field whose options are known by key: selected holds the chosen option’s key, so the choice stays with its option when the options are reordered or relabelled. When the chosen key goes from the options, selected becomes None and the field shows (none). A disabled option (SelectOption::disabled) is shown greyed in the menu with its reason, and cannot be chosen by the arrows, a click or typing.
slider
A slider over range, holding value, moved in steps of step (none when 0): a track with a knob, filled up to it. ←/↓ and →/↑ move it a step, PageDown and PageUp a tenth of the range, Home and End to its ends; a click puts the knob under the pointer, and a drag moves it, as does the wheel. The filled side is in the theme’s slider.filled style (else accent), the rest in slider.track (else muted), the knob in slider.knob; with the focus the knob takes control.focus. Its role is Slider: name it with label; its value is the number. Twenty cells wide unless sized.
smoothed
A number that follows source on the app’s clock, rising with the time constant attack and falling with release (an exponential ease: after one time constant it has come about 63% of the way). A level meter’s needle, a progress bar that glides, a spectrum’s bars falling back slowly. A zero time constant follows at once. It moves a frame at a time while it eases, and stops once it is there.
sparkline
A sparkline of values, a closure that may read signals: rs-rich-ext’s Sparkline (ratatui’s and Textual’s), one cell a value, as tall as the node. Given more values than cells it resamples them, so keep as many as should show (a history the width of the node, the newest last). Its accessible name says the values’ range and trend.
sparkline_with
A sparkline whose Sparkline configure sets up (.range(0.0, 1.0), .style("green"), .min_max(true)).
spectrogram
A spectrogram (a waterfall) of spectra, a closure that may read signals and returns the spectra to show, oldest first, each the levels of its bins, lowest frequency first: rs-rich-ext’s Spectrogram, the newest at the top, as tall as the node. Keep the history with the core’s recent (recent(spectrum, rows) keeps the newest rows, oldest first). Its accessible name says the spectra’s range and where the newest peaks.
spectrogram_with
A spectrogram whose Spectrogram configure sets up (.resolution(rate / n), .scale(AxisScale::Log), .bounds(20.0, 20_000.0), .range(-60.0, 0.0), .charset(..)): it is given one holding the spectra, and returns it changed.
spinner
A spinner: rich’s spinner name (dots, line, arc, bouncingBar, … as rich::spinner::spinner_names lists them; dots for a name it does not know), turning at its own speed on the app’s clock, in the theme’s spinner style (else accent). It draws again only when its next frame is due, and only while it spins: always, or while spinning_when’s condition holds (blank otherwise). Its glyph is decoration, hidden from assistive technology: say what is happening in text beside it, or mark what is loading busy. In text mode it is a still *.
stepped_worker
Run work on a thread of its own, stepped by the app’s clock: each period the app hands it the ticks due (at most max_catch_up after a stall), waits up to 250 ms for it to finish them, and shows its newest output in the signal returned (None until the first). work calls Steps::wait for ticks and Steps::publish for output, and returns when wait gives None (the app has gone). A worker that returns early makes each step return at once. stepped_worker_with takes the timeout too.
stepped_worker_with
stepped_worker paced as pace says, with its timer: to pause or cancel the stepping.
switch_of
A switch_toggle whose label is read from label, a closure that may read signals.
switch_toggle
A switch labelled label (console markup), on while on holds: a knob at the right of its track when on (──●), at the left when off (●──), the track in the theme’s switch.on style (else good) or switch.off (else muted). Space, Enter or a click flips it; → turns it on and ← off. Its role is Switch, named by its label, with its checked state.
tiles_of
A node over a whole frame cut into tiles (#818): tiles returns them (a closure that may read signals; rich_art::tiles::Tiler::tiles), each drawn at its place in cells through pixels. A tile that did not change keeps its picture’s id and version, so with image_graphics only the tiles that changed reach the terminal again (Kitty’s uploads for its placeholders). A tile that does not fit the node is left out.
treemap
A treemap of items, each (key, weight): a tile per item, its area in proportion to its weight, as near square as the weights let it be, each holding the node build makes for its key and its rectangle (on the screen). items may read signals: the map lays out again when they change. A tile keeps its node while its key and size stay; a new key, or a tile of another size, is built afresh (what it shows may depend on its room), and the old one is dropped. What takes the room: a disk by folder, memory by process, a bundle by module. Tiles that take the focus move between each other by direction with bind_directional_focus.
treemap_with
treemap laid out as options say: the smallest tile, a cell’s shape, and a tile for the rest.
tuner
A tuner’s readout of reading, a closure that may read signals (an rs-rich-ext Tuner’s reading of the newest spectrum): the note in big glyphs (digits), a needle from flat to sharp across the width, its centre the note, and the frequency and cents. Green within 5 cents, yellow within 15, red beyond; -- and no signal with no reading.
watch_path
Watch path (a file, or a directory and its direct entries) and call on_change on the app’s thread when it changes, with the changes gathered over 100 ms. Call it while the app is built or running. See watch_path_with for a recursive watch and another interval.
watch_path_with
watch_path with WatchOptions: recursive, or another debounce interval.

Type Aliases§

ModePage
One mode of Node::modes: its value, its help section, and what binds its keys.