Compare commits
3 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
| 748a9f3a3b | |||
| 5c6b11c794 | |||
| 9f0aa5f806 |
@@ -304,6 +304,12 @@ exclude_fs_types = ["tmpfs", "devtmpfs", "sysfs", "proc"]
|
|||||||
### Display Format
|
### Display Format
|
||||||
|
|
||||||
```
|
```
|
||||||
|
Network:
|
||||||
|
● eno1:
|
||||||
|
├─ ip: 192.168.30.105
|
||||||
|
└─ tailscale0: 100.125.108.16
|
||||||
|
● eno2:
|
||||||
|
└─ ip: 192.168.32.105
|
||||||
CPU:
|
CPU:
|
||||||
● Load: 0.23 0.21 0.13
|
● Load: 0.23 0.21 0.13
|
||||||
└─ Freq: 1048 MHz
|
└─ Freq: 1048 MHz
|
||||||
|
|||||||
6
Cargo.lock
generated
6
Cargo.lock
generated
@@ -279,7 +279,7 @@ checksum = "a1d728cc89cf3aee9ff92b05e62b19ee65a02b5702cff7d5a377e32c6ae29d8d"
|
|||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "cm-dashboard"
|
name = "cm-dashboard"
|
||||||
version = "0.1.165"
|
version = "0.1.168"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"anyhow",
|
"anyhow",
|
||||||
"chrono",
|
"chrono",
|
||||||
@@ -301,7 +301,7 @@ dependencies = [
|
|||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "cm-dashboard-agent"
|
name = "cm-dashboard-agent"
|
||||||
version = "0.1.165"
|
version = "0.1.168"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"anyhow",
|
"anyhow",
|
||||||
"async-trait",
|
"async-trait",
|
||||||
@@ -324,7 +324,7 @@ dependencies = [
|
|||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "cm-dashboard-shared"
|
name = "cm-dashboard-shared"
|
||||||
version = "0.1.165"
|
version = "0.1.168"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"chrono",
|
"chrono",
|
||||||
"serde",
|
"serde",
|
||||||
|
|||||||
@@ -1,6 +1,6 @@
|
|||||||
[package]
|
[package]
|
||||||
name = "cm-dashboard-agent"
|
name = "cm-dashboard-agent"
|
||||||
version = "0.1.165"
|
version = "0.1.168"
|
||||||
edition = "2021"
|
edition = "2021"
|
||||||
|
|
||||||
[dependencies]
|
[dependencies]
|
||||||
|
|||||||
@@ -90,6 +90,12 @@ impl NetworkCollector {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Only add interfaces that have at least one IP address
|
||||||
|
// This filters out ifb*, dummy interfaces, etc. that have no IPs
|
||||||
|
if ipv4_addresses.is_empty() && ipv6_addresses.is_empty() {
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
// Determine if physical and get status
|
// Determine if physical and get status
|
||||||
let is_physical = Self::is_physical_interface(&name);
|
let is_physical = Self::is_physical_interface(&name);
|
||||||
let link_status = if is_physical {
|
let link_status = if is_physical {
|
||||||
@@ -104,7 +110,7 @@ impl NetworkCollector {
|
|||||||
ipv6_addresses,
|
ipv6_addresses,
|
||||||
is_physical,
|
is_physical,
|
||||||
link_status,
|
link_status,
|
||||||
parent_interface: None, // TODO: Implement virtual interface parent detection
|
parent_interface: None,
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,6 +1,6 @@
|
|||||||
[package]
|
[package]
|
||||||
name = "cm-dashboard"
|
name = "cm-dashboard"
|
||||||
version = "0.1.165"
|
version = "0.1.168"
|
||||||
edition = "2021"
|
edition = "2021"
|
||||||
|
|
||||||
[dependencies]
|
[dependencies]
|
||||||
|
|||||||
@@ -628,60 +628,65 @@ impl SystemWidget {
|
|||||||
let physical: Vec<_> = self.network_interfaces.iter().filter(|i| i.is_physical).collect();
|
let physical: Vec<_> = self.network_interfaces.iter().filter(|i| i.is_physical).collect();
|
||||||
let virtual_interfaces: Vec<_> = self.network_interfaces.iter().filter(|i| !i.is_physical).collect();
|
let virtual_interfaces: Vec<_> = self.network_interfaces.iter().filter(|i| !i.is_physical).collect();
|
||||||
|
|
||||||
// Render physical interfaces first
|
// Render physical interfaces
|
||||||
for (i, interface) in physical.iter().enumerate() {
|
for (phy_idx, interface) in physical.iter().enumerate() {
|
||||||
let is_last = i == physical.len() - 1 && virtual_interfaces.is_empty();
|
let is_last_physical = phy_idx == physical.len() - 1 && virtual_interfaces.is_empty();
|
||||||
let tree_symbol = if is_last { " └─ " } else { " ├─ " };
|
|
||||||
|
|
||||||
// Show interface name with IPs
|
// Physical interface header with status icon
|
||||||
let mut interface_text = format!("{}: ", interface.name);
|
let mut header_spans = vec![];
|
||||||
|
header_spans.extend(StatusIcons::create_status_spans(
|
||||||
// Add compressed IPv4 addresses
|
|
||||||
if !interface.ipv4_addresses.is_empty() {
|
|
||||||
interface_text.push_str(&Self::compress_ipv4_addresses(&interface.ipv4_addresses));
|
|
||||||
}
|
|
||||||
|
|
||||||
// Add IPv6 addresses (no compression for now)
|
|
||||||
if !interface.ipv6_addresses.is_empty() {
|
|
||||||
if !interface.ipv4_addresses.is_empty() {
|
|
||||||
interface_text.push_str(", ");
|
|
||||||
}
|
|
||||||
interface_text.push_str(&interface.ipv6_addresses.join(", "));
|
|
||||||
}
|
|
||||||
|
|
||||||
// Physical interfaces show status icon
|
|
||||||
let mut spans = vec![
|
|
||||||
Span::styled(tree_symbol, Typography::tree()),
|
|
||||||
];
|
|
||||||
spans.extend(StatusIcons::create_status_spans(
|
|
||||||
interface.link_status.clone(),
|
interface.link_status.clone(),
|
||||||
&interface_text
|
&format!("{}:", interface.name)
|
||||||
));
|
));
|
||||||
lines.push(Line::from(spans));
|
lines.push(Line::from(header_spans));
|
||||||
|
|
||||||
|
// Show IPs nested under the interface
|
||||||
|
let ip_count = interface.ipv4_addresses.len() + interface.ipv6_addresses.len();
|
||||||
|
let mut ip_index = 0;
|
||||||
|
|
||||||
|
// IPv4 addresses
|
||||||
|
for ipv4 in &interface.ipv4_addresses {
|
||||||
|
ip_index += 1;
|
||||||
|
let is_last_ip = ip_index == ip_count && is_last_physical;
|
||||||
|
let tree_symbol = if is_last_ip { " └─ " } else { " ├─ " };
|
||||||
|
lines.push(Line::from(vec![
|
||||||
|
Span::styled(tree_symbol, Typography::tree()),
|
||||||
|
Span::styled(format!("ip: {}", ipv4), Typography::secondary()),
|
||||||
|
]));
|
||||||
|
}
|
||||||
|
|
||||||
|
// IPv6 addresses
|
||||||
|
for ipv6 in &interface.ipv6_addresses {
|
||||||
|
ip_index += 1;
|
||||||
|
let is_last_ip = ip_index == ip_count && is_last_physical;
|
||||||
|
let tree_symbol = if is_last_ip { " └─ " } else { " ├─ " };
|
||||||
|
lines.push(Line::from(vec![
|
||||||
|
Span::styled(tree_symbol, Typography::tree()),
|
||||||
|
Span::styled(format!("ip: {}", ipv6), Typography::secondary()),
|
||||||
|
]));
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// Render virtual interfaces
|
// Render standalone virtual interfaces (those without a parent)
|
||||||
for (i, interface) in virtual_interfaces.iter().enumerate() {
|
for (virt_idx, interface) in virtual_interfaces.iter().enumerate() {
|
||||||
let is_last = i == virtual_interfaces.len() - 1;
|
let is_last = virt_idx == virtual_interfaces.len() - 1;
|
||||||
let tree_symbol = if is_last { " └─ " } else { " ├─ " };
|
let tree_symbol = if is_last { " └─ " } else { " ├─ " };
|
||||||
|
|
||||||
// Show interface name with IPs
|
// Virtual interface with IPs
|
||||||
let mut interface_text = format!("{}: ", interface.name);
|
let ip_text = if !interface.ipv4_addresses.is_empty() {
|
||||||
|
Self::compress_ipv4_addresses(&interface.ipv4_addresses)
|
||||||
|
} else if !interface.ipv6_addresses.is_empty() {
|
||||||
|
interface.ipv6_addresses.join(", ")
|
||||||
|
} else {
|
||||||
|
String::new()
|
||||||
|
};
|
||||||
|
|
||||||
// Add compressed IPv4 addresses
|
let interface_text = if !ip_text.is_empty() {
|
||||||
if !interface.ipv4_addresses.is_empty() {
|
format!("{}: {}", interface.name, ip_text)
|
||||||
interface_text.push_str(&Self::compress_ipv4_addresses(&interface.ipv4_addresses));
|
} else {
|
||||||
}
|
format!("{}:", interface.name)
|
||||||
|
};
|
||||||
|
|
||||||
// Add IPv6 addresses (no compression for now)
|
|
||||||
if !interface.ipv6_addresses.is_empty() {
|
|
||||||
if !interface.ipv4_addresses.is_empty() {
|
|
||||||
interface_text.push_str(", ");
|
|
||||||
}
|
|
||||||
interface_text.push_str(&interface.ipv6_addresses.join(", "));
|
|
||||||
}
|
|
||||||
|
|
||||||
// Virtual interfaces don't show status icon
|
|
||||||
lines.push(Line::from(vec![
|
lines.push(Line::from(vec![
|
||||||
Span::styled(tree_symbol, Typography::tree()),
|
Span::styled(tree_symbol, Typography::tree()),
|
||||||
Span::styled(interface_text, Typography::secondary()),
|
Span::styled(interface_text, Typography::secondary()),
|
||||||
@@ -710,28 +715,18 @@ impl SystemWidget {
|
|||||||
Span::styled(format!("Agent: {}", agent_version_text), Typography::secondary())
|
Span::styled(format!("Agent: {}", agent_version_text), Typography::secondary())
|
||||||
]));
|
]));
|
||||||
|
|
||||||
// Network section
|
|
||||||
if !self.network_interfaces.is_empty() {
|
|
||||||
lines.push(Line::from(vec![
|
|
||||||
Span::styled("Network:", Typography::widget_title())
|
|
||||||
]));
|
|
||||||
|
|
||||||
let network_lines = self.render_network();
|
|
||||||
lines.extend(network_lines);
|
|
||||||
}
|
|
||||||
|
|
||||||
// CPU section
|
// CPU section
|
||||||
lines.push(Line::from(vec![
|
lines.push(Line::from(vec![
|
||||||
Span::styled("CPU:", Typography::widget_title())
|
Span::styled("CPU:", Typography::widget_title())
|
||||||
]));
|
]));
|
||||||
|
|
||||||
let load_text = self.format_cpu_load();
|
let load_text = self.format_cpu_load();
|
||||||
let cpu_spans = StatusIcons::create_status_spans(
|
let cpu_spans = StatusIcons::create_status_spans(
|
||||||
self.cpu_status.clone(),
|
self.cpu_status.clone(),
|
||||||
&format!("Load: {}", load_text)
|
&format!("Load: {}", load_text)
|
||||||
);
|
);
|
||||||
lines.push(Line::from(cpu_spans));
|
lines.push(Line::from(cpu_spans));
|
||||||
|
|
||||||
let freq_text = self.format_cpu_frequency();
|
let freq_text = self.format_cpu_frequency();
|
||||||
lines.push(Line::from(vec![
|
lines.push(Line::from(vec![
|
||||||
Span::styled(" └─ ", Typography::tree()),
|
Span::styled(" └─ ", Typography::tree()),
|
||||||
@@ -742,7 +737,7 @@ impl SystemWidget {
|
|||||||
lines.push(Line::from(vec![
|
lines.push(Line::from(vec![
|
||||||
Span::styled("RAM:", Typography::widget_title())
|
Span::styled("RAM:", Typography::widget_title())
|
||||||
]));
|
]));
|
||||||
|
|
||||||
let memory_text = self.format_memory_usage();
|
let memory_text = self.format_memory_usage();
|
||||||
let memory_spans = StatusIcons::create_status_spans(
|
let memory_spans = StatusIcons::create_status_spans(
|
||||||
self.memory_status.clone(),
|
self.memory_status.clone(),
|
||||||
@@ -754,16 +749,16 @@ impl SystemWidget {
|
|||||||
for (i, tmpfs) in self.tmpfs_mounts.iter().enumerate() {
|
for (i, tmpfs) in self.tmpfs_mounts.iter().enumerate() {
|
||||||
let is_last = i == self.tmpfs_mounts.len() - 1;
|
let is_last = i == self.tmpfs_mounts.len() - 1;
|
||||||
let tree_symbol = if is_last { " └─ " } else { " ├─ " };
|
let tree_symbol = if is_last { " └─ " } else { " ├─ " };
|
||||||
|
|
||||||
let usage_text = if tmpfs.total_gb > 0.0 {
|
let usage_text = if tmpfs.total_gb > 0.0 {
|
||||||
format!("{:.0}% {:.1}GB/{:.1}GB",
|
format!("{:.0}% {:.1}GB/{:.1}GB",
|
||||||
tmpfs.usage_percent,
|
tmpfs.usage_percent,
|
||||||
tmpfs.used_gb,
|
tmpfs.used_gb,
|
||||||
tmpfs.total_gb)
|
tmpfs.total_gb)
|
||||||
} else {
|
} else {
|
||||||
"— —/—".to_string()
|
"— —/—".to_string()
|
||||||
};
|
};
|
||||||
|
|
||||||
let mut tmpfs_spans = vec![
|
let mut tmpfs_spans = vec![
|
||||||
Span::styled(tree_symbol, Typography::tree()),
|
Span::styled(tree_symbol, Typography::tree()),
|
||||||
];
|
];
|
||||||
@@ -774,6 +769,16 @@ impl SystemWidget {
|
|||||||
lines.push(Line::from(tmpfs_spans));
|
lines.push(Line::from(tmpfs_spans));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Network section
|
||||||
|
if !self.network_interfaces.is_empty() {
|
||||||
|
lines.push(Line::from(vec![
|
||||||
|
Span::styled("Network:", Typography::widget_title())
|
||||||
|
]));
|
||||||
|
|
||||||
|
let network_lines = self.render_network();
|
||||||
|
lines.extend(network_lines);
|
||||||
|
}
|
||||||
|
|
||||||
// Storage section
|
// Storage section
|
||||||
lines.push(Line::from(vec![
|
lines.push(Line::from(vec![
|
||||||
Span::styled("Storage:", Typography::widget_title())
|
Span::styled("Storage:", Typography::widget_title())
|
||||||
|
|||||||
@@ -1,6 +1,6 @@
|
|||||||
[package]
|
[package]
|
||||||
name = "cm-dashboard-shared"
|
name = "cm-dashboard-shared"
|
||||||
version = "0.1.165"
|
version = "0.1.168"
|
||||||
edition = "2021"
|
edition = "2021"
|
||||||
|
|
||||||
[dependencies]
|
[dependencies]
|
||||||
|
|||||||
Reference in New Issue
Block a user