Improve widget formatting and add logged-in users support

Services widget:
- Fix disk quota formatting with proper rounding instead of truncation
- Remove decimals from RAM quotas and use GB instead of G
- Change quota display to use GB consistently

Backups widget:
- Change GiB to GB for consistency
- Remove spaces between numbers and units
- Update disk usage format to match other widgets: used (totalGB)
- Remove percentage display for cleaner format

System widget:
- Add support for logged-in users in description lines
- Format C-states with "C-State:" prefix on first line, indent subsequent lines
- Add logged_in_users field to SystemSummary data structure

Documentation:
- Add example hash error output to NixOS update instructions
This commit is contained in:
Christoffer Martinsson 2025-10-14 18:59:31 +02:00
parent 3e5e91f078
commit 1ee398e648
6 changed files with 59 additions and 22 deletions

View File

@ -397,8 +397,15 @@ When code changes are made to cm-dashboard, the NixOS configuration at `~/nixosb
}' 2>&1 | grep "got:" }' 2>&1 | grep "got:"
``` ```
Example output:
```
error: hash mismatch in fixed-output derivation '/nix/store/...':
specified: sha256-AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA=
got: sha256-x8crxNusOUYRrkP9mYEOG+Ga3JCPIdJLkEAc5P1ZxdQ=
```
4. **Update Configuration with Correct Hash** 4. **Update Configuration with Correct Hash**
Replace the placeholder with the hash from the error message. Replace the placeholder with the hash from the error message (the "got:" line).
5. **Commit NixOS Configuration** 5. **Commit NixOS Configuration**
```bash ```bash

View File

@ -486,8 +486,15 @@ nix-build --no-out-link -E 'with import <nixpkgs> {}; fetchFromGitea {
}' 2>&1 | grep "got:" }' 2>&1 | grep "got:"
``` ```
Example output:
```
error: hash mismatch in fixed-output derivation '/nix/store/...':
specified: sha256-AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA=
got: sha256-x8crxNusOUYRrkP9mYEOG+Ga3JCPIdJLkEAc5P1ZxdQ=
```
#### 4. Update the Hash #### 4. Update the Hash
Replace the placeholder with the correct hash: Replace the placeholder with the correct hash from the error message (the "got:" line):
```nix ```nix
hash = "sha256-vjy+j91iDCHUf0RE43anK4WZ+rKcyohP/3SykwZGof8="; # Use actual hash hash = "sha256-vjy+j91iDCHUf0RE43anK4WZ+rKcyohP/3SykwZGof8="; # Use actual hash
``` ```

View File

@ -58,6 +58,8 @@ pub struct SystemSummary {
pub cpu_temp_status: Option<String>, pub cpu_temp_status: Option<String>,
#[serde(default)] #[serde(default)]
pub cpu_cstate: Option<Vec<String>>, pub cpu_cstate: Option<Vec<String>>,
#[serde(default)]
pub logged_in_users: Option<Vec<String>>,
} }
#[derive(Debug, Clone, Serialize, Deserialize)] #[derive(Debug, Clone, Serialize, Deserialize)]

View File

@ -59,11 +59,11 @@ fn render_metrics(frame: &mut Frame, _host: &HostDisplayData, metrics: &BackupMe
data.add_row( data.add_row(
Some(WidgetStatus::new(latest_status)), Some(WidgetStatus::new(latest_status)),
vec![format!("{} archives, {:.1} GiB total", metrics.backup.snapshot_count, metrics.backup.size_gb)], vec![format!("{} archives, {:.1}GB total", metrics.backup.snapshot_count, metrics.backup.size_gb)],
vec![ vec![
"Latest".to_string(), "Latest".to_string(),
latest_time, latest_time,
format!("{:.1} GiB", metrics.backup.latest_archive_size_gb.unwrap_or(metrics.backup.size_gb)), format!("{:.1}GB", metrics.backup.latest_archive_size_gb.unwrap_or(metrics.backup.size_gb)),
], ],
); );
@ -81,7 +81,15 @@ fn render_metrics(frame: &mut Frame, _host: &HostDisplayData, metrics: &BackupMe
vec![ vec![
"Disk".to_string(), "Disk".to_string(),
disk.health.clone(), disk.health.clone(),
format!("{:.1}/{:.0} GB ({:.0}%)", disk.used_gb, disk.total_gb, disk.usage_percent), {
let used_mb = disk.used_gb * 1000.0;
let used_str = if used_mb < 1000.0 {
format!("{:.0}MB", used_mb)
} else {
format!("{:.1}GB", disk.used_gb)
};
format!("{} ({}GB)", used_str, disk.total_gb.round() as u32)
},
], ],
); );
} else { } else {

View File

@ -154,13 +154,8 @@ fn format_memory_value(used: f32, quota: f32) -> String {
if quota > 0.05 { if quota > 0.05 {
let quota_gb = quota / 1000.0; let quota_gb = quota / 1000.0;
// Format quota nicely - show decimals only if needed // Format quota without decimals and use GB
let quota_str = if quota_gb.fract() == 0.0 { format!("{} ({}GB)", used_value, quota_gb as u32)
format!("{}G", quota_gb as u32)
} else {
format!("{:.1}G", quota_gb)
};
format!("{} ({})", used_value, quota_str)
} else { } else {
used_value used_value
} }
@ -178,13 +173,8 @@ fn format_disk_value(used: f32, quota: f32) -> String {
let used_value = format_bytes(used * 1000.0); // Convert GB to MB for format_bytes let used_value = format_bytes(used * 1000.0); // Convert GB to MB for format_bytes
if quota > 0.05 { if quota > 0.05 {
// Format quota nicely - show decimals only if needed // Format quota without decimals and use GB (round to nearest GB)
let quota_str = if quota.fract() == 0.0 { format!("{} ({}GB)", used_value, quota.round() as u32)
format!("{}G", quota as u32)
} else {
format!("{:.1}G", quota)
};
format!("{} ({})", used_value, quota_str)
} else { } else {
used_value used_value
} }

View File

@ -65,12 +65,35 @@ fn render_metrics(
overall_status.clone() overall_status.clone()
); );
// Use agent-provided C-states as description (agent decides what goes in descriptions) // Use agent-provided C-states and logged-in users as description
let cstate_description = summary.cpu_cstate.clone().unwrap_or_default(); let mut description_lines = Vec::new();
// Add C-states with prefix on first line, indent subsequent lines
if let Some(cstates) = &summary.cpu_cstate {
for (i, cstate_line) in cstates.iter().enumerate() {
if i == 0 {
description_lines.push(format!("C-State: {}", cstate_line));
} else {
description_lines.push(format!(" {}", cstate_line));
}
}
}
// Add logged-in users to description
if let Some(users) = &summary.logged_in_users {
if !users.is_empty() {
let user_line = if users.len() == 1 {
format!("Logged in: {}", users[0])
} else {
format!("Logged in: {} users ({})", users.len(), users.join(", "))
};
description_lines.push(user_line);
}
}
system_dataset.add_row( system_dataset.add_row(
overall_status.clone(), overall_status.clone(),
cstate_description, description_lines,
vec![ vec![
format!("{:.1} / {:.1} GB", summary.memory_used_mb / 1000.0, summary.memory_total_mb / 1000.0), format!("{:.1} / {:.1} GB", summary.memory_used_mb / 1000.0, summary.memory_total_mb / 1000.0),
format!("{:.2}{:.2}{:.2}", summary.cpu_load_1, summary.cpu_load_5, summary.cpu_load_15), format!("{:.2}{:.2}{:.2}", summary.cpu_load_1, summary.cpu_load_5, summary.cpu_load_15),