You signed in with another tab or window. Reload to refresh your session.You signed out in another tab or window. Reload to refresh your session.You switched accounts on another tab or window. Reload to refresh your session.Dismiss alert
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
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)
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
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
At least one metric (CPU%) visibly updates in the system tray / menu bar at the configured interval, rendered as icon + numeric value
Multiple metrics can be enabled and shown together in a configurable order
The numeric value's color changes when the metric crosses warning and critical thresholds, and reverts when it drops back
No measurable CPU usage increase from tray updates compared to current background cost (< 0.5% on idle test machine)
Tray widget can be disabled entirely from Settings
Settings persist across restarts
Unit tests cover: state classification at threshold boundaries, metric formatting, throttling, and the "skip identical frame" optimization
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?
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:
Relationship to #1275 / #1422
Goals
Non-Goals
Proposed UX
Display content (default)
Each enabled metric is rendered as icon + value, with the value colored by state:
42%< 70%/ warning70–85%/ critical≥ 85%18%< 70%/ warning70–90%/ critical≥ 90%58°< 70°C/ warning70–85°C/ critical≥ 85°CUser 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)
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
NSStatusItemwithNSAttributedString(icon glyph + colored numeric run) via Tauri's tray API(metric, value, state)to avoid re-rendering identical framesAppIndicatorlabel where supported (no inline color), tooltip otherwiseClick behavior
Settings page
A new "Tray widget" section under Settings:
Scope
v1 (this issue)
HardwareMonitorUpdateevent stream — no new collectorFuture follow-ups
Technical Notes
HardwareMonitorUpdateevent from the backend (src-tauri/src/services/...). Do not add a parallel collector.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.(metric, value) -> Statelives next to the renderer and is unit-tested independently of any tray API.src/lib/tauriStore.ts), exposed throughuseTauriStorelike other UI settings.Platform Notes
NSAttributedStringlets us mix a glyph and a colored numeric run in a single status item title. Start here.HICONper update. Use a small in-memory canvas; cache bitmaps keyed by(metric, value, state).Acceptance Criteria
Open Questions