Tray UI: start hidden, fixed window, grid layout, traffic + peers
Reworks the tray window per feedback and fills it in. - Starts hidden in the tray (no window on launch); the window opens from the menu. On macOS the app now uses the Accessory activation policy, so it lives in the menu bar with no Dock icon (via a winit event-loop hook, pinned to eframe's winit). Fixed, non-resizable 360x600 window; the layout targets it. - Tidied layout: top bar (title + refresh), a bottom panel with the join form and the action banner, and a scrolling list of network tiles between them. Labels and values sit on two-column grids so they align; the join name and secret inputs are the same fixed width. - A ⟳ button beside the secret generates a fresh random secret (NetworkSecret::generate), for creating a network. - Each network tile shows id, range, this device's address, peers (connected/known), the active and broadcast switches, a Leave button, and a collapsing "peers (N)" section with a formatted per-peer table (short key, address, handshake age, tx/rx packets, rate). - Traffic: a new sampler turns the agent's cumulative packet counters into a packets-per-second rate per network and per peer, from the deltas between snapshots (the snapshot now carries a generation and timestamp so each refresh is sampled once). The network tile shows the current rate and a small hand-drawn sparkline of recent rates (no extra dependency). Note: rates are packets/s — the report has no byte counters yet; bytes would need an agent change. Builds and is clippy-clean (-D warnings) on macOS; the UI has not been run in this headless session. Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
This commit is contained in:
1 parent
19509dc833
commit
d1de61ba29
7 files changed
+416
-75
No files matched your search
@@ -5,6 +5,7 @@ use std::time::Duration;
|
||||
use eframe::egui;
|
||||
|
||||
use crate::agent::{AgentClient, resolve_socket};
|
||||
use crate::stats::Traffic;
|
||||
use crate::tray::{Action, Tray};
|
||||
use crate::ui::{self, JoinForm};
|
||||
|
||||
@@ -13,6 +14,11 @@ pub(crate) struct App {
|
||||
agent: AgentClient,
|
||||
tray: Tray,
|
||||
join: JoinForm,
|
||||
/// Traffic rates derived from successive snapshots.
|
||||
traffic: Traffic,
|
||||
/// The snapshot generation last folded into `traffic`, so each refresh is
|
||||
/// sampled exactly once rather than on every repaint.
|
||||
sampled: u64,
|
||||
/// Set when the user chose Quit, so the next close actually exits instead
|
||||
/// of hiding to the tray.
|
||||
quitting: bool,
|
||||
@@ -31,6 +37,8 @@ impl App {
|
||||
agent,
|
||||
tray,
|
||||
join: JoinForm::default(),
|
||||
traffic: Traffic::default(),
|
||||
sampled: 0,
|
||||
quitting: false,
|
||||
})
|
||||
}
|
||||
@@ -58,7 +66,14 @@ impl eframe::App for App {
|
||||
}
|
||||
|
||||
let snapshot = self.agent.snapshot();
|
||||
ui::draw(ctx, &self.agent, &mut self.join, &snapshot);
|
||||
// Fold each fresh status into the traffic rates exactly once.
|
||||
if snapshot.generation != self.sampled
|
||||
&& let (Some(Ok(report)), Some(at)) = (&snapshot.status, snapshot.at)
|
||||
{
|
||||
self.traffic.observe(report, at);
|
||||
self.sampled = snapshot.generation;
|
||||
}
|
||||
ui::draw(ctx, &self.agent, &mut self.join, &snapshot, &self.traffic);
|
||||
|
||||
// Keep ticking so fresh status and tray actions are picked up even when
|
||||
// nothing else requests a repaint.
|
||||
|
||||
Reference in new issue
Block a user