Skip to content

Add ratatui and fix implementation - #10

Merged
imsudiproy merged 2 commits into
masterfrom
dev
Aug 8, 2026
Merged

Add ratatui and fix implementation#10
imsudiproy merged 2 commits into
masterfrom
dev

Conversation

@imsudiproy

@imsudiproy imsudiproy commented Aug 8, 2026

Copy link
Copy Markdown
Owner

Summary by CodeRabbit

  • New Features

    • Added process monitoring by PID or exact process name.
    • Added interactive terminal dashboard with CPU, memory, disk, and network metrics.
    • Added pause, resume, history clearing, and quit controls.
    • Added CSV and JSON export options.
    • Added automated release packaging for Linux and macOS binaries.
  • Documentation

    • Updated documentation with usage instructions, controls, export formats, and development checks.
  • Bug Fixes

    • Added validation and clearer errors for invalid intervals, missing processes, and ambiguous names.

@imsudiproy imsudiproy self-assigned this Aug 8, 2026
@imsudiproy imsudiproy added the enhancement New feature or request label Aug 8, 2026
@coderabbitai

coderabbitai Bot commented Aug 8, 2026

Copy link
Copy Markdown

Review Change Stack

📝 Walkthrough

Walkthrough

Changes

Process monitoring feature

Layer / File(s) Summary
Sampling model and data contracts
Cargo.toml, src/datasources/cpu_mem.rs, tests/cpu_mem_tests.rs
Adds serializable ProcessInfo records and stateful ProcessSampler logic for process, memory, CPU, disk, and network metrics.
Target resolution and collection control
src/core/mod.rs, src/core/monitor.rs
Adds PID and exact-name target resolution, interval validation, bounded collection, and process-exit handling.
CLI, dashboard, and export flow
src/lib.rs, src/main.rs, src/output/*, readme.md
Adds CLI monitoring and export control, a Ratatui dashboard, CSV and JSON writers, public module exports, and usage documentation.

Release automation

Layer / File(s) Summary
Multi-target release builds
.github/workflows/release.yml
Builds locked release binaries for four Linux and macOS targets, packages checksummed archives, and uploads artifacts.
Versioned release publication
.github/workflows/release.yml
Downloads artifacts, derives the version tag from Cargo.toml, and creates a GitHub release when the tag is absent.

Estimated code review effort: 4 (Complex) | ~45 minutes

Sequence Diagram(s)

sequenceDiagram
  participant CLI
  participant Monitor
  participant ProcessSampler
  participant Output
  CLI->>Monitor: resolve target and collect samples
  Monitor->>ProcessSampler: sample process metrics
  ProcessSampler-->>Monitor: ProcessInfo
  Monitor-->>CLI: collected samples
  CLI->>Output: render dashboard or write CSV/JSON
Loading

Possibly related PRs

  • imsudiproy/uniproc#1: Earlier PID monitoring logic replaced by ProcessSampler, Monitor, and the new CLI workflow.
  • imsudiproy/uniproc#4: Earlier process-name monitoring APIs and CLI flow evolved into exact-name target resolution.
🚥 Pre-merge checks | ✅ 4 | ❌ 1

❌ Failed checks (1 warning)

Check name Status Explanation Resolution
Docstring Coverage ⚠️ Warning Docstring coverage is 23.53% which is insufficient. The required threshold is 80.00%. Write docstrings for the functions missing them to satisfy the coverage threshold.
✅ Passed checks (4 passed)
Check name Status Explanation
Description Check ✅ Passed Check skipped - CodeRabbit’s high-level summary is enabled.
Title check ✅ Passed The title identifies the Ratatui addition and implementation work, which are central changes in the pull request.
Linked Issues check ✅ Passed Check skipped because no linked issues were found for this pull request.
Out of Scope Changes check ✅ Passed Check skipped because no linked issues were found for this pull request.
✨ Finishing Touches 💡 1
📝 Generate docstrings 💡
  • Create stacked PR
  • Commit on current branch
🧪 Generate unit tests (beta)
  • Create PR with unit tests
  • Commit unit tests in branch dev

Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out.

❤️ Share

Comment @coderabbitai help to get the list of available commands.

@imsudiproy
imsudiproy merged commit 55a9633 into master Aug 8, 2026
5 of 6 checks passed
@imsudiproy
imsudiproy deleted the dev branch August 8, 2026 17:27

@coderabbitai coderabbitai Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Actionable comments posted: 5

🧹 Nitpick comments (4)
src/core/monitor.rs (1)

64-76: 🎯 Functional Correctness | 🔵 Trivial | ⚡ Quick win

Sampling period drifts, and collect never returns when duration is None.

thread::sleep(self.interval) runs after each sample. The sampling work adds to the period, so the effective interval is larger than the configured value and the drift accumulates over a long run. Sleeping until the next scheduled tick keeps the period stable.

collect also loops forever when duration is None, because is_expired always returns false. src/main.rs currently requires --duration for exports, so this path is not reachable from the CLI. A guard in collect protects the public API against future callers.

♻️ Proposed drift-free loop with a duration guard
     pub fn collect(mut self) -> Result<Vec<ProcessInfo>, String> {
+        if self.duration.is_none() {
+            return Err("collect requires a duration".to_owned());
+        }
         let mut samples = Vec::new();
+        let mut next_tick = Instant::now();
         loop {
             if self.is_expired() {
                 break;
             }
             let sample = self.sample().ok_or("the monitored process exited")?;
             samples.push(sample);
-            thread::sleep(self.interval);
+            next_tick += self.interval;
+            let now = Instant::now();
+            if next_tick > now {
+                thread::sleep(next_tick - now);
+            } else {
+                next_tick = now;
+            }
         }
         Ok(samples)
     }
🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@src/core/monitor.rs` around lines 64 - 76, Update Monitor::collect to reject
or terminate immediately when duration is None, preserving the existing Result
error contract. Replace the unconditional thread::sleep(self.interval) with
scheduling based on the next intended sampling tick so sampling work does not
accumulate drift; retain the current expiration check, sample collection, and
process-exit error behavior.
src/output/tui.rs (2)

85-87: 📐 Maintainability & Code Quality | 🔵 Trivial | 💤 Low value

Collapse the nested if blocks to satisfy Clippy.

Clippy reports collapsible_if at Lines 85 and 86. If the build runs with -D warnings, this fails the pipeline. Let-chains or a combined condition remove the warning.

🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@src/output/tui.rs` around lines 85 - 87, Collapse the nested event checks in
the terminal event loop by combining the poll, key-event pattern match, and
KeyEventKind::Press condition using a let-chain or equivalent. Preserve the
existing error propagation and execute the press-event handling only when all
conditions match.

Source: Linters/SAST tools


24-47: 🩺 Stability & Availability | 🔵 Trivial | ⚡ Quick win

Restore the terminal when the dashboard panics.

restore_terminal runs only on the normal return path. If run_dashboard, draw, or a widget panics, raw mode stays enabled and the alternate screen stays active. The user then gets an unusable shell. A guard type that restores the terminal in Drop covers the panic path as well.

🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@src/output/tui.rs` around lines 24 - 47, The run function currently restores
terminal state only after run_dashboard returns normally. Add a guard type for
the initialized terminal that invokes restore_terminal in Drop, ensuring raw
mode and the alternate screen are restored during panics from run_dashboard,
draw, or widgets; retain the existing result/error propagation and avoid
duplicate cleanup on normal completion.
src/datasources/cpu_mem.rs (1)

95-106: 🚀 Performance & Scalability | 🔵 Trivial | 💤 Low value

Use a minimal sysinfo refresh for the name lookup.

System::new_all() enables all system data categories before this function only reads process names. A fresh System::new() followed by refresh_processes_specifics(..., ProcessRefreshKind::nothing()) avoids loading CPU, memory, disk, and network metrics for this lookup.

🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@src/datasources/cpu_mem.rs` around lines 95 - 106, Update
find_processes_by_name to construct System with System::new() instead of
System::new_all(), then perform a targeted process refresh using
refresh_processes_specifics with ProcessRefreshKind::nothing() before
processes_by_exact_name. Import or reference the required sysinfo refresh
symbols while preserving the existing result mapping.
🤖 Prompt for all review comments with AI agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

Inline comments:
In @.github/workflows/release.yml:
- Around line 10-11: Set the workflow-level contents permission to read, then
add a job-level contents: write permission only to the publish job. Keep the
other build jobs restricted to the read-only default.
- Around line 37-39: Update the action references in the release workflow for
actions/checkout, dtolnay/rust-toolchain, actions/upload-artifact, and
actions/download-artifact to verified 40-character commit SHAs, retaining each
current release tag in a trailing comment for update tooling.

In `@src/datasources/cpu_mem.rs`:
- Around line 37-54: Update ProcessSampler::new so the process is warmed up
before tui::run publishes its first sample: perform the initial process refresh,
wait at least sysinfo’s MINIMUM_CPU_UPDATE_INTERVAL, then refresh the process
again before returning. Preserve the existing missing-PID validation and sampler
initialization.

In `@src/output/json.rs`:
- Around line 6-11: Update write to bind the BufWriter created around the file,
pass it to serde_json::to_writer_pretty, then explicitly flush it and map any
flush failure to the existing JSON output error format. Ensure both
serialization and final buffered-write errors are propagated instead of relying
on Drop.

In `@src/output/tui.rs`:
- Around line 80-84: Update the poll-timeout calculation near last_tick and
monitor.interval() to clamp the resulting timeout to a non-zero minimum,
including while paused. Preserve the existing upper bound and ensure event::poll
never receives a zero duration.

---

Nitpick comments:
In `@src/core/monitor.rs`:
- Around line 64-76: Update Monitor::collect to reject or terminate immediately
when duration is None, preserving the existing Result error contract. Replace
the unconditional thread::sleep(self.interval) with scheduling based on the next
intended sampling tick so sampling work does not accumulate drift; retain the
current expiration check, sample collection, and process-exit error behavior.

In `@src/datasources/cpu_mem.rs`:
- Around line 95-106: Update find_processes_by_name to construct System with
System::new() instead of System::new_all(), then perform a targeted process
refresh using refresh_processes_specifics with ProcessRefreshKind::nothing()
before processes_by_exact_name. Import or reference the required sysinfo refresh
symbols while preserving the existing result mapping.

In `@src/output/tui.rs`:
- Around line 85-87: Collapse the nested event checks in the terminal event loop
by combining the poll, key-event pattern match, and KeyEventKind::Press
condition using a let-chain or equivalent. Preserve the existing error
propagation and execute the press-event handling only when all conditions match.
- Around line 24-47: The run function currently restores terminal state only
after run_dashboard returns normally. Add a guard type for the initialized
terminal that invokes restore_terminal in Drop, ensuring raw mode and the
alternate screen are restored during panics from run_dashboard, draw, or
widgets; retain the existing result/error propagation and avoid duplicate
cleanup on normal completion.
🪄 Autofix

Fix all unresolved CodeRabbit comments on this PR:

  • Push a commit to this branch (recommended)
  • Create a new PR with the fixes

ℹ️ Review info
⚙️ Run configuration

Configuration used: defaults

Review profile: CHILL

Plan: Pro Plus

Run ID: eed592dc-e224-41a6-873a-ce7822150d6e

📥 Commits

Reviewing files that changed from the base of the PR and between 7f991cd and 36f0aee.

⛔ Files ignored due to path filters (1)
  • Cargo.lock is excluded by !**/*.lock
📒 Files selected for processing (13)
  • .github/workflows/release.yml
  • Cargo.toml
  • readme.md
  • src/core/mod.rs
  • src/core/monitor.rs
  • src/datasources/cpu_mem.rs
  • src/lib.rs
  • src/main.rs
  • src/output/csv.rs
  • src/output/json.rs
  • src/output/mod.rs
  • src/output/tui.rs
  • tests/cpu_mem_tests.rs

Comment on lines +10 to +11
permissions:
contents: write

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

🔒 Security & Privacy | 🟠 Major | ⚡ Quick win

Limit contents: write to publish.

Line 11 grants repository write access to all four build jobs. A compromised build step can modify repository contents or release state. Set the workflow default to contents: read, then grant contents: write only to publish. GitHub recommends read-only default permissions and job-specific escalation. (docs.github.com)

Proposed fix
 permissions:
-  contents: write
+  contents: read
...
   publish:
     name: Publish GitHub release
+    permissions:
+      contents: write
     needs: build
🧰 Tools
🪛 zizmor (1.29.0)

[error] 11-11: overly broad permissions (excessive-permissions): contents: write is overly broad at the workflow level

(excessive-permissions)

🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In @.github/workflows/release.yml around lines 10 - 11, Set the workflow-level
contents permission to read, then add a job-level contents: write permission
only to the publish job. Keep the other build jobs restricted to the read-only
default.

Source: Linters/SAST tools

Comment on lines +37 to +39
- uses: actions/checkout@v4

- uses: dtolnay/rust-toolchain@stable

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

🔒 Security & Privacy | 🟠 Major | ⚡ Quick win

🧩 Analysis chain

🏁 Script executed:

#!/bin/bash
set -euo pipefail

echo "== workflow excerpt =="
sed -n '1,110p' .github/workflows/release.yml | cat -n

echo
echo "== action uses in release workflow =="
rg -n "uses:" .github/workflows/release.yml

Repository: imsudiproy/uniproc

Length of output: 3869


Pin each action to a verified full commit SHA.

Lines 37, 39, 58, 69, and 72 use mutable action tags for actions/checkout, dtolnay/rust-toolchain, actions/upload-artifact, and actions/download-artifact. Pin these actions to verified 40-character commit SHAs; keep the release tag in a trailing comment for update tooling. Use a full commit SHA, which is an immutable GitHub Actions reference.

Also applies to: 58, 69, 72

🧰 Tools
🪛 zizmor (1.29.0)

[warning] 37-37: credential persistence through GitHub Actions artifacts (artipacked): does not set persist-credentials: false

(artipacked)

🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In @.github/workflows/release.yml around lines 37 - 39, Update the action
references in the release workflow for actions/checkout, dtolnay/rust-toolchain,
actions/upload-artifact, and actions/download-artifact to verified 40-character
commit SHAs, retaining each current release tag in a trailing comment for update
tooling.

Comment on lines +37 to 54
pub fn new(pid: u32) -> Result<Self, String> {
let pid = Pid::from_u32(pid);
let mut system = System::new();
system.refresh_memory();
system.refresh_processes_specifics(
ProcessesToUpdate::Some(&[pid]),
true,
ProcessRefreshKind::everything(),
);
if system.process(pid).is_none() {
return Err(format!("process with PID {pid} was not found"));
}
Ok(Self {
pid,
system,
networks: Networks::new_with_refreshed_list(),
})
}

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

🎯 Functional Correctness | 🟡 Minor | ⚡ Quick win

🧩 Analysis chain

🌐 Web query:

sysinfo 0.37 MINIMUM_CPU_UPDATE_INTERVAL process cpu_usage two refreshes required

💡 Result:

In the sysinfo crate (version 0.37), CPU usage calculation is based on the difference between two measurements taken over a period of time [1][2][3]. Because of this, a single call to a refresh method is insufficient to calculate a non-zero, accurate percentage; the system requires at least two refreshes with an interval between them to compute the delta [1][2][3]. The constant MINIMUM_CPU_UPDATE_INTERVAL is provided by sysinfo as a recommended minimum wait time between these two calls [4][5]. Currently, this value is set to 200 milliseconds [6]. If refreshes occur too frequently—below this interval—the calculated CPU usage may incorrectly return 0% or, on some platforms like Linux, report maximum values [4]. To obtain accurate CPU usage, follow this pattern: 1. Initialize your System object [1][2]. 2. Perform an initial refresh [7]. 3. Wait for at least the duration specified by MINIMUM_CPU_UPDATE_INTERVAL using std::thread::sleep [1][2][7]. 4. Perform a second refresh [1][2][7]. 5. Access the cpu_usage value [1][3]. This requirement applies to both global CPU usage and individual process CPU usage [1][2]. Using a sleep duration shorter than the minimum interval can lead to inaccurate data, as the internal time-diff calculation will not have sufficient time elapsed to produce a valid measurement [4][2][3].

Citations:


🏁 Script executed:

#!/bin/bash
set -euo pipefail

echo "== rust manifest =="
git ls-files | rg '(^|/)Cargo\.toml$|(^|/)Cargo\.lock$' || true

for f in $(git ls-files | rg '(^|/)Cargo\.toml$'); do
  echo "--- $f ---"
  rg -n 'sysinfo|name = "cpu_mem"|process_info|ProcessSampler|Monitor|sample' "$f" -C 2 || true
done

echo "== source files matching candidate names =="
git ls-files | rg '(^|/)cpu_mem\.rs$|(^|/).*monitor.*\.rs$|(^|/)tui.*\.rs$' || true

for f in $(git ls-files | rg '(^|/)cpu_mem\.rs$'); do
  echo "--- $f ---"
  wc -l "$f"
  cat -n "$f" | sed -n '1,140p'
done

echo "== sample/call-site references =="
rg -n 'ProcessSampler::new|process_sample|sample\(|\.cpu_usage\(\)|MINIMUM_CPU_UPDATE_INTERVAL' $(git ls-files '*.rs') -C 3

Repository: imsudiproy/uniproc

Length of output: 9244


🌐 Web query:

docs.rs sysinfo 0.37.0 Process cpu_usage documentation MINIMUM_CPU_UPDATE_INTERVAL

💡 Result:

In the sysinfo crate (including version 0.37.0), MINIMUM_CPU_UPDATE_INTERVAL is a constant defined as the minimum interval time used internally by the library to refresh CPU-related information [1][2]. Its primary purpose is to ensure that CPU usage calculations—which rely on measuring the difference in process or system time over a specific period—remain accurate [3][4][2]. Because CPU usage is computed by dividing a process's time usage by the total system time over a given interval, attempting to refresh this data too frequently can lead to inaccurate results (e.g., returning 0% or, on some systems like Linux, an artificially high maximum value) [3][2]. Key usage guidelines for MINIMUM_CPU_UPDATE_INTERVAL: 1. Recommended Delay: Developers are advised to use this constant with std::thread::sleep or similar timing mechanisms when looping to refresh CPU usage [5][3][4]. 2. Accuracy Requirement: Because computation is based on time differentials, a process or system must be refreshed at least twice with sufficient time between calls to obtain meaningful usage data [3][4]. 3. Platform Variation: The specific duration of this constant is platform-dependent, as noted in the official documentation [2]. In practice, you should incorporate this interval when implementing refresh loops for both system-wide CPU usage (via System::refresh_cpu_usage) and specific process CPU usage (via Process::cpu_usage) [5][3][4].

Citations:


Warm up ProcessSampler before returning the first sample.

sysinfo computes process CPU usage from two refreshes with at least MINIMUM_CPU_UPDATE_INTERVAL between them. ProcessSampler::new performs only the first refresh, but tui::run publishes the first sample() immediately. That sample can return cpu_percent: 0.0 or another unreliable value. Add a wait before the first sample return, or skip/discard the first sample.

🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@src/datasources/cpu_mem.rs` around lines 37 - 54, Update ProcessSampler::new
so the process is warmed up before tui::run publishes its first sample: perform
the initial process refresh, wait at least sysinfo’s
MINIMUM_CPU_UPDATE_INTERVAL, then refresh the process again before returning.
Preserve the existing missing-PID validation and sampler initialization.

Comment thread src/output/json.rs
Comment on lines +6 to +11
pub fn write(path: impl AsRef<Path>, samples: &[ProcessInfo]) -> Result<(), String> {
let file =
File::create(path.as_ref()).map_err(|e| format!("cannot create JSON output: {e}"))?;
serde_json::to_writer_pretty(BufWriter::new(file), samples)
.map_err(|e| format!("cannot write JSON output: {e}"))
}

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

🗄️ Data Integrity & Integration | 🟠 Major | ⚡ Quick win

Flush the buffered writer explicitly to detect write failures.

serde_json::to_writer_pretty does not flush the BufWriter. The BufWriter is a temporary here, so it is dropped at the end of the expression. Drop flushes the remaining buffer but discards any error. If the final write fails, for example when the disk is full, write returns Ok(()) and src/main.rs prints a success message for a truncated file. Bind the writer and flush it explicitly.

🐛 Proposed fix
+use std::io::Write;
+
 pub fn write(path: impl AsRef<Path>, samples: &[ProcessInfo]) -> Result<(), String> {
     let file =
         File::create(path.as_ref()).map_err(|e| format!("cannot create JSON output: {e}"))?;
-    serde_json::to_writer_pretty(BufWriter::new(file), samples)
-        .map_err(|e| format!("cannot write JSON output: {e}"))
+    let mut writer = BufWriter::new(file);
+    serde_json::to_writer_pretty(&mut writer, samples)
+        .map_err(|e| format!("cannot write JSON output: {e}"))?;
+    writer
+        .flush()
+        .map_err(|e| format!("cannot write JSON output: {e}"))
 }
📝 Committable suggestion

‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.

Suggested change
pub fn write(path: impl AsRef<Path>, samples: &[ProcessInfo]) -> Result<(), String> {
let file =
File::create(path.as_ref()).map_err(|e| format!("cannot create JSON output: {e}"))?;
serde_json::to_writer_pretty(BufWriter::new(file), samples)
.map_err(|e| format!("cannot write JSON output: {e}"))
}
use std::io::Write;
pub fn write(path: impl AsRef<Path>, samples: &[ProcessInfo]) -> Result<(), String> {
let file =
File::create(path.as_ref()).map_err(|e| format!("cannot create JSON output: {e}"))?;
let mut writer = BufWriter::new(file);
serde_json::to_writer_pretty(&mut writer, samples)
.map_err(|e| format!("cannot write JSON output: {e}"))?;
writer
.flush()
.map_err(|e| format!("cannot write JSON output: {e}"))
}
🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@src/output/json.rs` around lines 6 - 11, Update write to bind the BufWriter
created around the file, pass it to serde_json::to_writer_pretty, then
explicitly flush it and map any flush failure to the existing JSON output error
format. Ensure both serialization and final buffered-write errors are propagated
instead of relying on Drop.

Comment thread src/output/tui.rs
Comment on lines +80 to +84
let elapsed = last_tick.elapsed();
let timeout = monitor
.interval()
.saturating_sub(elapsed)
.min(Duration::from_millis(100));

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

🚀 Performance & Scalability | 🟠 Major | ⚡ Quick win

Pausing the dashboard causes a busy loop.

last_tick is updated only when a new sample is taken (Line 117). While paused is true, no sample is taken, so elapsed keeps growing. interval().saturating_sub(elapsed) then returns zero, event::poll returns immediately, and the loop redraws continuously. The process consumes a full CPU core for as long as the dashboard stays paused. This also inflates the CPU reading of the monitored process when the user monitors uniproc itself.

Clamp the poll timeout to a non-zero minimum.

🐛 Proposed fix for the poll timeout
         let elapsed = last_tick.elapsed();
         let timeout = monitor
             .interval()
             .saturating_sub(elapsed)
-            .min(Duration::from_millis(100));
+            .min(Duration::from_millis(100))
+            .max(Duration::from_millis(10));
📝 Committable suggestion

‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.

Suggested change
let elapsed = last_tick.elapsed();
let timeout = monitor
.interval()
.saturating_sub(elapsed)
.min(Duration::from_millis(100));
let elapsed = last_tick.elapsed();
let timeout = monitor
.interval()
.saturating_sub(elapsed)
.min(Duration::from_millis(100))
.max(Duration::from_millis(10));
🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@src/output/tui.rs` around lines 80 - 84, Update the poll-timeout calculation
near last_tick and monitor.interval() to clamp the resulting timeout to a
non-zero minimum, including while paused. Preserve the existing upper bound and
ensure event::poll never receives a zero duration.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

enhancement New feature or request

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant