Skip to content

feat: live hardware metrics in system tray / menu bar (Stats-like widget) #1401

@shm11C3

Description

@shm11C3

Summary

Display live hardware metrics (CPU usage, GPU usage, temperatures, etc.) directly in the system tray (Windows / Linux) and menu bar (macOS), so that users can monitor their system at a glance without opening the main window.

This is the "always-visible micro-dashboard" pattern popularized by Stats on macOS and Open Hardware Monitor / Rainmeter on Windows.

Motivation

A hardware monitor only delivers value while the user is actually looking at it. Today, that requires keeping the main window open or foregrounded.

A live tray/menu-bar readout:

  • Turns HardwareVisualizer into a passive, "always-on" tool, dramatically increasing daily-active usage
  • Eliminates the friction of opening the app to check a single number ("is my CPU spiking?")
  • Aligns the product with the most-loved feature of competitors in this category (Stats on macOS, RTSS overlay on Windows, KSysGuard panel applet on KDE)
  • Differentiates from HWMonitor / HWiNFO, whose tray support is minimal or icon-only

Relationship to #1275 / #1422

Goals

  • Show one or more live numeric metrics in the system tray / menu bar
  • Each metric is shown as a small icon + numeric value pair (not a text-only label)
  • The numeric value's color changes based on the metric's state (normal / warning / critical) so the user can tell something is wrong at a glance, without reading the number
  • Update at a configurable interval (default 1s, matching the foreground collector)
  • Allow the user to choose which metrics to show
  • Look native on each platform (text + glyph on macOS, composite icon or label on Windows/Linux)
  • Keep the rendering cost negligible (no main-window React work required while hidden)

Non-Goals

  • In-game / DirectX/Vulkan overlay (separate, larger feature)
  • Floating always-on-top mini window (separate feature; could share the same metric source)
  • Fully user-customizable color palette / threshold tuning UI in v1 (only built-in state colors and default thresholds)
  • Per-core or per-GPU breakdown in the tray surface (the main window remains the right place for that)

Proposed UX

Display content (default)

Each enabled metric is rendered as icon + value, with the value colored by state:

Metric Icon Value format Default state thresholds
CPU usage CPU glyph 42% normal < 70% / warning 70–85% / critical ≥ 85%
GPU usage GPU glyph 18% normal < 70% / warning 70–90% / critical ≥ 90%
Temperature (hottest sensor) Thermometer glyph 58° normal < 70°C / warning 70–85°C / critical ≥ 85°C

User can toggle each metric on/off and reorder them in Settings. Thresholds use sensible defaults in v1; per-user tuning is a future follow-up.

State colors (v1, built-in)

  • Normal: system foreground color (white in dark mode, black in light mode) — matches the menu bar / tray's surrounding text
  • Warning: amber/yellow
  • Critical: red

The icon glyph itself stays in the system foreground color regardless of state — only the numeric value changes color, so the row remains readable and the warning is unambiguous.

Per-platform presentation

Platform Approach Notes
macOS NSStatusItem with NSAttributedString (icon glyph + colored numeric run) via Tauri's tray API Native, cheap to update; per-character coloring is supported
Windows Dynamic tray icon: pre-render glyph + colored number to a small bitmap each update Tray API only renders icons; cache by (metric, value, state) to avoid re-rendering identical frames
Linux AppIndicator label where supported (no inline color), tooltip otherwise KDE/Xfce/GNOME-with-extension support label; color may be approximated by glyph swap (e.g. red thermometer) on platforms that strip color

Click behavior

Settings page

A new "Tray widget" section under Settings:

  • Visibility toggle (master)
  • Per-metric checkboxes and reorder
  • Update interval
  • Compact mode (icon only, no number — color still reflects state)

Scope

v1 (this issue)

  • Render at least CPU%, GPU%, and one temperature as icon + colored value pairs in the tray/menu bar
  • macOS first (menu-bar text + glyph is cheapest and highest impact)
  • Windows support via dynamic icon rendering with state-based number color
  • Built-in 3-state coloring (normal / warning / critical) using the default thresholds above
  • Settings UI to toggle and reorder metrics
  • Reuse the existing HardwareMonitorUpdate event stream — no new collector

Future follow-ups

  • User-customizable thresholds and color palette
  • Mini sparkline icons (Windows/Linux composite icon)
  • Per-GPU selection in multi-GPU systems (depends on Multi-GPU support: redesign monitoring and display pipeline #1296)
  • Hysteresis / debouncing on color transitions to avoid flicker at threshold boundaries
  • Linux: full color support on AppIndicator backends that allow it

Technical Notes

  • Source of truth: subscribe to the existing HardwareMonitorUpdate event from the backend (src-tauri/src/services/...). Do not add a parallel collector.
  • Rendering location: a small Rust-side renderer in src-tauri/src/tray/ that consumes the latest snapshot, classifies each metric into a state, and updates the tray surface. This keeps the path independent of the React tree, so updates work whether or not the main window is alive.
  • State classification: a pure function (metric, value) -> State lives next to the renderer and is unit-tested independently of any tray API.
  • Throttling: rate-limit tray updates to the user-configured interval (1/2/5 s) even if the underlying event stream is faster.
  • Diffing: skip the OS call if the rendered output is identical to the previous frame (same value and same state). Critical for Windows dynamic icons.
  • Settings: persist via the existing Tauri Store (src/lib/tauriStore.ts), exposed through useTauriStore like other UI settings.
  • Icons/glyphs: use a single icon font or pre-baked PNG set bundled with the app, so rendering is deterministic across platforms.

Platform Notes

  • macOS: highest ROI — NSAttributedString lets us mix a glyph and a colored numeric run in a single status item title. Start here.
  • Windows: requires drawing the glyph + colored number onto an HICON per update. Use a small in-memory canvas; cache bitmaps keyed by (metric, value, state).
  • Linux: AppIndicator label support and color support are uneven. Document the limitation (vanilla GNOME, label-only environments) rather than chase parity in v1.

Acceptance Criteria

Open Questions

  • Should the v1 default state be enabled (more discoverable) or opt-in (safer rollout)?
  • For Windows, do we ship a single composite icon that lists all metrics vertically, or one icon per metric (multiple tray entries)? Single icon is simpler; multiple lets each metric be readable at small DPI and lets each row carry its own state color.
  • Should the tray update interval be independent from the in-app collector interval, or always match it?
  • Are the proposed default thresholds (70/85 % for usage, 70/85 °C for temp) reasonable, or should they vary per hardware vendor?
  • For Linux, do we accept icon-only / no-color fallback on vanilla GNOME, or require users to install an extension and document it?

Metadata

Metadata

Assignees

No one assigned

    Projects

    Status
    Done

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions