Skip to main content

08_system_monitor/
08_system_monitor.rs

1//! Example 08: System Monitor
2//!
3//! Demonstrates multiple streams and complex layout with simulated system stats.
4//!
5//! Run with: cargo run -p telex-tui --example 08_system_monitor
6
7use crossterm::event::KeyCode;
8use crossterm::style::Color;
9use std::time::Duration;
10use telex::prelude::*;
11
12telex::require_api!(0, 2);
13
14fn main() {
15    telex::run_with_theme(App, telex::theme::Theme::nord()).unwrap();
16}
17
18struct App;
19
20impl Component for App {
21    fn render(&self, cx: Scope) -> View {
22        let show_help = state!(cx, || false);
23
24        // F1 toggles help
25        cx.use_command(
26            KeyBinding::key(KeyCode::F(1)),
27            with!(show_help => move || show_help.update(|v| *v = !*v)),
28        );
29
30        // CPU usage stream (fluctuates between 10-90%)
31        let cpu = stream!(cx, || {
32            let mut rng_state = 42u64;
33            (0..).map(move |_| {
34                std::thread::sleep(Duration::from_millis(500));
35                // Simple LCG pseudo-random
36                rng_state = rng_state.wrapping_mul(1103515245).wrapping_add(12345);
37
38                ((rng_state >> 16) % 80) as u8 + 10
39            })
40        });
41
42        // Memory usage stream (slowly increases then drops)
43        let memory = stream!(cx, || {
44            (0..).map(|i| {
45                std::thread::sleep(Duration::from_millis(800));
46                let cycle = i % 20;
47                if cycle < 15 {
48                    40 + (cycle * 3) as u8
49                } else {
50                    40 + ((20 - cycle) * 8) as u8
51                }
52            })
53        });
54
55        // Network stream (random-ish traffic)
56        let network = stream!(cx, || {
57            let mut rng_state = 123u64;
58            (0..).map(move |_| {
59                std::thread::sleep(Duration::from_millis(300));
60                rng_state = rng_state.wrapping_mul(1103515245).wrapping_add(12345);
61
62                ((rng_state >> 16) % 1000) as u32 + 100
63            })
64        });
65
66        let cpu_val = cpu.get();
67        let mem_val = memory.get();
68        let net_val = network.get();
69
70        // Color based on value
71        let cpu_color = if cpu_val > 80 {
72            Color::Red
73        } else if cpu_val > 50 {
74            Color::Yellow
75        } else {
76            Color::Green
77        };
78
79        let mem_color = if mem_val > 80 {
80            Color::Red
81        } else if mem_val > 60 {
82            Color::Yellow
83        } else {
84            Color::Green
85        };
86
87        // Create progress bar (ASCII to avoid multi-byte char issues)
88        fn progress_bar(value: u8, width: usize) -> String {
89            let filled = (value as usize * width) / 100;
90            let empty = width - filled;
91            format!("[{}{}]", "#".repeat(filled), "-".repeat(empty))
92        }
93
94        View::vstack()
95            .child(
96                View::styled_text("System Monitor")
97                    .color(Color::Cyan)
98                    .bold()
99                    .build(),
100            )
101            .child(View::gap(1))
102            .child(
103                View::hstack()
104                    .child(View::text("CPU:    "))
105                    .child(
106                        View::styled_text(progress_bar(cpu_val, 20))
107                            .color(cpu_color)
108                            .build(),
109                    )
110                    .child(
111                        View::styled_text(format!(" {:>3}%", cpu_val))
112                            .bold()
113                            .build(),
114                    )
115                    .build(),
116            )
117            .child(
118                View::hstack()
119                    .child(View::text("Memory: "))
120                    .child(
121                        View::styled_text(progress_bar(mem_val, 20))
122                            .color(mem_color)
123                            .build(),
124                    )
125                    .child(
126                        View::styled_text(format!(" {:>3}%", mem_val))
127                            .bold()
128                            .build(),
129                    )
130                    .build(),
131            )
132            .child(View::gap(1))
133            .child(
134                View::hstack()
135                    .child(View::text("Network: "))
136                    .child(
137                        View::styled_text(format!("{:>6} KB/s", net_val))
138                            .color(Color::Magenta)
139                            .build(),
140                    )
141                    .build(),
142            )
143            .child(View::gap(1))
144            .child(View::styled_text("F1 help • Ctrl+Q quit").dim().build())
145            .child(
146                View::modal()
147                    .visible(show_help.get())
148                    .title("Example 08: System Monitor")
149                    .on_dismiss(with!(show_help => move || show_help.set(false)))
150                    .child(
151                        View::vstack()
152                            .child(View::styled_text("What you're seeing").bold().build())
153                            .child(View::text(
154                                "• Multiple independent streams running together",
155                            ))
156                            .child(View::text("• Color-coded thresholds (green/yellow/red)"))
157                            .child(View::text("• ASCII progress bars"))
158                            .child(View::gap(1))
159                            .child(View::styled_text("Key concepts").bold().build())
160                            .child(View::text("• Each stream!() runs in its own thread"))
161                            .child(View::text(
162                                "• Streams update independently at different rates",
163                            ))
164                            .child(View::text("• Conditional styling based on values"))
165                            .child(View::gap(1))
166                            .child(View::styled_text("Try this").bold().build())
167                            .child(View::text("• Watch CPU fluctuate randomly"))
168                            .child(View::text("• Memory climbs then drops cyclically"))
169                            .child(View::text("• Network updates fastest (300ms)"))
170                            .child(View::gap(1))
171                            .child(View::styled_text("Next up").bold().build())
172                            .child(View::text(
173                                "→ 09_syntax_comparison: builder vs macro syntax",
174                            ))
175                            .child(View::gap(1))
176                            .child(View::styled_text("Press Escape to close").dim().build())
177                            .build(),
178                    )
179                    .build(),
180            )
181            .build()
182    }
183}