Replaces the in-tile collapsing peer list with a "Show devices" button that
opens a separate, normal-sized window per network, and fills in the rest of the
feedback.
Main window:
- A global show: pkts/bytes toggle; all rates and sparklines follow it.
- Sparklines now reserve their space always (fixed height, "no data yet"
placeholder) so the layout no longer jumps when data arrives, and each one
overlays its peak (top-left) and current value (bottom-right) with units.
- Device id has a copy button; the hostname is editable in place (✎ → edit →
✔/Enter sends SetHostname); network name, range and address copy on click.
- The Leave button is a neutral red. Per-tile "Show devices" opens the detail.
Devices window (a separate egui viewport per network):
- Network summary: id (copy), active/broadcast, range, this device's address,
candidates, relay fwd/via/recv, dial/handshake failures.
- A larger network traffic sparkline.
- Connected peers in a table: hostname when known (else short key), protocol,
address, handshake age formatted (s/m/h/d), tx/rx packets, tx/rx bytes, and
current rate with an inline sparkline. Names and addresses copy on click.
- Offline members listed below with address and failed-dial count.
Agent: a SetHostname command (over the existing ipc) backs the in-place rename.
Traffic sampler now tracks both packet and byte rates; the toggle selects which.
Deferred: a "copy secret" button on the tile — the secret is deliberately not
exported to the GUI (secrets never leave the agent), so it needs a small local
secret-export ipc, which is the next step.
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>
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>
A native, lightweight tray application to manage a running agent — no web
engine. It is a thin client over the agent's local control socket, reusing
tsunagi::ipc and the report types directly; it owns its own runtime, logger and
event loop, like the CLI.
This first increment:
- tray icon with an Open/Quit menu (tray-icon); menu events are read on a
background thread so Open works even while the window is hidden;
- a small window that hides to the tray on close and reopens from the menu;
- a background tokio worker that polls status every 2s and runs commands off the
UI thread, writing a snapshot the UI renders from and repainting egui;
- UI: device header, a per-network card (name, id, range, this device's
address, peers connected/known, a Leave button, and the per-network active
and broadcast switches), a join form (name + secret), and a transient
action/error banner; an "agent unreachable" state that names the socket.
Socket resolution: TSUNAGI_CONTROL_SOCKET if set, else the platform default
(/var/run/tsunagi/agent.sock on macOS) — the same socket the group-access agent
serves.
Scope/notes: macOS first (Linux and Windows follow). Not yet done — macOS
accessory activation policy (so there is no dock icon), light/dark tray icons
from assets (the icon is drawn in code for now), and packaging/autostart. Builds
and is clippy-clean (-D warnings) on macOS; the UI itself has not been run in
this headless session.
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>