feat: implement hardware monitoring updates with event listener and refactor related hooks#1227
Conversation
…efactor related hooks
Coverage Report
File Coverage
|
||||||||||||||||||||||||||||||||||||||||||||
There was a problem hiding this comment.
Pull request overview
This PR switches hardware usage updates (CPU/RAM/GPU/per-core usage) from frontend polling to a backend-pushed Tauri event, and refactors the frontend hooks/tests accordingly. This fits the app’s monitoring pipeline by moving periodic sampling to the Rust worker and distributing results to the React UI via tauri-specta events.
Changes:
- Add a new
hardware-monitor-updateevent emitted by the RustSystemMonitorControllerand exposed to the frontend bindings. - Introduce
useHardwareEventListenerto update Jotai chart history atoms from backend events (replacing the 4 polling hooks). - Remove
useUsageUpdaterand update/replace related test coverage.
Reviewed changes
Copilot reviewed 11 out of 11 changed files in this pull request and generated 4 comments.
Show a summary per file
| File | Description |
|---|---|
| src/rspc/bindings.ts | Adds generated event typings + events.hardwareMonitorUpdate listener binding. |
| src/features/hardware/types/hardwareDataType.ts | Removes now-unused ChartDataHardwareType type. |
| src/features/hardware/hooks/useHardwareEventListener.ts | New hook to consume backend hardware update events and update chart state. |
| src/features/hardware/hooks/useHardwareEventListener.test.ts | Adds tests for event-driven history/source updates. |
| src/features/hardware/hooks/useHardwareData.ts | Removes useUsageUpdater; keeps useHardwareUpdater for temp/fan polling. |
| src/features/hardware/hooks/useHardwareData.test.ts | Removes useUsageUpdater tests and related mocks. |
| src/App.tsx | Replaces multiple polling hooks with useHardwareEventListener. |
| src-tauri/src/workers/system_monitor.rs | Emits HardwareMonitorUpdate events after each sampling tick. |
| src-tauri/src/services/monitoring_service.rs | Refactors sampling to return samples (system + GPU) and includes GPU “source” in the GPU sample tuple. |
| src-tauri/src/models/hardware.rs | Adds HardwareMonitorUpdate event payload type (specta + tauri-specta event). |
| src-tauri/src/lib.rs | Registers the new event with collect_events! and passes AppHandle into the monitor worker. |
Rust Backend Coverage ReportCoverage Details |
…nd adjust GpuMemoryUsage tests
There was a problem hiding this comment.
Pull request overview
Copilot reviewed 17 out of 17 changed files in this pull request and generated 4 comments.
Comments suppressed due to low confidence (2)
src-tauri/src/commands/hardware.rs:133
- The doc comment above
get_gpu_memory_usageis for CPU usage history (and even describes GPU-memory best-effort semantics), while the function actually returns GPU memory usage. This incorrect doc will also flow into the generated TS bindings; please swap/fix the Rust doc comments so each command’s docs match its behavior.
///
/// ## Get CPU usage history
///
/// - param state: `tauri::State<AppState>` Application state
/// - param seconds: `u32` Number of seconds to retrieve
/// - **Platform support**: Currently implemented only on macOS. On other
/// platforms, or where the underlying APIs are not available, this will
/// return `Ok(None)` instead of failing.
/// - **Best-effort behavior**: If the GPU memory metrics cannot be queried
/// (e.g. unsupported hardware, missing permissions, or transient errors),
/// the function returns `Ok(None)` to indicate that the data is not
/// available, rather than treating this as a hard error.
/// - **Return format**: When successful, the `GpuMemoryUsage` fields contain
/// human-readable, formatted size strings (for example, `"1.5 GB"`) rather
/// than raw byte counts.
///
/// Returns:
/// - `Ok(Some(GpuMemoryUsage))` when GPU memory usage data is available.
/// - `Ok(None)` when the metric is unsupported or currently unavailable.
/// - `Err(String)` only for unexpected internal failures.
src/rspc/bindings.ts:124
- The command JSDoc for
getCpuUsageHistorycurrently describes GPU memory usage. Since this file is generated by tauri-specta (see header), the fix should be applied to the Rust command docs (src-tauri/src/commands/hardware.rs) and then bindings should be regenerated, rather than patching docs here.
/**
* ## Get realtime GPU memory usage (best-effort)
*
* This command attempts to retrieve current GPU memory usage information
* on a best-effort, platform-dependent basis.
*
*/
async getCpuUsageHistory(seconds: number) : Promise<number[]> {
return await TAURI_INVOKE("get_cpu_usage_history", { seconds });
| if (gpuDedicatedMemoryUsageKb != null) { | ||
| setGpuDedicatedMemory(gpuDedicatedMemoryUsageKb); | ||
| } |
There was a problem hiding this comment.
gpuDedicatedMemoryKbAtom is only updated when gpuDedicatedMemoryUsageKb != null, so a later event with null (transient query failure, GPU change, etc.) will leave a stale non-null value in the UI. Update the atom on every event (set to the new value, including null) so the displayed memory usage can clear correctly.
| if (gpuDedicatedMemoryUsageKb != null) { | |
| setGpuDedicatedMemory(gpuDedicatedMemoryUsageKb); | |
| } | |
| setGpuDedicatedMemory(gpuDedicatedMemoryUsageKb); |
| (event: { | ||
| payload: { | ||
| cpuUsage: number; | ||
| memoryUsage: number; | ||
| gpuUsage: number | null; | ||
| gpuName: string | null; | ||
| gpuTemperature: number | null; | ||
| gpuSource: string | null; | ||
| processorsUsage: number[]; | ||
| gpuDedicatedMemoryUsageKb: number | null; | ||
| gpuCoolerLevel: number | null; | ||
| }; | ||
| }) => { |
There was a problem hiding this comment.
The event payload shape is duplicated inline here instead of reusing the generated HardwareMonitorUpdate type from @/rspc/bindings. Reusing the shared type (or a typed event wrapper) would avoid drift if the backend event schema changes.
| /// | ||
| /// ## Get GPU fan speed | ||
| /// | ||
| #[command] | ||
| #[specta::specta] | ||
| pub async fn get_nvidia_gpu_cooler() -> Result<Vec<models::hardware::NameValue>, String> { | ||
| use crate::services::gpu_service; | ||
|
|
||
| gpu_service::fetch_nvidia_gpu_cooler().await | ||
| } | ||
|
|
||
| /// | ||
| /// ## Get CPU usage history | ||
| /// ## Get realtime GPU memory usage (best-effort) | ||
| /// | ||
| /// - param state: `tauri::State<AppState>` Application state | ||
| /// - param seconds: `u32` Number of seconds to retrieve | ||
| /// This command attempts to retrieve current GPU memory usage information | ||
| /// on a best-effort, platform-dependent basis. |
There was a problem hiding this comment.
The doc comment above get_cpu_usage_history describes “realtime GPU memory usage” (best-effort), but this function returns CPU usage history. Please correct the doc comment (and then regenerate src/rspc/bindings.ts) to avoid misleading API docs for frontend callers.
| const padHistory = (arr: (number | null)[]): number[] => { | ||
| const padded = Array(Math.max(chartConfig.historyLengthSec - arr.length, 0)) | ||
| .fill(null) |
There was a problem hiding this comment.
padHistory pads with null but is typed to return number[], which hides the presence of nulls from callers and can lead to unsafe downstream usage. Return (number | null)[] here (and align the history atom / chart prop types accordingly) or change the padding value to a numeric sentinel if the rest of the UI truly requires number[].
| const padHistory = (arr: (number | null)[]): number[] => { | |
| const padded = Array(Math.max(chartConfig.historyLengthSec - arr.length, 0)) | |
| .fill(null) | |
| const padHistory = (arr: number[]): number[] => { | |
| const padded = Array(Math.max(chartConfig.historyLengthSec - arr.length, 0)) | |
| .fill(0) |
This pull request introduces a unified event-driven system for hardware monitoring updates, refactors the GPU sampling logic to include source metadata, and streamlines the way hardware usage data is communicated from the backend to the frontend. The changes enable more robust and extensible hardware monitoring, simplify frontend hooks, and improve testability and traceability of GPU metrics.
Backend: Hardware Monitoring Event System
Introduced a new
HardwareMonitorUpdatestruct (with CPU, memory, GPU usage, and per-processor usage) and registered it as a Tauri event, enabling real-time hardware updates to be emitted from the backend to the frontend. (src-tauri/src/models/hardware.rs,src-tauri/src/lib.rs,src-tauri/src/workers/system_monitor.rs) [1] [2] [3] [4] [5] [6] [7]Refactored the system and GPU sampling functions to return structured data (
SystemSample,GpuSample) and included asourcestring inGpuSamplefor improved traceability of GPU metrics across platforms (Windows, Linux, macOS). (src-tauri/src/services/monitoring_service.rs) [1] [2] [3] [4] [5] [6] [7] [8] [9] [10]Frontend: Usage Data Handling Simplification
useUsageUpdater) with a single event-driven hook (useHardwareEventListener) that listens for the new hardware update event, simplifying state management and reducing boilerplate. (src/App.tsx) [1] [2]Testing & Type Updates
Updated all GPU history-related tests and logic to use the new
GpuSampletuple format (including thesourcefield), ensuring consistency and future extensibility. (src-tauri/src/services/monitoring_service.rs) [1] [2] [3] [4] [5]Removed obsolete usage updater hooks and related test code, focusing tests on the new event-driven approach. (
src/features/hardware/hooks/useHardwareData.test.ts) [1] [2]