pub struct StreamRenderer { /* private fields */ }Expand description
Incrementally renders markdown text chunks as they arrive.
StreamRenderer is designed for streaming LLM responses: as the model
generates markdown text chunk by chunk, this renderer produces complete,
renderable lines as soon as enough input has been buffered to form a
complete markdown element (e.g. a paragraph ended by a blank line, a
complete table, a closed fenced code block).
§Examples
use smart_markdown::{StreamRenderer, ThemeMode, is_light_terminal};
let width = terminal_size::terminal_size()
.map(|(w, _)| w.0 as usize)
.unwrap_or(80);
let theme = if is_light_terminal() { ThemeMode::Light } else { ThemeMode::Dark };
let mut sr = StreamRenderer::new(width, theme)
.with_ascii_table_borders(true)
.with_code_theme("base16-ocean.dark");
// Feed chunks as they arrive from the LLM
for line in sr.push("# Hello\n\n") {
println!("{line}");
}
for line in sr.push("this is **bold** text") {
println!("{line}");
}
// Flush anything still buffered at the end
for line in sr.flush_remaining() {
println!("{line}");
}Implementations§
Source§impl StreamRenderer
impl StreamRenderer
Sourcepub fn new(width: usize, theme_mode: ThemeMode) -> Self
pub fn new(width: usize, theme_mode: ThemeMode) -> Self
Create a new stream renderer.
width: terminal width in columns (e.g. from theterminal_sizecrate).theme_mode: controls syntax highlighting theme for code blocks.
Examples found in repository?
6fn main() -> Result<(), Box<dyn std::error::Error>> {
7 let mut sr = StreamRenderer::new(80, ThemeMode::Dark);
8
9 println!("Streaming Table Updates Demo\n--------------------------\n");
10
11 // Simulate LLM streaming table header and separator
12 for line in sr.push("# Real-time Data Analysis\n\n") {
13 println!("{line}");
14 }
15 io::stdout().flush()?;
16 thread::sleep(Duration::from_millis(300));
17
18 for line in sr.push("| Time | Metric | Value | Status |\n") {
19 println!("{line}");
20 }
21 io::stdout().flush()?;
22 thread::sleep(Duration::from_millis(300));
23
24 for line in sr.push("|------|--------|-------|--------|\n") {
25 println!("{line}");
26 }
27 io::stdout().flush()?;
28 thread::sleep(Duration::from_millis(300));
29
30 // Simulate LLM streaming data rows one by one
31 let lines = sr.push("| 10:00 | CPU Usage | 45% | Normal |\n");
32 for line in lines {
33 println!("{line}");
34 }
35 io::stdout().flush()?;
36 thread::sleep(Duration::from_millis(500));
37
38 let lines = sr.push("| 10:05 | CPU Usage | 67% | Warning |\n");
39 for line in lines {
40 println!("{line}");
41 }
42 io::stdout().flush()?;
43 thread::sleep(Duration::from_millis(500));
44
45 let lines = sr.push("| 10:10 | CPU Usage | 82% | Alert |\n");
46 for line in lines {
47 println!("{line}");
48 }
49 io::stdout().flush()?;
50 thread::sleep(Duration::from_millis(500));
51
52 let lines = sr.push("| 10:15 | CPU Usage | 95% | Critical |\n");
53 for line in lines {
54 println!("{line}");
55 }
56 io::stdout().flush()?;
57 thread::sleep(Duration::from_millis(500));
58
59 // Flush any remaining content
60 for line in sr.flush_remaining() {
61 println!("{line}");
62 }
63
64 println!();
65 Ok(())
66}More examples
6fn main() -> Result<(), Box<dyn std::error::Error>> {
7 let mut sr = StreamRenderer::new(80, ThemeMode::Dark);
8
9 println!(
10 "Streaming Markdown Demo with Table Updates\n----------------------------------------\n"
11 );
12
13 // Stream some headings and text
14 for line in sr.push("# Live Data Dashboard\n\n") {
15 println!("{line}");
16 }
17 io::stdout().flush()?;
18 thread::sleep(Duration::from_millis(200));
19
20 for line in sr.push("## System Metrics\n\n") {
21 println!("{line}");
22 }
23 io::stdout().flush()?;
24 thread::sleep(Duration::from_millis(200));
25
26 // Stream table header and separator
27 for line in sr.push("| Component | Status | Value | Trend |\n") {
28 println!("{line}");
29 }
30 io::stdout().flush()?;
31 thread::sleep(Duration::from_millis(200));
32
33 for line in sr.push("|-----------|--------|-------|-------|\n") {
34 println!("{line}");
35 }
36 io::stdout().flush()?;
37 thread::sleep(Duration::from_millis(200));
38
39 // Stream first data row
40 let lines = sr.push("| CPU Usage | Normal | 42% | → |\n");
41 for line in lines {
42 println!("{line}");
43 }
44 io::stdout().flush()?;
45 thread::sleep(Duration::from_millis(500));
46
47 // Stream second data row
48 let lines = sr.push("| Memory | Normal | 67% | ↗ |\n");
49 for line in lines {
50 println!("{line}");
51 }
52 io::stdout().flush()?;
53 thread::sleep(Duration::from_millis(500));
54
55 // Stream third data row
56 let lines = sr.push("| Disk I/O | High | 89% | ↗↗ |\n");
57 for line in lines {
58 println!("{line}");
59 }
60 io::stdout().flush()?;
61 thread::sleep(Duration::from_millis(500));
62
63 // Add some text after the table
64 for line in sr.push("\n### Alerts\n\n") {
65 println!("{line}");
66 }
67 io::stdout().flush()?;
68 thread::sleep(Duration::from_millis(200));
69
70 // Stream a list
71 for line in sr.push("- High disk I/O detected\n") {
72 println!("{line}");
73 }
74 io::stdout().flush()?;
75 thread::sleep(Duration::from_millis(200));
76
77 for line in sr.push("- Memory usage trending upward\n") {
78 println!("{line}");
79 }
80 io::stdout().flush()?;
81 thread::sleep(Duration::from_millis(200));
82
83 // Flush any remaining content
84 for line in sr.flush_remaining() {
85 println!("{line}");
86 }
87
88 println!();
89 Ok(())
90}Sourcepub fn with_code_theme(self, theme: &str) -> Self
pub fn with_code_theme(self, theme: &str) -> Self
Set a custom syntax highlighting theme by name.
See crate::highlight::list_themes for available theme names.
Sourcepub fn with_ascii_table_borders(self, ascii: bool) -> Self
pub fn with_ascii_table_borders(self, ascii: bool) -> Self
Use ASCII-only table borders (+, -, |) instead of Unicode
box-drawing characters (┌, ─, │, etc.).
Useful for terminals where Unicode box-drawing renders poorly (e.g. light-background themes without proper color inversion).
Sourcepub fn push(&mut self, text: &str) -> Vec<String>
pub fn push(&mut self, text: &str) -> Vec<String>
Push additional text chunks.
Returns rendered complete lines as they become available. Incomplete markdown (partial fenced blocks, tables, paragraphs) is buffered internally.
Examples found in repository?
6fn main() -> Result<(), Box<dyn std::error::Error>> {
7 let mut sr = StreamRenderer::new(80, ThemeMode::Dark);
8
9 println!("Streaming Table Updates Demo\n--------------------------\n");
10
11 // Simulate LLM streaming table header and separator
12 for line in sr.push("# Real-time Data Analysis\n\n") {
13 println!("{line}");
14 }
15 io::stdout().flush()?;
16 thread::sleep(Duration::from_millis(300));
17
18 for line in sr.push("| Time | Metric | Value | Status |\n") {
19 println!("{line}");
20 }
21 io::stdout().flush()?;
22 thread::sleep(Duration::from_millis(300));
23
24 for line in sr.push("|------|--------|-------|--------|\n") {
25 println!("{line}");
26 }
27 io::stdout().flush()?;
28 thread::sleep(Duration::from_millis(300));
29
30 // Simulate LLM streaming data rows one by one
31 let lines = sr.push("| 10:00 | CPU Usage | 45% | Normal |\n");
32 for line in lines {
33 println!("{line}");
34 }
35 io::stdout().flush()?;
36 thread::sleep(Duration::from_millis(500));
37
38 let lines = sr.push("| 10:05 | CPU Usage | 67% | Warning |\n");
39 for line in lines {
40 println!("{line}");
41 }
42 io::stdout().flush()?;
43 thread::sleep(Duration::from_millis(500));
44
45 let lines = sr.push("| 10:10 | CPU Usage | 82% | Alert |\n");
46 for line in lines {
47 println!("{line}");
48 }
49 io::stdout().flush()?;
50 thread::sleep(Duration::from_millis(500));
51
52 let lines = sr.push("| 10:15 | CPU Usage | 95% | Critical |\n");
53 for line in lines {
54 println!("{line}");
55 }
56 io::stdout().flush()?;
57 thread::sleep(Duration::from_millis(500));
58
59 // Flush any remaining content
60 for line in sr.flush_remaining() {
61 println!("{line}");
62 }
63
64 println!();
65 Ok(())
66}More examples
6fn main() -> Result<(), Box<dyn std::error::Error>> {
7 let mut sr = StreamRenderer::new(80, ThemeMode::Dark);
8
9 println!(
10 "Streaming Markdown Demo with Table Updates\n----------------------------------------\n"
11 );
12
13 // Stream some headings and text
14 for line in sr.push("# Live Data Dashboard\n\n") {
15 println!("{line}");
16 }
17 io::stdout().flush()?;
18 thread::sleep(Duration::from_millis(200));
19
20 for line in sr.push("## System Metrics\n\n") {
21 println!("{line}");
22 }
23 io::stdout().flush()?;
24 thread::sleep(Duration::from_millis(200));
25
26 // Stream table header and separator
27 for line in sr.push("| Component | Status | Value | Trend |\n") {
28 println!("{line}");
29 }
30 io::stdout().flush()?;
31 thread::sleep(Duration::from_millis(200));
32
33 for line in sr.push("|-----------|--------|-------|-------|\n") {
34 println!("{line}");
35 }
36 io::stdout().flush()?;
37 thread::sleep(Duration::from_millis(200));
38
39 // Stream first data row
40 let lines = sr.push("| CPU Usage | Normal | 42% | → |\n");
41 for line in lines {
42 println!("{line}");
43 }
44 io::stdout().flush()?;
45 thread::sleep(Duration::from_millis(500));
46
47 // Stream second data row
48 let lines = sr.push("| Memory | Normal | 67% | ↗ |\n");
49 for line in lines {
50 println!("{line}");
51 }
52 io::stdout().flush()?;
53 thread::sleep(Duration::from_millis(500));
54
55 // Stream third data row
56 let lines = sr.push("| Disk I/O | High | 89% | ↗↗ |\n");
57 for line in lines {
58 println!("{line}");
59 }
60 io::stdout().flush()?;
61 thread::sleep(Duration::from_millis(500));
62
63 // Add some text after the table
64 for line in sr.push("\n### Alerts\n\n") {
65 println!("{line}");
66 }
67 io::stdout().flush()?;
68 thread::sleep(Duration::from_millis(200));
69
70 // Stream a list
71 for line in sr.push("- High disk I/O detected\n") {
72 println!("{line}");
73 }
74 io::stdout().flush()?;
75 thread::sleep(Duration::from_millis(200));
76
77 for line in sr.push("- Memory usage trending upward\n") {
78 println!("{line}");
79 }
80 io::stdout().flush()?;
81 thread::sleep(Duration::from_millis(200));
82
83 // Flush any remaining content
84 for line in sr.flush_remaining() {
85 println!("{line}");
86 }
87
88 println!();
89 Ok(())
90}Sourcepub fn flush_remaining(&mut self) -> Vec<String>
pub fn flush_remaining(&mut self) -> Vec<String>
Flush any remaining buffered content and return the final lines.
Call this once at the end of the stream to emit any markdown that hasn’t been completed by a blank line or structural close.
Examples found in repository?
6fn main() -> Result<(), Box<dyn std::error::Error>> {
7 let mut sr = StreamRenderer::new(80, ThemeMode::Dark);
8
9 println!("Streaming Table Updates Demo\n--------------------------\n");
10
11 // Simulate LLM streaming table header and separator
12 for line in sr.push("# Real-time Data Analysis\n\n") {
13 println!("{line}");
14 }
15 io::stdout().flush()?;
16 thread::sleep(Duration::from_millis(300));
17
18 for line in sr.push("| Time | Metric | Value | Status |\n") {
19 println!("{line}");
20 }
21 io::stdout().flush()?;
22 thread::sleep(Duration::from_millis(300));
23
24 for line in sr.push("|------|--------|-------|--------|\n") {
25 println!("{line}");
26 }
27 io::stdout().flush()?;
28 thread::sleep(Duration::from_millis(300));
29
30 // Simulate LLM streaming data rows one by one
31 let lines = sr.push("| 10:00 | CPU Usage | 45% | Normal |\n");
32 for line in lines {
33 println!("{line}");
34 }
35 io::stdout().flush()?;
36 thread::sleep(Duration::from_millis(500));
37
38 let lines = sr.push("| 10:05 | CPU Usage | 67% | Warning |\n");
39 for line in lines {
40 println!("{line}");
41 }
42 io::stdout().flush()?;
43 thread::sleep(Duration::from_millis(500));
44
45 let lines = sr.push("| 10:10 | CPU Usage | 82% | Alert |\n");
46 for line in lines {
47 println!("{line}");
48 }
49 io::stdout().flush()?;
50 thread::sleep(Duration::from_millis(500));
51
52 let lines = sr.push("| 10:15 | CPU Usage | 95% | Critical |\n");
53 for line in lines {
54 println!("{line}");
55 }
56 io::stdout().flush()?;
57 thread::sleep(Duration::from_millis(500));
58
59 // Flush any remaining content
60 for line in sr.flush_remaining() {
61 println!("{line}");
62 }
63
64 println!();
65 Ok(())
66}More examples
6fn main() -> Result<(), Box<dyn std::error::Error>> {
7 let mut sr = StreamRenderer::new(80, ThemeMode::Dark);
8
9 println!(
10 "Streaming Markdown Demo with Table Updates\n----------------------------------------\n"
11 );
12
13 // Stream some headings and text
14 for line in sr.push("# Live Data Dashboard\n\n") {
15 println!("{line}");
16 }
17 io::stdout().flush()?;
18 thread::sleep(Duration::from_millis(200));
19
20 for line in sr.push("## System Metrics\n\n") {
21 println!("{line}");
22 }
23 io::stdout().flush()?;
24 thread::sleep(Duration::from_millis(200));
25
26 // Stream table header and separator
27 for line in sr.push("| Component | Status | Value | Trend |\n") {
28 println!("{line}");
29 }
30 io::stdout().flush()?;
31 thread::sleep(Duration::from_millis(200));
32
33 for line in sr.push("|-----------|--------|-------|-------|\n") {
34 println!("{line}");
35 }
36 io::stdout().flush()?;
37 thread::sleep(Duration::from_millis(200));
38
39 // Stream first data row
40 let lines = sr.push("| CPU Usage | Normal | 42% | → |\n");
41 for line in lines {
42 println!("{line}");
43 }
44 io::stdout().flush()?;
45 thread::sleep(Duration::from_millis(500));
46
47 // Stream second data row
48 let lines = sr.push("| Memory | Normal | 67% | ↗ |\n");
49 for line in lines {
50 println!("{line}");
51 }
52 io::stdout().flush()?;
53 thread::sleep(Duration::from_millis(500));
54
55 // Stream third data row
56 let lines = sr.push("| Disk I/O | High | 89% | ↗↗ |\n");
57 for line in lines {
58 println!("{line}");
59 }
60 io::stdout().flush()?;
61 thread::sleep(Duration::from_millis(500));
62
63 // Add some text after the table
64 for line in sr.push("\n### Alerts\n\n") {
65 println!("{line}");
66 }
67 io::stdout().flush()?;
68 thread::sleep(Duration::from_millis(200));
69
70 // Stream a list
71 for line in sr.push("- High disk I/O detected\n") {
72 println!("{line}");
73 }
74 io::stdout().flush()?;
75 thread::sleep(Duration::from_millis(200));
76
77 for line in sr.push("- Memory usage trending upward\n") {
78 println!("{line}");
79 }
80 io::stdout().flush()?;
81 thread::sleep(Duration::from_millis(200));
82
83 // Flush any remaining content
84 for line in sr.flush_remaining() {
85 println!("{line}");
86 }
87
88 println!();
89 Ok(())
90}