Skip to main content

Rendered

Struct Rendered 

Source
pub struct Rendered {
    pub lines: Vec<Line>,
}
Expand description

A fully laid-out block of styled lines, ready to print or compose.

Fields§

§lines: Vec<Line>

The styled lines, top to bottom.

Implementations§

Source§

impl Rendered

Source

pub fn new(lines: Vec<Line>) -> Self

Creates a rendered block from lines.

Examples found in repository?
examples/output-readme.rs (line 25)
24fn blank() -> Rendered {
25    Rendered::new(vec![Line::default()])
26}
27
28/// The top hero panel with styled, centered text.
29fn hero() -> Result<()> {
30    let cyan = Style::new().fg(Color::Cyan).bold();
31    let on_magenta = Style::new().fg(Color::Black).bg(Color::Magenta).bold();
32    let yellow = Style::new().fg(Color::Yellow).bold();
33    let body = Text::new(vec![
34        Line::new(vec![
35            Span::raw("A native Rust library for "),
36            Span::styled("styled terminal output", cyan),
37            Span::raw(" and "),
38            Span::styled("input", on_magenta),
39            Span::raw(" - panels, tables, trees, lists,"),
40        ]),
41        Line::new(vec![
42            Span::raw("key-value pairs, badges, rules, "),
43            Span::styled("columns", cyan),
44            Span::raw(" and progress bars, with Rich-compatible "),
45            Span::styled("[markup]", yellow),
46            Span::raw("."),
47        ]),
48    ]);
49    Panel::new(body)
50        .border(BorderType::Rounded)
51        .border_style(Style::new().fg(Color::Cyan))
52        .title(
53            Title::new(" sparcli ")
54                .align(Align::Center)
55                .style(Style::new().fg(Color::Cyan).bold()),
56        )
57        .content_align(Align::Center)
58        .width(96)
59        .print()
60}
61
62/// The three-column dashboard row.
63fn dashboard() -> Result<()> {
64    Columns::new()
65        .add_rendered(overview_table())
66        .add_rendered(middle_column())
67        .add_rendered(right_column())
68        .gap(3)
69        .separator(BorderType::Single)
70        .print()
71}
72
73/// Left column: the striped, row-separated "Overview" table.
74fn overview_table() -> Rendered {
75    let status = |text: &str, color: Color| {
76        Cell::new(Text::styled(text, Style::new().fg(color).bold()))
77    };
78    Table::new()
79        .title("Overview")
80        .title_style(Style::new().fg(Color::Magenta).bold())
81        .border(BorderType::Rounded)
82        .border_style(Style::new().fg(Color::Magenta))
83        .header_style(Style::new().fg(Color::Cyan).bold())
84        .striped(true)
85        .stripe_style(Style::new().bg(Color::Rgb(40, 40, 60)))
86        .row_separators(true)
87        .columns([
88            Column::new("Service"),
89            Column::new("Status").align(Align::Center),
90            Column::new("Uptime").align(Align::Right),
91        ])
92        .row([
93            Cell::new("api-gateway"),
94            status("● OK", Color::Green),
95            Cell::new("99.98%"),
96        ])
97        .row([
98            Cell::new("auth"),
99            status("● OK", Color::Green),
100            Cell::new("99.91%"),
101        ])
102        .row([
103            Cell::new("billing"),
104            status("● WARN", Color::Yellow),
105            Cell::new("97.40%"),
106        ])
107        .row([
108            Cell::new("scheduler"),
109            status("● FAIL", Color::Red),
110            Cell::new("82.10%"),
111        ])
112        .render(40)
113}
114
115/// Middle column: a numbered list above a tree beside titled rules.
116fn middle_column() -> Rendered {
117    let list = List::ordered(Marker::Number)
118        .marker_style(Style::new().fg(Color::Yellow).bold())
119        .item("Compose widgets side by side")
120        .item("Capture, pad, and align them")
121        .item("Style with Rich-style markup")
122        .item("Render to any UTF-8 terminal")
123        .render(36);
124
125    let tree = Tree::new()
126        .dashes(2)
127        .connector_style(Style::new().fg(Color::Cyan))
128        .node(
129            TreeNode::new("project/")
130                .child(
131                    TreeNode::new("api/")
132                        .child(TreeNode::new("routes.c"))
133                        .child(TreeNode::new("auth.c")),
134                )
135                .child(TreeNode::new("worker.c"))
136                .child(TreeNode::new("store.c")),
137        )
138        .render(18);
139
140    let rules = vstack(
141        &[
142            rule("Pipeline", BorderType::Single, Color::Magenta),
143            blank(),
144            rule("Workers", BorderType::Double, Color::Green),
145            blank(),
146            rule("Storage", BorderType::Thick, Color::Yellow),
147            blank(),
148        ],
149        0,
150    );
151
152    let tree_and_rules = Columns::new()
153        .add_rendered(tree)
154        .add_rendered(rules)
155        .gap(2)
156        .render(40);
157
158    vstack(&[list, blank(), tree_and_rules], 0)
159}
160
161/// One titled rule with a colored title and line.
162fn rule(title: &str, border: BorderType, color: Color) -> Rendered {
163    Rule::with_title(Text::styled(title, Style::new().fg(color).bold()))
164        .border(border)
165        .style(Style::new().fg(color))
166        .width(20)
167        .render(20)
168}
169
170/// Right column: key-value pairs, badges and a staggered columns demo.
171fn right_column() -> Rendered {
172    let kv = KeyValue::new()
173        .key_style(Style::new().fg(Color::Cyan).bold())
174        .value_style(Style::new())
175        .add("host", "localhost")
176        .add("port", "8080")
177        .add("scheme", "https")
178        .add("timeout", "30s")
179        .render(24);
180
181    let badge = |text: &str, color: Color| {
182        Badge::new(text)
183            .pad(1)
184            .style(Style::new().fg(Color::Black).bg(color).bold())
185            .span()
186    };
187    let badges = Rendered::new(vec![
188        Line::new(vec![
189            badge("DONE", Color::Green),
190            Span::raw(" "),
191            badge("INFO", Color::LightBlue),
192        ]),
193        Line::default(),
194        Line::new(vec![
195            badge("WARN", Color::Yellow),
196            Span::raw(" "),
197            badge("FAIL", Color::Red),
198        ]),
199    ]);
200
201    let label = |text: &str| {
202        Rendered::new(vec![Line::styled(
203            text,
204            Style::new().fg(Color::Cyan).bold(),
205        )])
206    };
207    let staggered = Columns::new()
208        .add_rendered(label("Col 1"))
209        .add_rendered(vstack(&[blank(), label("Col 2")], 0))
210        .add_rendered(vstack(&[blank(), blank(), label("Col 3")], 0))
211        .gap(2)
212        .separator(BorderType::Single)
213        .render(40);
214
215    vstack(&[kv, blank(), badges, blank(), staggered], 0)
216}
More examples
Hide additional examples
examples/prompt-readme.rs (line 29)
28fn blank() -> Rendered {
29    Rendered::new(vec![Line::default()])
30}
31
32/// The top hero panel, sized to match the dashboard width.
33fn hero(width: u16) -> Result<()> {
34    let theme = theme();
35    let body = Text::new(vec![
36        Line::raw("Interactive prompts - confirm · select · text · password ·"),
37        Line::raw("number · textarea · fuzzy · date."),
38    ]);
39    Panel::new(body)
40        .border(BorderType::Rounded)
41        .border_style(Style::new().fg(theme.accent))
42        .title(
43            Title::new(" sparcli · input widgets ")
44                .align(Align::Center)
45                .style(theme.title),
46        )
47        .content_align(Align::Center)
48        .width(width)
49        .print()
50}
51
52/// The balanced three-column dashboard of prompt frames.
53fn dashboard() -> Rendered {
54    Columns::new()
55        .add_rendered(left_column())
56        .add_rendered(middle_column())
57        .add_rendered(calendar_column())
58        .gap(3)
59        .separator(BorderType::Single)
60        .render(0)
61}
62
63/// Left column: the confirm prompt with the input fields stacked beneath it,
64/// then the notes textarea.
65fn left_column() -> Rendered {
66    vstack(
67        &[
68            Confirm::new("Deploy to production?").default_yes().frame(),
69            blank(),
70            TextInput::new("Service").initial("api-gateway").frame(),
71            PasswordInput::new("Password").initial("hunter2").frame(),
72            NumberInput::new("Replicas")
73                .initial(3.0)
74                .range(1.0, 10.0)
75                .frame(),
76            TextInput::new("Email")
77                .placeholder("you@example.com")
78                .frame(),
79            TextInput::new("Region")
80                .initial("eu-")
81                .suggestions(["eu-central-1"])
82                .frame(),
83            blank(),
84            Textarea::new("Notes")
85                .initial("first line\nsecond line\nthird line")
86                .frame(),
87        ],
88        0,
89    )
90}
91
92/// Middle column: the single- and multi-select prompts.
93fn middle_column() -> Rendered {
94    vstack(
95        &[
96            Select::new("Environment")
97                .options(["staging", "production", "local"])
98                .cursor(1)
99                .frame(),
100            blank(),
101            Select::new("Targets")
102                .multi()
103                .options(["web", "api", "worker", "db"])
104                .checked([0, 2])
105                .frame(),
106        ],
107        0,
108    )
109}
110
111/// Right column: the date picker with the fuzzy finder beneath it.
112fn calendar_column() -> Rendered {
113    let date = DatePicker::new("Release date")
114        .initial(Date::new(2026, 5, 15))
115        .frame();
116    vstack(&[date, blank(), fuzzy_block()], 0)
117}
118
119/// A composed snapshot of an inline fuzzy finder (no `fuzzy` feature needed
120/// for this static screenshot).
121fn fuzzy_block() -> Rendered {
122    let theme = theme();
123    Rendered::new(vec![
124        Line::new(vec![
125            Span::styled("Language ".to_string(), theme.title),
126            Span::raw("ru"),
127            Span::styled(" ".to_string(), theme.cursor),
128        ]),
129        Line::new(vec![
130            Span::styled("‣ ".to_string(), theme.selection),
131            Span::styled("Rust".to_string(), theme.selection),
132        ]),
133    ])
134}
examples/output_dynamic.rs (line 77)
72fn pager() -> Result<()> {
73    use sparcli::Pager;
74    let lines = (1..=200)
75        .map(|n| Line::raw(format!("line {n}")))
76        .collect::<Vec<_>>();
77    Pager::new().page(&Rendered::new(lines))
78}
examples/prompts.rs (line 119)
114fn footer_hint() -> sparcli::Result<()> {
115    let shortcuts = [
116        Shortcut::new(KeyPress::new(KeyCode::Enter), 1, "submit"),
117        Shortcut::new(KeyPress::new(KeyCode::Esc), 2, "cancel"),
118    ];
119    Rendered::new(vec![shortcut::hint_line(&shortcuts)]).print()
120}
examples/output_gallery.rs (lines 44-52)
41fn styled_text() -> Result<()> {
42    Rule::with_title("sparcli output gallery").print()?;
43    println!();
44    Rendered::new(vec![Line::new(vec![
45        Span::styled("bold ", Style::new().bold()),
46        Span::styled("dim ", Style::new().dim()),
47        Span::styled("italic ", Style::new().italic()),
48        Span::styled("red ", Style::new().fg(Color::Red)),
49        Span::styled("on-blue ", Style::new().bg(Color::Blue)),
50        Span::raw("link: "),
51        Span::raw("sparcli").link("https://example.com/sparcli"),
52    ])])
53    .print()?;
54
55    #[cfg(feature = "markup")]
56    markup_println("[bold green]markup[/]: [#ff8800]orange[/] and `code`")?;
57    Ok(())
58}
59
60/// Rules in all three alignments.
61fn sections() -> Result<()> {
62    section("Rules")?;
63    Rule::with_title("left")
64        .align(Align::Left)
65        .border(BorderType::Single)
66        .print()?;
67    Rule::with_title("center").align(Align::Center).print()?;
68    Rule::with_title("right")
69        .align(Align::Right)
70        .border(BorderType::Thick)
71        .print()?;
72    Ok(())
73}
74
75/// All five alert kinds.
76fn alerts() -> Result<()> {
77    section("Alerts")?;
78    Alert::info("Informational message.").print()?;
79    Alert::debug("Diagnostic detail.").print()?;
80    Alert::success("Everything compiled.").print()?;
81    Alert::warning("Low on disk space.").print()?;
82    Alert::error("Connection refused.").print()?;
83    Ok(())
84}
85
86/// Panels: borders, title/subtitle, fill and centered content.
87fn panels() -> Result<()> {
88    section("Panels")?;
89    Panel::new("Rounded border (default).").print()?;
90    Panel::new("Double border.")
91        .border(BorderType::Double)
92        .print()?;
93    Panel::new("Centered, filled, fixed width.")
94        .border(BorderType::Thick)
95        .title(Title::new("Title"))
96        .subtitle(Title::new("subtitle"))
97        .content_align(Align::Center)
98        .fill(Style::new().bg(Color::Indexed(236)))
99        .width(40)
100        .print()?;
101    Ok(())
102}
103
104/// Tables: colspan + striping, and footer + a wrapping column.
105fn tables() -> Result<()> {
106    section("Tables")?;
107    Table::new()
108        .title("Servers")
109        .columns(["Name", "Region", "Status"])
110        .row(["web-1", "eu-west", "online"])
111        .row(["db-1", "eu-west", "online"])
112        .row([Cell::new("maintenance window")
113            .colspan(3)
114            .align(Align::Center)])
115        .striped(true)
116        .print()?;
117    println!();
118    Table::new()
119        .columns([
120            Column::new("Item").align(Align::Left),
121            Column::new("Note").wrap().max_width(24),
122        ])
123        .row([
124            "alpha",
125            "a long note that wraps across several lines nicely",
126        ])
127        .row(["beta", "short note"])
128        .footer_row([Cell::new("2 items").colspan(2).align(Align::Right)])
129        .border(BorderType::Ascii)
130        .print()?;
131    Ok(())
132}
133
134/// Lists in several marker styles, plus a tree.
135fn lists_and_trees() -> Result<()> {
136    section("Lists & trees")?;
137    List::ordered(Marker::Number)
138        .item("First step")
139        .item_with("Second step", List::new().item("detail a").item("detail b"))
140        .item("Third step")
141        .print()?;
142    println!();
143    List::ordered(Marker::AlphaLower)
144        .item("alpha")
145        .item("beta")
146        .print()?;
147    List::ordered(Marker::RomanUpper)
148        .item("one")
149        .item("two")
150        .print()?;
151    println!();
152    Tree::new()
153        .node(
154            TreeNode::new("project")
155                .child(TreeNode::new("src").child(TreeNode::new("main.rs")))
156                .child(TreeNode::new("Cargo.toml")),
157        )
158        .print()?;
159    Ok(())
160}
161
162/// Key-value list and inline badges.
163fn key_values_and_badges() -> Result<()> {
164    section("Key-value & badges")?;
165    KeyValue::new()
166        .add("Version", "0.1.0")
167        .add("License", "MIT")
168        .print()?;
169    println!();
170    Rendered::new(vec![Line::new(vec![
171        Badge::new("PASS")
172            .style(Style::new().fg(Color::Green).bold())
173            .span(),
174        Span::raw(" "),
175        Badge::new("WARN")
176            .style(Style::new().fg(Color::Yellow).bold())
177            .span(),
178        Span::raw(" "),
179        Badge::new("v0.1").caps("(", ")").span(),
180    ])])
181    .print()?;
182    Ok(())
183}
184
185/// Progress bars in every style, a threshold bar, and a static spinner frame.
186fn progress_and_spinner() -> Result<()> {
187    section("Progress & spinner")?;
188    let styles = [
189        ("block", ProgressStyle::Block),
190        ("ascii", ProgressStyle::Ascii),
191        ("line", ProgressStyle::Line),
192        ("shaded", ProgressStyle::Shaded),
193    ];
194    for (label, style) in styles {
195        ProgressBar::new()
196            .style(style)
197            .label(label)
198            .width(20)
199            .bar(6.0, 10.0)
200            .print()?;
201    }
202    let thresholds = Thresholds {
203        mid: 0.5,
204        high: 0.8,
205        low_color: Color::Green,
206        mid_color: Color::Yellow,
207        high_color: Color::Red,
208    };
209    ProgressBar::new()
210        .thresholds(thresholds)
211        .label("load")
212        .width(20)
213        .bar(9.0, 10.0)
214        .print()?;
215    Spinner::new("spinner (static frame)").frame().print()?;
216    Ok(())
217}
218
219/// A colored diff and a two-column layout.
220fn diff_and_columns() -> Result<()> {
221    section("Diff & columns")?;
222    Diff::new(
223        "line one\nline two\nline three",
224        "line one\nline 2\nline three",
225    )
226    .print()?;
227    println!();
228    let left = Panel::new("left").render(20);
229    let right = Panel::new("right").render(20);
230    Columns::new()
231        .add_rendered(left)
232        .add_rendered(right)
233        .gap(3)
234        .separator(BorderType::Single)
235        .print()?;
236    Ok(())
237}
238
239/// Composition helpers: align, pad and vstack.
240fn composition() -> Result<()> {
241    section("Composition (align / pad / vstack)")?;
242    let block = Rendered::new(vec![Line::raw("aligned right")]);
243    align(&block, 30, Align::Right).print()?;
244    println!();
245    pad(&Rendered::new(vec![Line::raw("padded")]), Edges::all(1)).print()?;
246    println!();
247    let a = Rendered::new(vec![Line::raw("first block")]);
248    let b = Rendered::new(vec![Line::raw("second block")]);
249    vstack(&[a, b], 1).print()?;
250    Ok(())
251}
Source

pub fn empty() -> Self

Creates an empty rendered block.

Source

pub fn push(&mut self, line: impl Into<Line>)

Appends a line.

Source

pub fn width(&self) -> usize

Returns the width of the widest line in columns.

Examples found in repository?
examples/prompt-readme.rs (line 21)
18fn main() -> Result<()> {
19    println!();
20    let board = dashboard();
21    hero(board.width() as u16)?;
22    println!();
23    board.print()?;
24    Ok(())
25}
Source

pub fn height(&self) -> usize

Returns the number of lines.

Source

pub fn plain(&self) -> String

Returns the plain text (no styles), lines joined by \n.

Trait Implementations§

Source§

impl Clone for Rendered

Source§

fn clone(&self) -> Rendered

Returns a duplicate of the value. Read more
1.0.0 (const: unstable) · Source§

fn clone_from(&mut self, source: &Self)

Performs copy-assignment from source. Read more
Source§

impl Debug for Rendered

Source§

fn fmt(&self, f: &mut Formatter<'_>) -> Result

Formats the value using the given formatter. Read more
Source§

impl Default for Rendered

Source§

fn default() -> Rendered

Returns the “default value” for a type. Read more
Source§

impl Eq for Rendered

Source§

impl PartialEq for Rendered

Source§

fn eq(&self, other: &Rendered) -> bool

Equality operator ==. Read more
1.0.0 (const: unstable) · Source§

fn ne(&self, other: &Rhs) -> bool

Inequality operator !=. Read more
Source§

impl Renderable for Rendered

Source§

fn render(&self, _max_width: u16) -> Rendered

Lays the value out for a terminal at most max_width columns wide.
Source§

fn print(&self) -> Result<()>

Renders at the current terminal width and prints to standard output. Read more
Source§

fn print_to<W: Write>(&self, writer: &mut W) -> Result<()>

Renders at the current terminal width and writes to writer. Read more
Source§

impl StructuralPartialEq for Rendered

Auto Trait Implementations§

Blanket Implementations§

Source§

impl<T> Any for T
where T: 'static + ?Sized,

Source§

fn type_id(&self) -> TypeId

Gets the TypeId of self. Read more
Source§

impl<T> Borrow<T> for T
where T: ?Sized,

Source§

fn borrow(&self) -> &T

Immutably borrows from an owned value. Read more
Source§

impl<T> BorrowMut<T> for T
where T: ?Sized,

Source§

fn borrow_mut(&mut self) -> &mut T

Mutably borrows from an owned value. Read more
Source§

impl<T> CloneToUninit for T
where T: Clone,

Source§

unsafe fn clone_to_uninit(&self, dest: *mut u8)

🔬This is a nightly-only experimental API. (clone_to_uninit)
Performs copy-assignment from self to dest. Read more
Source§

impl<T> From<T> for T

Source§

fn from(t: T) -> T

Returns the argument unchanged.

Source§

impl<T, U> Into<U> for T
where U: From<T>,

Source§

fn into(self) -> U

Calls U::from(self).

That is, this conversion is whatever the implementation of From<T> for U chooses to do.

Source§

impl<T> ToOwned for T
where T: Clone,

Source§

type Owned = T

The resulting type after obtaining ownership.
Source§

fn to_owned(&self) -> T

Creates owned data from borrowed data, usually by cloning. Read more
Source§

fn clone_into(&self, target: &mut T)

Uses borrowed data to replace owned data, usually by cloning. Read more
Source§

impl<T, U> TryFrom<U> for T
where U: Into<T>,

Source§

type Error = Infallible

The type returned in the event of a conversion error.
Source§

fn try_from(value: U) -> Result<T, <T as TryFrom<U>>::Error>

Performs the conversion.
Source§

impl<T, U> TryInto<U> for T
where U: TryFrom<T>,

Source§

type Error = <U as TryFrom<T>>::Error

The type returned in the event of a conversion error.
Source§

fn try_into(self) -> Result<U, <U as TryFrom<T>>::Error>

Performs the conversion.