fmt: apply rustfmt
This commit is contained in:
parent
747aef0005
commit
274a485f8d
@ -1,6 +1,10 @@
|
|||||||
//! App state and main loop: input handling, fetching metrics, updating history, and drawing.
|
//! App state and main loop: input handling, fetching metrics, updating history, and drawing.
|
||||||
|
|
||||||
use std::{collections::VecDeque, io, time::{Duration, Instant}};
|
use std::{
|
||||||
|
collections::VecDeque,
|
||||||
|
io,
|
||||||
|
time::{Duration, Instant},
|
||||||
|
};
|
||||||
|
|
||||||
use crossterm::{
|
use crossterm::{
|
||||||
event::{self, Event, KeyCode},
|
event::{self, Event, KeyCode},
|
||||||
@ -16,7 +20,15 @@ use tokio::time::sleep;
|
|||||||
|
|
||||||
use crate::history::{push_capped, PerCoreHistory};
|
use crate::history::{push_capped, PerCoreHistory};
|
||||||
use crate::types::Metrics;
|
use crate::types::Metrics;
|
||||||
use crate::ui::{header::draw_header, cpu::{draw_cpu_avg_graph, draw_per_core_bars}, mem::draw_mem, swap::draw_swap, disks::draw_disks, net::draw_net_spark, processes::draw_top_processes};
|
use crate::ui::{
|
||||||
|
cpu::{draw_cpu_avg_graph, draw_per_core_bars},
|
||||||
|
disks::draw_disks,
|
||||||
|
header::draw_header,
|
||||||
|
mem::draw_mem,
|
||||||
|
net::draw_net_spark,
|
||||||
|
processes::draw_top_processes,
|
||||||
|
swap::draw_swap,
|
||||||
|
};
|
||||||
use crate::ws::{connect, request_metrics};
|
use crate::ws::{connect, request_metrics};
|
||||||
|
|
||||||
pub struct App {
|
pub struct App {
|
||||||
@ -88,7 +100,10 @@ impl App {
|
|||||||
// Input (non-blocking)
|
// Input (non-blocking)
|
||||||
while event::poll(Duration::from_millis(10))? {
|
while event::poll(Duration::from_millis(10))? {
|
||||||
if let Event::Key(k) = event::read()? {
|
if let Event::Key(k) = event::read()? {
|
||||||
if matches!(k.code, KeyCode::Char('q') | KeyCode::Char('Q') | KeyCode::Esc) {
|
if matches!(
|
||||||
|
k.code,
|
||||||
|
KeyCode::Char('q') | KeyCode::Char('Q') | KeyCode::Esc
|
||||||
|
) {
|
||||||
self.should_quit = true;
|
self.should_quit = true;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -130,7 +145,9 @@ impl App {
|
|||||||
let rx = ((rx_total.saturating_sub(prx)) as f64 / dt / 1024.0).round() as u64;
|
let rx = ((rx_total.saturating_sub(prx)) as f64 / dt / 1024.0).round() as u64;
|
||||||
let tx = ((tx_total.saturating_sub(ptx)) as f64 / dt / 1024.0).round() as u64;
|
let tx = ((tx_total.saturating_sub(ptx)) as f64 / dt / 1024.0).round() as u64;
|
||||||
(rx, tx)
|
(rx, tx)
|
||||||
} else { (0, 0) };
|
} else {
|
||||||
|
(0, 0)
|
||||||
|
};
|
||||||
self.last_net_totals = Some((rx_total, tx_total, now));
|
self.last_net_totals = Some((rx_total, tx_total, now));
|
||||||
push_capped(&mut self.rx_hist, rx_kb, 600);
|
push_capped(&mut self.rx_hist, rx_kb, 600);
|
||||||
push_capped(&mut self.tx_hist, tx_kb, 600);
|
push_capped(&mut self.tx_hist, tx_kb, 600);
|
||||||
@ -174,25 +191,37 @@ impl App {
|
|||||||
|
|
||||||
let left_stack = ratatui::layout::Layout::default()
|
let left_stack = ratatui::layout::Layout::default()
|
||||||
.direction(Direction::Vertical)
|
.direction(Direction::Vertical)
|
||||||
.constraints([Constraint::Min(6), Constraint::Length(4), Constraint::Length(4)])
|
.constraints([
|
||||||
|
Constraint::Min(6),
|
||||||
|
Constraint::Length(4),
|
||||||
|
Constraint::Length(4),
|
||||||
|
])
|
||||||
.split(bottom[0]);
|
.split(bottom[0]);
|
||||||
|
|
||||||
draw_disks(f, left_stack[0], self.last_metrics.as_ref());
|
draw_disks(f, left_stack[0], self.last_metrics.as_ref());
|
||||||
draw_net_spark(
|
draw_net_spark(
|
||||||
f,
|
f,
|
||||||
left_stack[1],
|
left_stack[1],
|
||||||
&format!("Download (KB/s) — now: {} | peak: {}", self.rx_hist.back().copied().unwrap_or(0), self.rx_peak),
|
&format!(
|
||||||
|
"Download (KB/s) — now: {} | peak: {}",
|
||||||
|
self.rx_hist.back().copied().unwrap_or(0),
|
||||||
|
self.rx_peak
|
||||||
|
),
|
||||||
&self.rx_hist,
|
&self.rx_hist,
|
||||||
ratatui::style::Color::Green,
|
ratatui::style::Color::Green,
|
||||||
);
|
);
|
||||||
draw_net_spark(
|
draw_net_spark(
|
||||||
f,
|
f,
|
||||||
left_stack[2],
|
left_stack[2],
|
||||||
&format!("Upload (KB/s) — now: {} | peak: {}", self.tx_hist.back().copied().unwrap_or(0), self.tx_peak),
|
&format!(
|
||||||
|
"Upload (KB/s) — now: {} | peak: {}",
|
||||||
|
self.tx_hist.back().copied().unwrap_or(0),
|
||||||
|
self.tx_peak
|
||||||
|
),
|
||||||
&self.tx_hist,
|
&self.tx_hist,
|
||||||
ratatui::style::Color::Blue,
|
ratatui::style::Color::Blue,
|
||||||
);
|
);
|
||||||
|
|
||||||
draw_top_processes(f, bottom[1], self.last_metrics.as_ref());
|
draw_top_processes(f, bottom[1], self.last_metrics.as_ref());
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@ -17,7 +17,10 @@ pub struct PerCoreHistory {
|
|||||||
|
|
||||||
impl PerCoreHistory {
|
impl PerCoreHistory {
|
||||||
pub fn new(cap: usize) -> Self {
|
pub fn new(cap: usize) -> Self {
|
||||||
Self { deques: Vec::new(), cap }
|
Self {
|
||||||
|
deques: Vec::new(),
|
||||||
|
cap,
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// Ensure we have one deque per core; resize on CPU topology changes
|
// Ensure we have one deque per core; resize on CPU topology changes
|
||||||
@ -36,4 +39,4 @@ impl PerCoreHistory {
|
|||||||
push_capped(&mut self.deques[i], val, self.cap);
|
push_capped(&mut self.deques[i], val, self.cap);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@ -6,8 +6,8 @@ mod types;
|
|||||||
mod ui;
|
mod ui;
|
||||||
mod ws;
|
mod ws;
|
||||||
|
|
||||||
use std::env;
|
|
||||||
use app::App;
|
use app::App;
|
||||||
|
use std::env;
|
||||||
|
|
||||||
#[tokio::main]
|
#[tokio::main]
|
||||||
async fn main() -> Result<(), Box<dyn std::error::Error>> {
|
async fn main() -> Result<(), Box<dyn std::error::Error>> {
|
||||||
@ -27,4 +27,4 @@ async fn main() -> Result<(), Box<dyn std::error::Error>> {
|
|||||||
|
|
||||||
let mut app = App::new();
|
let mut app = App::new();
|
||||||
app.run(&url).await
|
app.run(&url).await
|
||||||
}
|
}
|
||||||
|
|||||||
@ -38,4 +38,4 @@ pub struct Metrics {
|
|||||||
pub disks: Vec<Disk>,
|
pub disks: Vec<Disk>,
|
||||||
pub networks: Vec<Network>,
|
pub networks: Vec<Network>,
|
||||||
pub top_processes: Vec<ProcessInfo>,
|
pub top_processes: Vec<ProcessInfo>,
|
||||||
}
|
}
|
||||||
|
|||||||
@ -1,12 +1,12 @@
|
|||||||
//! CPU average sparkline + per-core mini bars.
|
//! CPU average sparkline + per-core mini bars.
|
||||||
|
|
||||||
|
use ratatui::style::Modifier;
|
||||||
use ratatui::{
|
use ratatui::{
|
||||||
layout::{Constraint, Direction, Layout, Rect},
|
layout::{Constraint, Direction, Layout, Rect},
|
||||||
style::{Color, Style},
|
style::{Color, Style},
|
||||||
text::{Line, Span},
|
text::{Line, Span},
|
||||||
widgets::{Block, Borders, Paragraph, Sparkline},
|
widgets::{Block, Borders, Paragraph, Sparkline},
|
||||||
};
|
};
|
||||||
use ratatui::style::Modifier;
|
|
||||||
|
|
||||||
use crate::history::PerCoreHistory;
|
use crate::history::PerCoreHistory;
|
||||||
use crate::types::Metrics;
|
use crate::types::Metrics;
|
||||||
@ -17,7 +17,11 @@ pub fn draw_cpu_avg_graph(
|
|||||||
hist: &std::collections::VecDeque<u64>,
|
hist: &std::collections::VecDeque<u64>,
|
||||||
m: Option<&Metrics>,
|
m: Option<&Metrics>,
|
||||||
) {
|
) {
|
||||||
let title = if let Some(mm) = m { format!("CPU avg (now: {:>5.1}%)", mm.cpu_total) } else { "CPU avg".into() };
|
let title = if let Some(mm) = m {
|
||||||
|
format!("CPU avg (now: {:>5.1}%)", mm.cpu_total)
|
||||||
|
} else {
|
||||||
|
"CPU avg".into()
|
||||||
|
};
|
||||||
let max_points = area.width.saturating_sub(2) as usize;
|
let max_points = area.width.saturating_sub(2) as usize;
|
||||||
let start = hist.len().saturating_sub(max_points);
|
let start = hist.len().saturating_sub(max_points);
|
||||||
let data: Vec<u64> = hist.iter().skip(start).cloned().collect();
|
let data: Vec<u64> = hist.iter().skip(start).cloned().collect();
|
||||||
@ -35,16 +39,31 @@ pub fn draw_per_core_bars(
|
|||||||
m: Option<&Metrics>,
|
m: Option<&Metrics>,
|
||||||
per_core_hist: &PerCoreHistory,
|
per_core_hist: &PerCoreHistory,
|
||||||
) {
|
) {
|
||||||
f.render_widget(Block::default().borders(Borders::ALL).title("Per-core"), area);
|
f.render_widget(
|
||||||
let Some(mm) = m else { return; };
|
Block::default().borders(Borders::ALL).title("Per-core"),
|
||||||
|
area,
|
||||||
|
);
|
||||||
|
let Some(mm) = m else {
|
||||||
|
return;
|
||||||
|
};
|
||||||
|
|
||||||
let inner = Rect { x: area.x + 1, y: area.y + 1, width: area.width.saturating_sub(2), height: area.height.saturating_sub(2) };
|
let inner = Rect {
|
||||||
if inner.height == 0 { return; }
|
x: area.x + 1,
|
||||||
|
y: area.y + 1,
|
||||||
|
width: area.width.saturating_sub(2),
|
||||||
|
height: area.height.saturating_sub(2),
|
||||||
|
};
|
||||||
|
if inner.height == 0 {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
let rows = inner.height as usize;
|
let rows = inner.height as usize;
|
||||||
let show_n = rows.min(mm.cpu_per_core.len());
|
let show_n = rows.min(mm.cpu_per_core.len());
|
||||||
let constraints: Vec<Constraint> = (0..show_n).map(|_| Constraint::Length(1)).collect();
|
let constraints: Vec<Constraint> = (0..show_n).map(|_| Constraint::Length(1)).collect();
|
||||||
let vchunks = Layout::default().direction(Direction::Vertical).constraints(constraints).split(inner);
|
let vchunks = Layout::default()
|
||||||
|
.direction(Direction::Vertical)
|
||||||
|
.constraints(constraints)
|
||||||
|
.split(inner);
|
||||||
|
|
||||||
for i in 0..show_n {
|
for i in 0..show_n {
|
||||||
let rect = vchunks[i];
|
let rect = vchunks[i];
|
||||||
@ -54,13 +73,19 @@ pub fn draw_per_core_bars(
|
|||||||
.split(rect);
|
.split(rect);
|
||||||
|
|
||||||
let curr = mm.cpu_per_core[i].clamp(0.0, 100.0);
|
let curr = mm.cpu_per_core[i].clamp(0.0, 100.0);
|
||||||
let older = per_core_hist.deques.get(i)
|
let older = per_core_hist
|
||||||
|
.deques
|
||||||
|
.get(i)
|
||||||
.and_then(|d| d.iter().rev().nth(20).copied())
|
.and_then(|d| d.iter().rev().nth(20).copied())
|
||||||
.map(|v| v as f32)
|
.map(|v| v as f32)
|
||||||
.unwrap_or(curr);
|
.unwrap_or(curr);
|
||||||
let trend = if curr > older + 0.2 { "↑" }
|
let trend = if curr > older + 0.2 {
|
||||||
else if curr + 0.2 < older { "↓" }
|
"↑"
|
||||||
else { "╌" };
|
} else if curr + 0.2 < older {
|
||||||
|
"↓"
|
||||||
|
} else {
|
||||||
|
"╌"
|
||||||
|
};
|
||||||
|
|
||||||
let fg = match curr {
|
let fg = match curr {
|
||||||
x if x < 25.0 => Color::Green,
|
x if x < 25.0 => Color::Green,
|
||||||
@ -85,7 +110,10 @@ pub fn draw_per_core_bars(
|
|||||||
f.render_widget(spark, hchunks[0]);
|
f.render_widget(spark, hchunks[0]);
|
||||||
|
|
||||||
let label = format!("cpu{:<2}{}{:>5.1}%", i, trend, curr);
|
let label = format!("cpu{:<2}{}{:>5.1}%", i, trend, curr);
|
||||||
let line = Line::from(Span::styled(label, Style::default().fg(fg).add_modifier(Modifier::BOLD)));
|
let line = Line::from(Span::styled(
|
||||||
|
label,
|
||||||
|
Style::default().fg(fg).add_modifier(Modifier::BOLD),
|
||||||
|
));
|
||||||
f.render_widget(Paragraph::new(line).right_aligned(), hchunks[1]);
|
f.render_widget(Paragraph::new(line).right_aligned(), hchunks[1]);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@ -1,16 +1,18 @@
|
|||||||
//! Disk cards with per-device gauge and title line.
|
//! Disk cards with per-device gauge and title line.
|
||||||
|
|
||||||
|
use crate::types::Metrics;
|
||||||
|
use crate::ui::util::{disk_icon, human, truncate_middle};
|
||||||
use ratatui::{
|
use ratatui::{
|
||||||
layout::{Constraint, Direction, Layout, Rect},
|
layout::{Constraint, Direction, Layout, Rect},
|
||||||
style::Style,
|
style::Style,
|
||||||
widgets::{Block, Borders, Gauge},
|
widgets::{Block, Borders, Gauge},
|
||||||
};
|
};
|
||||||
use crate::types::Metrics;
|
|
||||||
use crate::ui::util::{human, truncate_middle, disk_icon};
|
|
||||||
|
|
||||||
pub fn draw_disks(f: &mut ratatui::Frame<'_>, area: Rect, m: Option<&Metrics>) {
|
pub fn draw_disks(f: &mut ratatui::Frame<'_>, area: Rect, m: Option<&Metrics>) {
|
||||||
f.render_widget(Block::default().borders(Borders::ALL).title("Disks"), area);
|
f.render_widget(Block::default().borders(Borders::ALL).title("Disks"), area);
|
||||||
let Some(mm) = m else { return; };
|
let Some(mm) = m else {
|
||||||
|
return;
|
||||||
|
};
|
||||||
|
|
||||||
let inner = Rect {
|
let inner = Rect {
|
||||||
x: area.x + 1,
|
x: area.x + 1,
|
||||||
@ -18,12 +20,16 @@ pub fn draw_disks(f: &mut ratatui::Frame<'_>, area: Rect, m: Option<&Metrics>) {
|
|||||||
width: area.width.saturating_sub(2),
|
width: area.width.saturating_sub(2),
|
||||||
height: area.height.saturating_sub(2),
|
height: area.height.saturating_sub(2),
|
||||||
};
|
};
|
||||||
if inner.height < 3 { return; }
|
if inner.height < 3 {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
let per_disk_h = 3u16;
|
let per_disk_h = 3u16;
|
||||||
let max_cards = (inner.height / per_disk_h).min(mm.disks.len() as u16) as usize;
|
let max_cards = (inner.height / per_disk_h).min(mm.disks.len() as u16) as usize;
|
||||||
|
|
||||||
let constraints: Vec<Constraint> = (0..max_cards).map(|_| Constraint::Length(per_disk_h)).collect();
|
let constraints: Vec<Constraint> = (0..max_cards)
|
||||||
|
.map(|_| Constraint::Length(per_disk_h))
|
||||||
|
.collect();
|
||||||
let rows = Layout::default()
|
let rows = Layout::default()
|
||||||
.direction(Direction::Vertical)
|
.direction(Direction::Vertical)
|
||||||
.constraints(constraints)
|
.constraints(constraints)
|
||||||
@ -32,10 +38,20 @@ pub fn draw_disks(f: &mut ratatui::Frame<'_>, area: Rect, m: Option<&Metrics>) {
|
|||||||
for (i, slot) in rows.iter().enumerate() {
|
for (i, slot) in rows.iter().enumerate() {
|
||||||
let d = &mm.disks[i];
|
let d = &mm.disks[i];
|
||||||
let used = d.total.saturating_sub(d.available);
|
let used = d.total.saturating_sub(d.available);
|
||||||
let ratio = if d.total > 0 { used as f64 / d.total as f64 } else { 0.0 };
|
let ratio = if d.total > 0 {
|
||||||
|
used as f64 / d.total as f64
|
||||||
|
} else {
|
||||||
|
0.0
|
||||||
|
};
|
||||||
let pct = (ratio * 100.0).round() as u16;
|
let pct = (ratio * 100.0).round() as u16;
|
||||||
|
|
||||||
let color = if pct < 70 { ratatui::style::Color::Green } else if pct < 90 { ratatui::style::Color::Yellow } else { ratatui::style::Color::Red };
|
let color = if pct < 70 {
|
||||||
|
ratatui::style::Color::Green
|
||||||
|
} else if pct < 90 {
|
||||||
|
ratatui::style::Color::Yellow
|
||||||
|
} else {
|
||||||
|
ratatui::style::Color::Red
|
||||||
|
};
|
||||||
|
|
||||||
let title = format!(
|
let title = format!(
|
||||||
"{} {} {} / {} ({}%)",
|
"{} {} {} / {} ({}%)",
|
||||||
@ -55,7 +71,9 @@ pub fn draw_disks(f: &mut ratatui::Frame<'_>, area: Rect, m: Option<&Metrics>) {
|
|||||||
width: slot.width.saturating_sub(2),
|
width: slot.width.saturating_sub(2),
|
||||||
height: slot.height.saturating_sub(2),
|
height: slot.height.saturating_sub(2),
|
||||||
};
|
};
|
||||||
if inner_card.height == 0 { continue; }
|
if inner_card.height == 0 {
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
let gauge_rect = Rect {
|
let gauge_rect = Rect {
|
||||||
x: inner_card.x,
|
x: inner_card.x,
|
||||||
@ -70,4 +88,4 @@ pub fn draw_disks(f: &mut ratatui::Frame<'_>, area: Rect, m: Option<&Metrics>) {
|
|||||||
|
|
||||||
f.render_widget(g, gauge_rect);
|
f.render_widget(g, gauge_rect);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@ -1,20 +1,32 @@
|
|||||||
//! Top header with hostname and CPU temperature indicator.
|
//! Top header with hostname and CPU temperature indicator.
|
||||||
|
|
||||||
|
use crate::types::Metrics;
|
||||||
use ratatui::{
|
use ratatui::{
|
||||||
layout::Rect,
|
layout::Rect,
|
||||||
widgets::{Block, Borders},
|
widgets::{Block, Borders},
|
||||||
};
|
};
|
||||||
use crate::types::Metrics;
|
|
||||||
|
|
||||||
pub fn draw_header(f: &mut ratatui::Frame<'_>, area: Rect, m: Option<&Metrics>) {
|
pub fn draw_header(f: &mut ratatui::Frame<'_>, area: Rect, m: Option<&Metrics>) {
|
||||||
let title = if let Some(mm) = m {
|
let title = if let Some(mm) = m {
|
||||||
let temp = mm.cpu_temp_c.map(|t| {
|
let temp = mm
|
||||||
let icon = if t < 50.0 { "😎" } else if t < 85.0 { "⚠️" } else { "🔥" };
|
.cpu_temp_c
|
||||||
format!("CPU Temp: {:.1}°C {}", t, icon)
|
.map(|t| {
|
||||||
}).unwrap_or_else(|| "CPU Temp: N/A".into());
|
let icon = if t < 50.0 {
|
||||||
format!("socktop — host: {} | {} (press 'q' to quit)", mm.hostname, temp)
|
"😎"
|
||||||
|
} else if t < 85.0 {
|
||||||
|
"⚠️"
|
||||||
|
} else {
|
||||||
|
"🔥"
|
||||||
|
};
|
||||||
|
format!("CPU Temp: {:.1}°C {}", t, icon)
|
||||||
|
})
|
||||||
|
.unwrap_or_else(|| "CPU Temp: N/A".into());
|
||||||
|
format!(
|
||||||
|
"socktop — host: {} | {} (press 'q' to quit)",
|
||||||
|
mm.hostname, temp
|
||||||
|
)
|
||||||
} else {
|
} else {
|
||||||
"socktop — connecting... (press 'q' to quit)".into()
|
"socktop — connecting... (press 'q' to quit)".into()
|
||||||
};
|
};
|
||||||
f.render_widget(Block::default().title(title).borders(Borders::BOTTOM), area);
|
f.render_widget(Block::default().title(title).borders(Borders::BOTTOM), area);
|
||||||
}
|
}
|
||||||
|
|||||||
@ -1,18 +1,24 @@
|
|||||||
//! Memory gauge.
|
//! Memory gauge.
|
||||||
|
|
||||||
|
use crate::types::Metrics;
|
||||||
|
use crate::ui::util::human;
|
||||||
use ratatui::{
|
use ratatui::{
|
||||||
layout::Rect,
|
layout::Rect,
|
||||||
style::{Color, Style},
|
style::{Color, Style},
|
||||||
widgets::{Block, Borders, Gauge},
|
widgets::{Block, Borders, Gauge},
|
||||||
};
|
};
|
||||||
use crate::types::Metrics;
|
|
||||||
use crate::ui::util::human;
|
|
||||||
|
|
||||||
pub fn draw_mem(f: &mut ratatui::Frame<'_>, area: Rect, m: Option<&Metrics>) {
|
pub fn draw_mem(f: &mut ratatui::Frame<'_>, area: Rect, m: Option<&Metrics>) {
|
||||||
let (used, total, pct) = if let Some(mm) = m {
|
let (used, total, pct) = if let Some(mm) = m {
|
||||||
let pct = if mm.mem_total > 0 { (mm.mem_used as f64 / mm.mem_total as f64 * 100.0) as u16 } else { 0 };
|
let pct = if mm.mem_total > 0 {
|
||||||
|
(mm.mem_used as f64 / mm.mem_total as f64 * 100.0) as u16
|
||||||
|
} else {
|
||||||
|
0
|
||||||
|
};
|
||||||
(mm.mem_used, mm.mem_total, pct)
|
(mm.mem_used, mm.mem_total, pct)
|
||||||
} else { (0, 0, 0) };
|
} else {
|
||||||
|
(0, 0, 0)
|
||||||
|
};
|
||||||
|
|
||||||
let g = Gauge::default()
|
let g = Gauge::default()
|
||||||
.block(Block::default().borders(Borders::ALL).title("Memory"))
|
.block(Block::default().borders(Borders::ALL).title("Memory"))
|
||||||
@ -20,4 +26,4 @@ pub fn draw_mem(f: &mut ratatui::Frame<'_>, area: Rect, m: Option<&Metrics>) {
|
|||||||
.percent(pct)
|
.percent(pct)
|
||||||
.label(format!("{} / {}", human(used), human(total)));
|
.label(format!("{} / {}", human(used), human(total)));
|
||||||
f.render_widget(g, area);
|
f.render_widget(g, area);
|
||||||
}
|
}
|
||||||
|
|||||||
@ -1,10 +1,10 @@
|
|||||||
//! UI module root: exposes drawing functions for individual panels.
|
//! UI module root: exposes drawing functions for individual panels.
|
||||||
|
|
||||||
pub mod header;
|
|
||||||
pub mod cpu;
|
pub mod cpu;
|
||||||
pub mod mem;
|
|
||||||
pub mod swap;
|
|
||||||
pub mod disks;
|
pub mod disks;
|
||||||
|
pub mod header;
|
||||||
|
pub mod mem;
|
||||||
pub mod net;
|
pub mod net;
|
||||||
pub mod processes;
|
pub mod processes;
|
||||||
pub mod util;
|
pub mod swap;
|
||||||
|
pub mod util;
|
||||||
|
|||||||
@ -1,11 +1,11 @@
|
|||||||
//! Network sparklines (download/upload).
|
//! Network sparklines (download/upload).
|
||||||
|
|
||||||
use std::collections::VecDeque;
|
|
||||||
use ratatui::{
|
use ratatui::{
|
||||||
layout::Rect,
|
layout::Rect,
|
||||||
style::{Color, Style},
|
style::{Color, Style},
|
||||||
widgets::{Block, Borders, Sparkline},
|
widgets::{Block, Borders, Sparkline},
|
||||||
};
|
};
|
||||||
|
use std::collections::VecDeque;
|
||||||
|
|
||||||
pub fn draw_net_spark(
|
pub fn draw_net_spark(
|
||||||
f: &mut ratatui::Frame<'_>,
|
f: &mut ratatui::Frame<'_>,
|
||||||
@ -19,8 +19,12 @@ pub fn draw_net_spark(
|
|||||||
let data: Vec<u64> = hist.iter().skip(start).cloned().collect();
|
let data: Vec<u64> = hist.iter().skip(start).cloned().collect();
|
||||||
|
|
||||||
let spark = Sparkline::default()
|
let spark = Sparkline::default()
|
||||||
.block(Block::default().borders(Borders::ALL).title(title.to_string()))
|
.block(
|
||||||
|
Block::default()
|
||||||
|
.borders(Borders::ALL)
|
||||||
|
.title(title.to_string()),
|
||||||
|
)
|
||||||
.data(&data)
|
.data(&data)
|
||||||
.style(Style::default().fg(color));
|
.style(Style::default().fg(color));
|
||||||
f.render_widget(spark, area);
|
f.render_widget(spark, area);
|
||||||
}
|
}
|
||||||
|
|||||||
@ -1,71 +1,94 @@
|
|||||||
//! Top processes table with per-cell coloring and zebra striping.
|
//! Top processes table with per-cell coloring and zebra striping.
|
||||||
|
|
||||||
|
use ratatui::style::Modifier;
|
||||||
use ratatui::{
|
use ratatui::{
|
||||||
layout::{Constraint, Rect},
|
layout::{Constraint, Rect},
|
||||||
style::{Color, Style},
|
style::{Color, Style},
|
||||||
widgets::{Block, Borders, Cell, Row, Table},
|
widgets::{Block, Borders, Cell, Row, Table},
|
||||||
};
|
};
|
||||||
use ratatui::style::Modifier;
|
|
||||||
|
|
||||||
use crate::types::Metrics;
|
use crate::types::Metrics;
|
||||||
use crate::ui::util::human;
|
use crate::ui::util::human;
|
||||||
|
|
||||||
pub fn draw_top_processes(f: &mut ratatui::Frame<'_>, area: Rect, m: Option<&Metrics>) {
|
pub fn draw_top_processes(f: &mut ratatui::Frame<'_>, area: Rect, m: Option<&Metrics>) {
|
||||||
let Some(mm) = m else {
|
let Some(mm) = m else {
|
||||||
f.render_widget(Block::default().borders(Borders::ALL).title("Top Processes"), area);
|
f.render_widget(
|
||||||
|
Block::default()
|
||||||
|
.borders(Borders::ALL)
|
||||||
|
.title("Top Processes"),
|
||||||
|
area,
|
||||||
|
);
|
||||||
return;
|
return;
|
||||||
};
|
};
|
||||||
|
|
||||||
let total_mem_bytes = mm.mem_total.max(1);
|
let total_mem_bytes = mm.mem_total.max(1);
|
||||||
let title = format!("Top Processes ({} total)", mm.process_count);
|
let title = format!("Top Processes ({} total)", mm.process_count);
|
||||||
let peak_cpu = mm.top_processes.iter().map(|p| p.cpu_usage).fold(0.0_f32, f32::max);
|
let peak_cpu = mm
|
||||||
|
.top_processes
|
||||||
|
.iter()
|
||||||
|
.map(|p| p.cpu_usage)
|
||||||
|
.fold(0.0_f32, f32::max);
|
||||||
|
|
||||||
let rows: Vec<Row> = mm.top_processes.iter().enumerate().map(|(i, p)| {
|
let rows: Vec<Row> = mm
|
||||||
let mem_pct = (p.mem_bytes as f64 / total_mem_bytes as f64) * 100.0;
|
.top_processes
|
||||||
|
.iter()
|
||||||
|
.enumerate()
|
||||||
|
.map(|(i, p)| {
|
||||||
|
let mem_pct = (p.mem_bytes as f64 / total_mem_bytes as f64) * 100.0;
|
||||||
|
|
||||||
let cpu_fg = match p.cpu_usage {
|
let cpu_fg = match p.cpu_usage {
|
||||||
x if x < 25.0 => Color::Green,
|
x if x < 25.0 => Color::Green,
|
||||||
x if x < 60.0 => Color::Yellow,
|
x if x < 60.0 => Color::Yellow,
|
||||||
_ => Color::Red,
|
_ => Color::Red,
|
||||||
};
|
};
|
||||||
let mem_fg = match mem_pct {
|
let mem_fg = match mem_pct {
|
||||||
x if x < 5.0 => Color::Blue,
|
x if x < 5.0 => Color::Blue,
|
||||||
x if x < 20.0 => Color::Magenta,
|
x if x < 20.0 => Color::Magenta,
|
||||||
_ => Color::Red,
|
_ => Color::Red,
|
||||||
};
|
};
|
||||||
|
|
||||||
let zebra = if i % 2 == 0 { Style::default().fg(Color::Gray) } else { Style::default() };
|
let zebra = if i % 2 == 0 {
|
||||||
|
Style::default().fg(Color::Gray)
|
||||||
|
} else {
|
||||||
|
Style::default()
|
||||||
|
};
|
||||||
|
|
||||||
let emphasis = if (p.cpu_usage - peak_cpu).abs() < f32::EPSILON {
|
let emphasis = if (p.cpu_usage - peak_cpu).abs() < f32::EPSILON {
|
||||||
Style::default().add_modifier(Modifier::BOLD)
|
Style::default().add_modifier(Modifier::BOLD)
|
||||||
} else { Style::default() };
|
} else {
|
||||||
|
Style::default()
|
||||||
|
};
|
||||||
|
|
||||||
Row::new(vec![
|
Row::new(vec![
|
||||||
Cell::from(p.pid.to_string()).style(Style::default().fg(Color::DarkGray)),
|
Cell::from(p.pid.to_string()).style(Style::default().fg(Color::DarkGray)),
|
||||||
Cell::from(p.name.clone()),
|
Cell::from(p.name.clone()),
|
||||||
Cell::from(format!("{:.1}%", p.cpu_usage)).style(Style::default().fg(cpu_fg)),
|
Cell::from(format!("{:.1}%", p.cpu_usage)).style(Style::default().fg(cpu_fg)),
|
||||||
Cell::from(human(p.mem_bytes)),
|
Cell::from(human(p.mem_bytes)),
|
||||||
Cell::from(format!("{:.2}%", mem_pct)).style(Style::default().fg(mem_fg)),
|
Cell::from(format!("{:.2}%", mem_pct)).style(Style::default().fg(mem_fg)),
|
||||||
])
|
])
|
||||||
.style(zebra.patch(emphasis))
|
.style(zebra.patch(emphasis))
|
||||||
}).collect();
|
})
|
||||||
|
.collect();
|
||||||
|
|
||||||
let header = Row::new(vec!["PID", "Name", "CPU %", "Mem", "Mem %"])
|
let header = Row::new(vec!["PID", "Name", "CPU %", "Mem", "Mem %"]).style(
|
||||||
.style(Style::default().fg(Color::Cyan).add_modifier(Modifier::BOLD));
|
Style::default()
|
||||||
|
.fg(Color::Cyan)
|
||||||
|
.add_modifier(Modifier::BOLD),
|
||||||
|
);
|
||||||
|
|
||||||
let table = Table::new(
|
let table = Table::new(
|
||||||
rows,
|
rows,
|
||||||
vec![
|
vec![
|
||||||
Constraint::Length(8),
|
Constraint::Length(8),
|
||||||
Constraint::Percentage(40),
|
Constraint::Percentage(40),
|
||||||
Constraint::Length(8),
|
Constraint::Length(8),
|
||||||
Constraint::Length(12),
|
Constraint::Length(12),
|
||||||
Constraint::Length(8),
|
Constraint::Length(8),
|
||||||
],
|
],
|
||||||
)
|
)
|
||||||
.header(header)
|
.header(header)
|
||||||
.column_spacing(1)
|
.column_spacing(1)
|
||||||
.block(Block::default().borders(Borders::ALL).title(title));
|
.block(Block::default().borders(Borders::ALL).title(title));
|
||||||
|
|
||||||
f.render_widget(table, area);
|
f.render_widget(table, area);
|
||||||
}
|
}
|
||||||
|
|||||||
@ -1,18 +1,24 @@
|
|||||||
//! Swap gauge.
|
//! Swap gauge.
|
||||||
|
|
||||||
|
use crate::types::Metrics;
|
||||||
|
use crate::ui::util::human;
|
||||||
use ratatui::{
|
use ratatui::{
|
||||||
layout::Rect,
|
layout::Rect,
|
||||||
style::{Color, Style},
|
style::{Color, Style},
|
||||||
widgets::{Block, Borders, Gauge},
|
widgets::{Block, Borders, Gauge},
|
||||||
};
|
};
|
||||||
use crate::types::Metrics;
|
|
||||||
use crate::ui::util::human;
|
|
||||||
|
|
||||||
pub fn draw_swap(f: &mut ratatui::Frame<'_>, area: Rect, m: Option<&Metrics>) {
|
pub fn draw_swap(f: &mut ratatui::Frame<'_>, area: Rect, m: Option<&Metrics>) {
|
||||||
let (used, total, pct) = if let Some(mm) = m {
|
let (used, total, pct) = if let Some(mm) = m {
|
||||||
let pct = if mm.swap_total > 0 { (mm.swap_used as f64 / mm.swap_total as f64 * 100.0) as u16 } else { 0 };
|
let pct = if mm.swap_total > 0 {
|
||||||
|
(mm.swap_used as f64 / mm.swap_total as f64 * 100.0) as u16
|
||||||
|
} else {
|
||||||
|
0
|
||||||
|
};
|
||||||
(mm.swap_used, mm.swap_total, pct)
|
(mm.swap_used, mm.swap_total, pct)
|
||||||
} else { (0, 0, 0) };
|
} else {
|
||||||
|
(0, 0, 0)
|
||||||
|
};
|
||||||
|
|
||||||
let g = Gauge::default()
|
let g = Gauge::default()
|
||||||
.block(Block::default().borders(Borders::ALL).title("Swap"))
|
.block(Block::default().borders(Borders::ALL).title("Swap"))
|
||||||
@ -20,4 +26,4 @@ pub fn draw_swap(f: &mut ratatui::Frame<'_>, area: Rect, m: Option<&Metrics>) {
|
|||||||
.percent(pct)
|
.percent(pct)
|
||||||
.label(format!("{} / {}", human(used), human(total)));
|
.label(format!("{} / {}", human(used), human(total)));
|
||||||
f.render_widget(g, area);
|
f.render_widget(g, area);
|
||||||
}
|
}
|
||||||
|
|||||||
@ -3,31 +3,49 @@
|
|||||||
pub fn human(b: u64) -> String {
|
pub fn human(b: u64) -> String {
|
||||||
const K: f64 = 1024.0;
|
const K: f64 = 1024.0;
|
||||||
let b = b as f64;
|
let b = b as f64;
|
||||||
if b < K { return format!("{b:.0}B"); }
|
if b < K {
|
||||||
|
return format!("{b:.0}B");
|
||||||
|
}
|
||||||
let kb = b / K;
|
let kb = b / K;
|
||||||
if kb < K { return format!("{kb:.1}KB"); }
|
if kb < K {
|
||||||
|
return format!("{kb:.1}KB");
|
||||||
|
}
|
||||||
let mb = kb / K;
|
let mb = kb / K;
|
||||||
if mb < K { return format!("{mb:.1}MB"); }
|
if mb < K {
|
||||||
|
return format!("{mb:.1}MB");
|
||||||
|
}
|
||||||
let gb = mb / K;
|
let gb = mb / K;
|
||||||
if gb < K { return format!("{gb:.1}GB"); }
|
if gb < K {
|
||||||
|
return format!("{gb:.1}GB");
|
||||||
|
}
|
||||||
let tb = gb / K;
|
let tb = gb / K;
|
||||||
format!("{tb:.2}TB")
|
format!("{tb:.2}TB")
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn truncate_middle(s: &str, max: usize) -> String {
|
pub fn truncate_middle(s: &str, max: usize) -> String {
|
||||||
if s.len() <= max { return s.to_string(); }
|
if s.len() <= max {
|
||||||
if max <= 3 { return "...".into(); }
|
return s.to_string();
|
||||||
|
}
|
||||||
|
if max <= 3 {
|
||||||
|
return "...".into();
|
||||||
|
}
|
||||||
let keep = max - 3;
|
let keep = max - 3;
|
||||||
let left = keep / 2;
|
let left = keep / 2;
|
||||||
let right = keep - left;
|
let right = keep - left;
|
||||||
format!("{}...{}", &s[..left], &s[s.len()-right..])
|
format!("{}...{}", &s[..left], &s[s.len() - right..])
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn disk_icon(name: &str) -> &'static str {
|
pub fn disk_icon(name: &str) -> &'static str {
|
||||||
let n = name.to_ascii_lowercase();
|
let n = name.to_ascii_lowercase();
|
||||||
if n.contains(':') { "🗄️" }
|
if n.contains(':') {
|
||||||
else if n.contains("nvme") { "⚡" }
|
"🗄️"
|
||||||
else if n.starts_with("sd") { "💽" }
|
} else if n.contains("nvme") {
|
||||||
else if n.contains("overlay") { "📦" }
|
"⚡"
|
||||||
else { "🖴" }
|
} else if n.starts_with("sd") {
|
||||||
}
|
"💽"
|
||||||
|
} else if n.contains("overlay") {
|
||||||
|
"📦"
|
||||||
|
} else {
|
||||||
|
"🖴"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|||||||
@ -25,4 +25,4 @@ pub async fn request_metrics(ws: &mut WsStream) -> Option<Metrics> {
|
|||||||
}
|
}
|
||||||
|
|
||||||
// Re-export SinkExt/StreamExt for call sites
|
// Re-export SinkExt/StreamExt for call sites
|
||||||
use futures_util::{SinkExt, StreamExt};
|
use futures_util::{SinkExt, StreamExt};
|
||||||
|
|||||||
@ -4,25 +4,26 @@
|
|||||||
mod metrics;
|
mod metrics;
|
||||||
mod sampler;
|
mod sampler;
|
||||||
mod state;
|
mod state;
|
||||||
mod ws;
|
|
||||||
mod types;
|
mod types;
|
||||||
|
mod ws;
|
||||||
|
|
||||||
use axum::{routing::get, Router};
|
use axum::{routing::get, Router};
|
||||||
use std::{collections::HashMap, net::SocketAddr, sync::Arc, time::Duration, sync::atomic::AtomicUsize};
|
use std::{
|
||||||
use sysinfo::{
|
collections::HashMap, net::SocketAddr, sync::atomic::AtomicUsize, sync::Arc, time::Duration,
|
||||||
Components, CpuRefreshKind, Disks, MemoryRefreshKind, Networks, ProcessRefreshKind, RefreshKind,
|
|
||||||
System,
|
|
||||||
};
|
};
|
||||||
use tokio::sync::{Mutex, RwLock, Notify};
|
use sysinfo::{
|
||||||
|
Components, CpuRefreshKind, Disks, MemoryRefreshKind, Networks, ProcessRefreshKind,
|
||||||
|
RefreshKind, System,
|
||||||
|
};
|
||||||
|
use tokio::sync::{Mutex, Notify, RwLock};
|
||||||
use tracing_subscriber::EnvFilter;
|
use tracing_subscriber::EnvFilter;
|
||||||
|
|
||||||
use state::{AppState, SharedTotals};
|
|
||||||
use sampler::spawn_sampler;
|
use sampler::spawn_sampler;
|
||||||
|
use state::{AppState, SharedTotals};
|
||||||
use ws::ws_handler;
|
use ws::ws_handler;
|
||||||
|
|
||||||
#[tokio::main]
|
#[tokio::main]
|
||||||
async fn main() {
|
async fn main() {
|
||||||
|
|
||||||
// Init logging; configure with RUST_LOG (e.g., RUST_LOG=info).
|
// Init logging; configure with RUST_LOG (e.g., RUST_LOG=info).
|
||||||
tracing_subscriber::fmt()
|
tracing_subscriber::fmt()
|
||||||
.with_env_filter(EnvFilter::from_default_env())
|
.with_env_filter(EnvFilter::from_default_env())
|
||||||
@ -60,7 +61,9 @@ async fn main() {
|
|||||||
// new: adaptive sampling controls
|
// new: adaptive sampling controls
|
||||||
client_count: Arc::new(AtomicUsize::new(0)),
|
client_count: Arc::new(AtomicUsize::new(0)),
|
||||||
wake_sampler: Arc::new(Notify::new()),
|
wake_sampler: Arc::new(Notify::new()),
|
||||||
auth_token: std::env::var("SOCKTOP_TOKEN").ok().filter(|s| !s.is_empty()),
|
auth_token: std::env::var("SOCKTOP_TOKEN")
|
||||||
|
.ok()
|
||||||
|
.filter(|s| !s.is_empty()),
|
||||||
};
|
};
|
||||||
|
|
||||||
// Start background sampler (adjust cadence as needed)
|
// Start background sampler (adjust cadence as needed)
|
||||||
@ -68,7 +71,9 @@ async fn main() {
|
|||||||
|
|
||||||
// Web app
|
// Web app
|
||||||
let port = resolve_port();
|
let port = resolve_port();
|
||||||
let app = Router::new().route("/ws", get(ws_handler)).with_state(state);
|
let app = Router::new()
|
||||||
|
.route("/ws", get(ws_handler))
|
||||||
|
.with_state(state);
|
||||||
|
|
||||||
let addr = SocketAddr::from(([0, 0, 0, 0], port));
|
let addr = SocketAddr::from(([0, 0, 0, 0], port));
|
||||||
|
|
||||||
@ -82,7 +87,6 @@ async fn main() {
|
|||||||
|
|
||||||
let listener = tokio::net::TcpListener::bind(addr).await.unwrap();
|
let listener = tokio::net::TcpListener::bind(addr).await.unwrap();
|
||||||
axum::serve(listener, app).await.unwrap();
|
axum::serve(listener, app).await.unwrap();
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// Resolve the listening port from CLI args/env with a 3000 default.
|
// Resolve the listening port from CLI args/env with a 3000 default.
|
||||||
@ -97,7 +101,10 @@ fn resolve_port() -> u16 {
|
|||||||
return p;
|
return p;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
eprintln!("Warning: invalid SOCKTOP_PORT='{}'; using default {}", s, DEFAULT);
|
eprintln!(
|
||||||
|
"Warning: invalid SOCKTOP_PORT='{}'; using default {}",
|
||||||
|
s, DEFAULT
|
||||||
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
let mut args = std::env::args().skip(1);
|
let mut args = std::env::args().skip(1);
|
||||||
@ -133,4 +140,3 @@ fn resolve_port() -> u16 {
|
|||||||
|
|
||||||
DEFAULT
|
DEFAULT
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@ -121,8 +121,11 @@ pub fn best_cpu_temp(components: &Components) -> Option<f32> {
|
|||||||
.iter()
|
.iter()
|
||||||
.filter(|c| {
|
.filter(|c| {
|
||||||
let label = c.label().to_lowercase();
|
let label = c.label().to_lowercase();
|
||||||
label.contains("cpu") || label.contains("package") || label.contains("tctl") || label.contains("tdie")
|
label.contains("cpu")
|
||||||
|
|| label.contains("package")
|
||||||
|
|| label.contains("tctl")
|
||||||
|
|| label.contains("tdie")
|
||||||
})
|
})
|
||||||
.filter_map(|c| c.temperature())
|
.filter_map(|c| c.temperature())
|
||||||
.max_by(|a, b| a.partial_cmp(b).unwrap_or(std::cmp::Ordering::Equal))
|
.max_by(|a, b| a.partial_cmp(b).unwrap_or(std::cmp::Ordering::Equal))
|
||||||
}
|
}
|
||||||
|
|||||||
@ -5,13 +5,16 @@ use crate::metrics::collect_metrics;
|
|||||||
use crate::state::AppState;
|
use crate::state::AppState;
|
||||||
//use serde_json::to_string;
|
//use serde_json::to_string;
|
||||||
use tokio::task::JoinHandle;
|
use tokio::task::JoinHandle;
|
||||||
use tokio::time::{Duration, interval, MissedTickBehavior};
|
use tokio::time::{interval, Duration, MissedTickBehavior};
|
||||||
|
|
||||||
pub fn spawn_sampler(state: AppState, period: Duration) -> JoinHandle<()> {
|
pub fn spawn_sampler(state: AppState, period: Duration) -> JoinHandle<()> {
|
||||||
tokio::spawn(async move {
|
tokio::spawn(async move {
|
||||||
let idle_period = Duration::from_secs(10);
|
let idle_period = Duration::from_secs(10);
|
||||||
loop {
|
loop {
|
||||||
let active = state.client_count.load(std::sync::atomic::Ordering::Relaxed) > 0;
|
let active = state
|
||||||
|
.client_count
|
||||||
|
.load(std::sync::atomic::Ordering::Relaxed)
|
||||||
|
> 0;
|
||||||
let mut ticker = interval(if active { period } else { idle_period });
|
let mut ticker = interval(if active { period } else { idle_period });
|
||||||
ticker.set_missed_tick_behavior(MissedTickBehavior::Skip);
|
ticker.set_missed_tick_behavior(MissedTickBehavior::Skip);
|
||||||
ticker.tick().await;
|
ticker.tick().await;
|
||||||
@ -33,4 +36,4 @@ pub fn spawn_sampler(state: AppState, period: Duration) -> JoinHandle<()> {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|||||||
@ -1,9 +1,9 @@
|
|||||||
//! Shared agent state: sysinfo handles and hot JSON cache.
|
//! Shared agent state: sysinfo handles and hot JSON cache.
|
||||||
|
|
||||||
use std::{collections::HashMap, sync::Arc};
|
|
||||||
use std::sync::atomic::AtomicUsize;
|
use std::sync::atomic::AtomicUsize;
|
||||||
|
use std::{collections::HashMap, sync::Arc};
|
||||||
use sysinfo::{Components, Disks, Networks, System};
|
use sysinfo::{Components, Disks, Networks, System};
|
||||||
use tokio::sync::{Mutex, RwLock, Notify};
|
use tokio::sync::{Mutex, Notify, RwLock};
|
||||||
|
|
||||||
pub type SharedSystem = Arc<Mutex<System>>;
|
pub type SharedSystem = Arc<Mutex<System>>;
|
||||||
pub type SharedNetworks = Arc<Mutex<Networks>>;
|
pub type SharedNetworks = Arc<Mutex<Networks>>;
|
||||||
@ -27,4 +27,4 @@ pub struct AppState {
|
|||||||
pub client_count: Arc<AtomicUsize>,
|
pub client_count: Arc<AtomicUsize>,
|
||||||
pub wake_sampler: Arc<Notify>,
|
pub wake_sampler: Arc<Notify>,
|
||||||
pub auth_token: Option<String>,
|
pub auth_token: Option<String>,
|
||||||
}
|
}
|
||||||
|
|||||||
@ -40,4 +40,4 @@ pub struct Metrics {
|
|||||||
pub disks: Vec<DiskInfo>,
|
pub disks: Vec<DiskInfo>,
|
||||||
pub networks: Vec<NetworkInfo>,
|
pub networks: Vec<NetworkInfo>,
|
||||||
pub top_processes: Vec<ProcessInfo>,
|
pub top_processes: Vec<ProcessInfo>,
|
||||||
}
|
}
|
||||||
|
|||||||
@ -63,4 +63,4 @@ async fn handle_socket(mut socket: WebSocket, state: AppState) {
|
|||||||
_ => {}
|
_ => {}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
Loading…
Reference in New Issue
Block a user