From 5b3b0ce15654218ce2ab764cd357f13d9a6e9603 Mon Sep 17 00:00:00 2001 From: m1ngsama Date: Tue, 16 Dec 2025 17:59:09 +0800 Subject: [PATCH] optimize(ui): enhance CLI output with colors and progress bars - Add crossterm dependency for TUI features - Implement colored progress bars for system metrics - Add clear screen and cursor reset in continuous mode - Style process table with colors and formatting - Colorize alert thresholds (Green/Yellow/Red) --- Cargo.toml | 2 ++ src/main.rs | 7 +++++++ src/monitor.rs | 46 +++++++++++++++++++++++++++++++++++++--------- src/process.rs | 34 ++++++++++++++++++++++++++++------ src/temperature.rs | 4 +++- 5 files changed, 77 insertions(+), 16 deletions(-) diff --git a/Cargo.toml b/Cargo.toml index 6a3035a..bd65670 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -16,6 +16,8 @@ log = "0.4" env_logger = "0.11" csv = "1.3" anyhow = "1.0" +crossterm = "0.27" + [profile.release] lto = true diff --git a/src/main.rs b/src/main.rs index adce467..7560689 100644 --- a/src/main.rs +++ b/src/main.rs @@ -1,6 +1,12 @@ use clap::Parser; use std::thread; use std::time::Duration; +use crossterm::{ + execute, + terminal::{Clear, ClearType}, + cursor::MoveTo, +}; +use std::io::stdout; mod config; mod monitor; @@ -44,6 +50,7 @@ fn main() { if args.continuous { log::info!("Starting continuous monitoring mode with {}s interval", args.interval); loop { + execute!(stdout(), Clear(ClearType::All), MoveTo(0, 0)).unwrap(); monitor.display_stats(); thread::sleep(Duration::from_secs(args.interval)); } diff --git a/src/monitor.rs b/src/monitor.rs index 7a43d62..1fdc1cd 100644 --- a/src/monitor.rs +++ b/src/monitor.rs @@ -5,6 +5,7 @@ use crate::alert::AlertSystem; use crate::logger::TrackerLogger; use sysinfo::{System, Disks, Networks}; use chrono::Local; +use crossterm::style::Stylize; pub struct SystemMonitor { config: Config, @@ -31,6 +32,23 @@ impl SystemMonitor { } } + fn draw_bar(&self, percent: f32) -> String { + let width = 20; + let filled = ((percent / 100.0) * width as f32).round() as usize; + let filled = filled.min(width); // Ensure we don't exceed width + let empty = width.saturating_sub(filled); + + let bar_str = format!("[{}{}]", "|".repeat(filled), " ".repeat(empty)); + + if percent >= 90.0 { + bar_str.red().to_string() + } else if percent >= 75.0 { + bar_str.yellow().to_string() + } else { + bar_str.green().to_string() + } + } + pub fn get_cpu_usage(&mut self) -> f32 { self.sys.refresh_cpu_all(); self.sys.global_cpu_usage() @@ -105,20 +123,24 @@ impl SystemMonitor { } pub fn display_stats(&mut self) { - println!("\n{}", "=".repeat(50)); - println!("System Tracker - {}", Local::now().format("%Y-%m-%d %H:%M:%S")); - println!("{}\n", "=".repeat(50)); + println!("\n{}", "=".repeat(50).blue()); + println!("System Tracker - {}", Local::now().format("%Y-%m-%d %H:%M:%S").to_string().cyan().bold()); + println!("{}\n", "=".repeat(50).blue()); if self.config.display.show_cpu { let cpu_usage = self.get_cpu_usage(); - println!("CPU Usage: {:.2}%", cpu_usage); + let bar = self.draw_bar(cpu_usage); + println!("{:<15} {} {:.2}%", "CPU Usage:".bold(), bar, cpu_usage); self.logger.log_stats("CPU", &format!("{:.2}%", cpu_usage)); self.alert_system.check_cpu_alert(cpu_usage); } if self.config.display.show_memory { let mem = self.get_memory_info(); - println!("Memory: {:.2}% ({:.2}GB / {:.2}GB)", + let bar = self.draw_bar(mem.percent); + println!("{:<15} {} {:.2}% ({:.2}GB / {:.2}GB)", + "Memory:".bold(), + bar, mem.percent, mem.used as f64 / (1024_f64.powi(3)), mem.total as f64 / (1024_f64.powi(3)) @@ -129,7 +151,10 @@ impl SystemMonitor { if self.config.display.show_disk { let disk = self.get_disk_usage(); - println!("Disk: {:.2}% ({:.2}GB / {:.2}GB)", + let bar = self.draw_bar(disk.percent); + println!("{:<15} {} {:.2}% ({:.2}GB / {:.2}GB)", + "Disk:".bold(), + bar, disk.percent, disk.used as f64 / (1024_f64.powi(3)), disk.total as f64 / (1024_f64.powi(3)) @@ -140,18 +165,21 @@ impl SystemMonitor { if self.config.display.show_network { let net = self.get_network_stats(); - println!("Network: Sent {:.2}MB | Recv {:.2}MB", - net.bytes_sent as f64 / (1024_f64.powi(2)), - net.bytes_recv as f64 / (1024_f64.powi(2)) + println!("{:<15} Sent {} | Recv {}", + "Network:".bold(), + format!("{:.2}MB", net.bytes_sent as f64 / (1024_f64.powi(2))).green(), + format!("{:.2}MB", net.bytes_recv as f64 / (1024_f64.powi(2))).green() ); self.logger.log_stats("Network", &format!("Sent: {} Recv: {}", net.bytes_sent, net.bytes_recv)); } if self.config.display.show_processes { + println!("\n{}", "Top Processes:".bold().underlined()); self.process_monitor.display_processes(self.config.process_limit); } if self.config.display.show_temperatures { + println!("\n{}", "Temperatures:".bold().underlined()); self.temperature_monitor.display_temperatures(); } } diff --git a/src/process.rs b/src/process.rs index 1be1c20..f2e5880 100644 --- a/src/process.rs +++ b/src/process.rs @@ -1,4 +1,5 @@ use sysinfo::System; +use crossterm::style::Stylize; pub struct ProcessMonitor { sys: System, @@ -52,16 +53,37 @@ impl ProcessMonitor { } pub fn display_processes(&mut self, limit: usize) { - println!("\nTop Processes by CPU Usage:"); - println!("{:<10}{:<30}{:<10}{:<10}", "PID", "Name", "CPU%", "Memory%"); - println!("{}", "-".repeat(60)); + println!("{:<10}{:<30}{:<10}{:<10}", + "PID".bold(), "Name".bold(), "CPU%".bold(), "Memory%".bold()); + println!("{}", "-".repeat(60).blue()); for proc in self.get_top_processes(limit) { - println!("{:<10}{:<30}{:<10.2}{:<10.2}", - proc.pid, proc.name, proc.cpu_percent, proc.memory_percent); + let cpu = format!("{:.2}", proc.cpu_percent); + let mem = format!("{:.2}", proc.memory_percent); + + let cpu_colored = if proc.cpu_percent > 50.0 { + cpu.red() + } else if proc.cpu_percent > 20.0 { + cpu.yellow() + } else { + cpu.green() + }; + + let name = if proc.name.len() > 28 { + format!("{}...", &proc.name[..25]) + } else { + proc.name.clone() + }; + + println!("{:<10}{:<30}{:<10}{:<10}", + proc.pid, + name, + cpu_colored, + mem + ); } - println!("\nTotal Processes: {}", self.get_process_count()); + println!("\nTotal Processes: {}", self.get_process_count().to_string().cyan()); } } diff --git a/src/temperature.rs b/src/temperature.rs index 7706fb3..81148df 100644 --- a/src/temperature.rs +++ b/src/temperature.rs @@ -1,3 +1,5 @@ +use crossterm::style::Stylize; + pub struct TemperatureMonitor; impl TemperatureMonitor { @@ -6,7 +8,7 @@ impl TemperatureMonitor { } pub fn display_temperatures(&mut self) { - println!("\nTemperature sensors not available on this system"); + println!("{}", "Temperature sensors not available on this system".yellow()); } }