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:
parent
3e5e91f078
commit
1ee398e648
@ -396,9 +396,16 @@ When code changes are made to cm-dashboard, the NixOS configuration at `~/nixosb
|
|||||||
sha256 = "sha256-AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA=";
|
sha256 = "sha256-AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA=";
|
||||||
}' 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
|
||||||
|
|||||||
@ -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
|
||||||
```
|
```
|
||||||
|
|||||||
@ -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)]
|
||||||
|
|||||||
@ -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 {
|
||||||
|
|||||||
@ -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
|
||||||
}
|
}
|
||||||
|
|||||||
@ -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),
|
||||||
|
|||||||
Loading…
x
Reference in New Issue
Block a user