Skip to main content

output_gallery/
output_gallery.rs

1//! A gallery of every static output widget. Run with
2//! `cargo run --example output_gallery --features markup,fuzzy,pager`.
3//!
4//! Everything here renders to a fixed frame, so the output is deterministic
5//! and pipe-friendly (`| cat`, `> file`, or `NO_COLOR=1` yield plain text).
6//! Time-based widgets (spinner animation, progress, multi-progress, live,
7//! pager) live in the `output_dynamic` example.
8
9use sparcli::prelude::*;
10use sparcli::{
11    Cell, Column, Columns, Diff, ProgressBar, ProgressStyle, Spinner, Table,
12    Thresholds,
13};
14
15#[cfg(feature = "markup")]
16use sparcli::markup::markup_println;
17
18fn main() -> Result<()> {
19    styled_text()?;
20    sections()?;
21    alerts()?;
22    panels()?;
23    tables()?;
24    lists_and_trees()?;
25    key_values_and_badges()?;
26    progress_and_spinner()?;
27    diff_and_columns()?;
28    composition()?;
29    Ok(())
30}
31
32/// Prints a section header rule.
33fn section(title: &str) -> Result<()> {
34    println!();
35    Rule::with_title(title).align(Align::Left).print()?;
36    println!();
37    Ok(())
38}
39
40/// Styled spans, attributes, an OSC-8 hyperlink and optional markup.
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}