Fix storage display parsing and improve title bar UI
All checks were successful
Build and Release / build-and-release (push) Successful in 1m14s
All checks were successful
Build and Release / build-and-release (push) Successful in 1m14s
- Fix disk drive name extraction for mount points with underscores (e.g., /mnt/steampool) - Replace confusing "1" and "2" drive names with proper device names like "sda1", "sda2" - Update title bar with blue background and dark text styling - Right-align host list in title bar while keeping "cm-dashboard" on left - Bump version to v0.1.35
This commit is contained in:
parent
97aa1708c2
commit
1b964545be
6
Cargo.lock
generated
6
Cargo.lock
generated
@ -270,7 +270,7 @@ checksum = "a1d728cc89cf3aee9ff92b05e62b19ee65a02b5702cff7d5a377e32c6ae29d8d"
|
|||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "cm-dashboard"
|
name = "cm-dashboard"
|
||||||
version = "0.1.33"
|
version = "0.1.34"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"anyhow",
|
"anyhow",
|
||||||
"chrono",
|
"chrono",
|
||||||
@ -291,7 +291,7 @@ dependencies = [
|
|||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "cm-dashboard-agent"
|
name = "cm-dashboard-agent"
|
||||||
version = "0.1.33"
|
version = "0.1.34"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"anyhow",
|
"anyhow",
|
||||||
"async-trait",
|
"async-trait",
|
||||||
@ -314,7 +314,7 @@ dependencies = [
|
|||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "cm-dashboard-shared"
|
name = "cm-dashboard-shared"
|
||||||
version = "0.1.33"
|
version = "0.1.34"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"chrono",
|
"chrono",
|
||||||
"serde",
|
"serde",
|
||||||
|
|||||||
@ -1,6 +1,6 @@
|
|||||||
[package]
|
[package]
|
||||||
name = "cm-dashboard-agent"
|
name = "cm-dashboard-agent"
|
||||||
version = "0.1.34"
|
version = "0.1.35"
|
||||||
edition = "2021"
|
edition = "2021"
|
||||||
|
|
||||||
[dependencies]
|
[dependencies]
|
||||||
|
|||||||
@ -1,6 +1,6 @@
|
|||||||
[package]
|
[package]
|
||||||
name = "cm-dashboard"
|
name = "cm-dashboard"
|
||||||
version = "0.1.34"
|
version = "0.1.35"
|
||||||
edition = "2021"
|
edition = "2021"
|
||||||
|
|
||||||
[dependencies]
|
[dependencies]
|
||||||
|
|||||||
@ -536,48 +536,71 @@ impl TuiApp {
|
|||||||
|
|
||||||
if self.available_hosts.is_empty() {
|
if self.available_hosts.is_empty() {
|
||||||
let title_text = "cm-dashboard • no hosts discovered";
|
let title_text = "cm-dashboard • no hosts discovered";
|
||||||
let title = Paragraph::new(title_text).style(Typography::title());
|
let title = Paragraph::new(title_text)
|
||||||
|
.style(Style::default().fg(Theme::background()).bg(Theme::highlight()));
|
||||||
frame.render_widget(title, area);
|
frame.render_widget(title, area);
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
// Create spans for each host with status indicators
|
// Split the title bar into left and right sections
|
||||||
let mut spans = vec![Span::styled("cm-dashboard • ", Typography::title())];
|
let chunks = Layout::default()
|
||||||
|
.direction(Direction::Horizontal)
|
||||||
|
.constraints([Constraint::Min(0), Constraint::Min(0)])
|
||||||
|
.split(area);
|
||||||
|
|
||||||
|
// Left side: "cm-dashboard" text
|
||||||
|
let left_span = Span::styled(
|
||||||
|
"cm-dashboard",
|
||||||
|
Style::default().fg(Theme::background()).bg(Theme::highlight())
|
||||||
|
);
|
||||||
|
let left_title = Paragraph::new(Line::from(vec![left_span]))
|
||||||
|
.style(Style::default().bg(Theme::highlight()));
|
||||||
|
frame.render_widget(left_title, chunks[0]);
|
||||||
|
|
||||||
|
// Right side: hosts with status indicators
|
||||||
|
let mut host_spans = Vec::new();
|
||||||
|
|
||||||
for (i, host) in self.available_hosts.iter().enumerate() {
|
for (i, host) in self.available_hosts.iter().enumerate() {
|
||||||
if i > 0 {
|
if i > 0 {
|
||||||
spans.push(Span::styled(" ", Typography::title()));
|
host_spans.push(Span::styled(
|
||||||
|
" ",
|
||||||
|
Style::default().fg(Theme::background()).bg(Theme::highlight())
|
||||||
|
));
|
||||||
}
|
}
|
||||||
|
|
||||||
// Always show normal status icon based on metrics (no command status at host level)
|
// Always show normal status icon based on metrics (no command status at host level)
|
||||||
let host_status = self.calculate_host_status(host, metric_store);
|
let host_status = self.calculate_host_status(host, metric_store);
|
||||||
let (status_icon, status_color) = (StatusIcons::get_icon(host_status), Theme::status_color(host_status));
|
let status_icon = StatusIcons::get_icon(host_status);
|
||||||
|
|
||||||
// Add status icon
|
// Add status icon with background color as foreground against blue background
|
||||||
spans.push(Span::styled(
|
host_spans.push(Span::styled(
|
||||||
format!("{} ", status_icon),
|
format!("{} ", status_icon),
|
||||||
Style::default().fg(status_color),
|
Style::default().fg(Theme::background()).bg(Theme::highlight()),
|
||||||
));
|
));
|
||||||
|
|
||||||
if Some(host) == self.current_host.as_ref() {
|
if Some(host) == self.current_host.as_ref() {
|
||||||
// Selected host in bold bright white
|
// Selected host in bold background color against blue background
|
||||||
spans.push(Span::styled(
|
host_spans.push(Span::styled(
|
||||||
host.clone(),
|
host.clone(),
|
||||||
Typography::title().add_modifier(Modifier::BOLD),
|
Style::default()
|
||||||
|
.fg(Theme::background())
|
||||||
|
.bg(Theme::highlight())
|
||||||
|
.add_modifier(Modifier::BOLD),
|
||||||
));
|
));
|
||||||
} else {
|
} else {
|
||||||
// Other hosts in normal style with status color
|
// Other hosts in normal background color against blue background
|
||||||
spans.push(Span::styled(
|
host_spans.push(Span::styled(
|
||||||
host.clone(),
|
host.clone(),
|
||||||
Style::default().fg(status_color),
|
Style::default().fg(Theme::background()).bg(Theme::highlight()),
|
||||||
));
|
));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
let title_line = Line::from(spans);
|
let host_line = Line::from(host_spans);
|
||||||
let title = Paragraph::new(vec![title_line]);
|
let host_title = Paragraph::new(vec![host_line])
|
||||||
|
.style(Style::default().bg(Theme::highlight()))
|
||||||
frame.render_widget(title, area);
|
.alignment(ratatui::layout::Alignment::Right);
|
||||||
|
frame.render_widget(host_title, chunks[1]);
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Calculate overall status for a host based on its metrics
|
/// Calculate overall status for a host based on its metrics
|
||||||
|
|||||||
@ -230,20 +230,49 @@ impl SystemWidget {
|
|||||||
|
|
||||||
/// Extract pool name from disk metric name
|
/// Extract pool name from disk metric name
|
||||||
fn extract_pool_name(&self, metric_name: &str) -> Option<String> {
|
fn extract_pool_name(&self, metric_name: &str) -> Option<String> {
|
||||||
if let Some(captures) = metric_name.strip_prefix("disk_") {
|
// Pattern: disk_{pool_name}_{drive_name}_{metric_type}
|
||||||
|
// Since pool_name can contain underscores, work backwards from known metric suffixes
|
||||||
|
if metric_name.starts_with("disk_") {
|
||||||
|
// First try drive-specific metrics that have device names
|
||||||
|
if let Some(suffix_pos) = metric_name.rfind("_temperature")
|
||||||
|
.or_else(|| metric_name.rfind("_wear_percent"))
|
||||||
|
.or_else(|| metric_name.rfind("_health")) {
|
||||||
|
// Find the second-to-last underscore to get pool name
|
||||||
|
let before_suffix = &metric_name[..suffix_pos];
|
||||||
|
if let Some(drive_start) = before_suffix.rfind('_') {
|
||||||
|
return Some(metric_name[5..drive_start].to_string()); // Skip "disk_"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
// For pool-level metrics (usage_percent, used_gb, total_gb), take everything before the metric suffix
|
||||||
|
else if let Some(suffix_pos) = metric_name.rfind("_usage_percent")
|
||||||
|
.or_else(|| metric_name.rfind("_used_gb"))
|
||||||
|
.or_else(|| metric_name.rfind("_total_gb")) {
|
||||||
|
return Some(metric_name[5..suffix_pos].to_string()); // Skip "disk_"
|
||||||
|
}
|
||||||
|
// Fallback to old behavior for unknown patterns
|
||||||
|
else if let Some(captures) = metric_name.strip_prefix("disk_") {
|
||||||
if let Some(pos) = captures.find('_') {
|
if let Some(pos) = captures.find('_') {
|
||||||
return Some(captures[..pos].to_string());
|
return Some(captures[..pos].to_string());
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
}
|
||||||
None
|
None
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Extract drive name from disk metric name
|
/// Extract drive name from disk metric name
|
||||||
fn extract_drive_name(&self, metric_name: &str) -> Option<String> {
|
fn extract_drive_name(&self, metric_name: &str) -> Option<String> {
|
||||||
// Pattern: disk_pool_drive_metric
|
// Pattern: disk_{pool_name}_{drive_name}_{metric_type}
|
||||||
let parts: Vec<&str> = metric_name.split('_').collect();
|
// Since pool_name can contain underscores, work backwards from known metric suffixes
|
||||||
if parts.len() >= 3 && parts[0] == "disk" {
|
if metric_name.starts_with("disk_") {
|
||||||
return Some(parts[2].to_string());
|
if let Some(suffix_pos) = metric_name.rfind("_temperature")
|
||||||
|
.or_else(|| metric_name.rfind("_wear_percent"))
|
||||||
|
.or_else(|| metric_name.rfind("_health")) {
|
||||||
|
// Find the second-to-last underscore to get the drive name
|
||||||
|
let before_suffix = &metric_name[..suffix_pos];
|
||||||
|
if let Some(drive_start) = before_suffix.rfind('_') {
|
||||||
|
return Some(before_suffix[drive_start + 1..].to_string());
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
None
|
None
|
||||||
}
|
}
|
||||||
|
|||||||
@ -1,6 +1,6 @@
|
|||||||
[package]
|
[package]
|
||||||
name = "cm-dashboard-shared"
|
name = "cm-dashboard-shared"
|
||||||
version = "0.1.34"
|
version = "0.1.35"
|
||||||
edition = "2021"
|
edition = "2021"
|
||||||
|
|
||||||
[dependencies]
|
[dependencies]
|
||||||
|
|||||||
Loading…
x
Reference in New Issue
Block a user