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

@ -396,9 +396,16 @@ When code changes are made to cm-dashboard, the NixOS configuration at `~/nixosb
sha256 = "sha256-AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA=";
}' 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**
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**
```bash

View File

@ -486,8 +486,15 @@ nix-build --no-out-link -E 'with import <nixpkgs> {}; fetchFromGitea {
}' 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
Replace the placeholder with the correct hash:
Replace the placeholder with the correct hash from the error message (the "got:" line):
```nix
hash = "sha256-vjy+j91iDCHUf0RE43anK4WZ+rKcyohP/3SykwZGof8="; # Use actual hash
```

View File

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

View File

@ -59,11 +59,11 @@ fn render_metrics(frame: &mut Frame, _host: &HostDisplayData, metrics: &BackupMe
data.add_row(
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![
"Latest".to_string(),
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![
"Disk".to_string(),
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 {

View File

@ -154,13 +154,8 @@ fn format_memory_value(used: f32, quota: f32) -> String {
if quota > 0.05 {
let quota_gb = quota / 1000.0;
// Format quota nicely - show decimals only if needed
let quota_str = if quota_gb.fract() == 0.0 {
format!("{}G", quota_gb as u32)
} else {
format!("{:.1}G", quota_gb)
};
format!("{} ({})", used_value, quota_str)
// Format quota without decimals and use GB
format!("{} ({}GB)", used_value, quota_gb as u32)
} else {
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
if quota > 0.05 {
// Format quota nicely - show decimals only if needed
let quota_str = if quota.fract() == 0.0 {
format!("{}G", quota as u32)
} else {
format!("{:.1}G", quota)
};
format!("{} ({})", used_value, quota_str)
// Format quota without decimals and use GB (round to nearest GB)
format!("{} ({}GB)", used_value, quota.round() as u32)
} else {
used_value
}

View File

@ -65,12 +65,35 @@ fn render_metrics(
overall_status.clone()
);
// Use agent-provided C-states as description (agent decides what goes in descriptions)
let cstate_description = summary.cpu_cstate.clone().unwrap_or_default();
// Use agent-provided C-states and logged-in users as description
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(
overall_status.clone(),
cstate_description,
description_lines,
vec![
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),