Resolves widget data persistence issue where switching hosts left stale data from the previous host displayed in widgets. Key improvements: - Add Clone derives to all widget structs (CpuWidget, MemoryWidget, ServicesWidget, BackupWidget) - Create HostWidgets struct to cache widget states per hostname - Update TuiApp with HashMap<String, HostWidgets> for per-host storage - Fix borrowing issues by cloning hostname before mutable self borrow - Implement instant widget state restoration when switching hosts Tab key host switching now displays cached widget data for each host without stale information persistence between switches.
133 lines
4.3 KiB
Rust
133 lines
4.3 KiB
Rust
use cm_dashboard_shared::{Metric, Status};
|
|
use ratatui::{
|
|
layout::{Constraint, Direction, Layout, Rect},
|
|
widgets::Paragraph,
|
|
Frame,
|
|
};
|
|
use tracing::debug;
|
|
|
|
use super::Widget;
|
|
use crate::ui::theme::{Theme, Typography, Components, StatusIcons};
|
|
|
|
/// CPU widget displaying load, temperature, and frequency
|
|
#[derive(Clone)]
|
|
pub struct CpuWidget {
|
|
/// CPU load averages (1, 5, 15 minutes)
|
|
load_1min: Option<f32>,
|
|
load_5min: Option<f32>,
|
|
load_15min: Option<f32>,
|
|
/// CPU temperature in Celsius
|
|
temperature: Option<f32>,
|
|
/// CPU frequency in MHz
|
|
frequency: Option<f32>,
|
|
/// Aggregated status
|
|
status: Status,
|
|
/// Last update indicator
|
|
has_data: bool,
|
|
}
|
|
|
|
impl CpuWidget {
|
|
pub fn new() -> Self {
|
|
Self {
|
|
load_1min: None,
|
|
load_5min: None,
|
|
load_15min: None,
|
|
temperature: None,
|
|
frequency: None,
|
|
status: Status::Unknown,
|
|
has_data: false,
|
|
}
|
|
}
|
|
|
|
/// Format load average for display
|
|
fn format_load(&self) -> String {
|
|
match (self.load_1min, self.load_5min, self.load_15min) {
|
|
(Some(l1), Some(l5), Some(l15)) => {
|
|
format!("{:.2} {:.2} {:.2}", l1, l5, l15)
|
|
}
|
|
_ => "— — —".to_string(),
|
|
}
|
|
}
|
|
|
|
/// Format frequency for display
|
|
fn format_frequency(&self) -> String {
|
|
match self.frequency {
|
|
Some(freq) => format!("{:.1} MHz", freq),
|
|
None => "— MHz".to_string(),
|
|
}
|
|
}
|
|
|
|
}
|
|
|
|
impl Widget for CpuWidget {
|
|
fn update_from_metrics(&mut self, metrics: &[&Metric]) {
|
|
debug!("CPU widget updating with {} metrics", metrics.len());
|
|
|
|
// Reset status aggregation
|
|
let mut statuses = Vec::new();
|
|
|
|
for metric in metrics {
|
|
match metric.name.as_str() {
|
|
"cpu_load_1min" => {
|
|
if let Some(value) = metric.value.as_f32() {
|
|
self.load_1min = Some(value);
|
|
statuses.push(metric.status);
|
|
}
|
|
}
|
|
"cpu_load_5min" => {
|
|
if let Some(value) = metric.value.as_f32() {
|
|
self.load_5min = Some(value);
|
|
statuses.push(metric.status);
|
|
}
|
|
}
|
|
"cpu_load_15min" => {
|
|
if let Some(value) = metric.value.as_f32() {
|
|
self.load_15min = Some(value);
|
|
statuses.push(metric.status);
|
|
}
|
|
}
|
|
"cpu_temperature_celsius" => {
|
|
if let Some(value) = metric.value.as_f32() {
|
|
self.temperature = Some(value);
|
|
statuses.push(metric.status);
|
|
}
|
|
}
|
|
"cpu_frequency_mhz" => {
|
|
if let Some(value) = metric.value.as_f32() {
|
|
self.frequency = Some(value);
|
|
statuses.push(metric.status);
|
|
}
|
|
}
|
|
_ => {}
|
|
}
|
|
}
|
|
|
|
// Aggregate status
|
|
self.status = if statuses.is_empty() {
|
|
Status::Unknown
|
|
} else {
|
|
Status::aggregate(&statuses)
|
|
};
|
|
|
|
self.has_data = !metrics.is_empty();
|
|
|
|
debug!("CPU widget updated: load={:?}, temp={:?}, freq={:?}, status={:?}",
|
|
self.load_1min, self.temperature, self.frequency, self.status);
|
|
}
|
|
|
|
fn render(&mut self, frame: &mut Frame, area: Rect) {
|
|
let content_chunks = Layout::default().direction(Direction::Vertical).constraints([Constraint::Length(1), Constraint::Length(1)]).split(area);
|
|
let cpu_title = Paragraph::new("CPU:").style(Typography::widget_title());
|
|
frame.render_widget(cpu_title, content_chunks[0]);
|
|
let load_freq_spans = StatusIcons::create_status_spans(self.status, &format!("Load: {} • {}", self.format_load(), self.format_frequency()));
|
|
let load_freq_para = Paragraph::new(ratatui::text::Line::from(load_freq_spans));
|
|
frame.render_widget(load_freq_para, content_chunks[1]);
|
|
}
|
|
}
|
|
|
|
|
|
impl Default for CpuWidget {
|
|
fn default() -> Self {
|
|
Self::new()
|
|
}
|
|
} |