feat(tray): Dynamic tray icon with primary progress bars + about dialog#51
Conversation
Display up to 4 usage bars in the system tray icon from enabled plugins' primary progress metrics. Allows users to see usage at a glance without opening the app. - Add `primary: boolean` field to plugin manifest lines (Rust + TS) - Normalize primary flags (only progress lines, first wins) - New tray-bars-icon.ts for SVG generation and rasterization - New tray-primary-progress.ts for extracting bar data from plugin states - Debounced tray updates on probe results (500ms) and settings changes (2s) - Revert to packaged gauge icon when no primary bars available - Update all bundled plugins with `primary: true` on Session/Plan usage - Comprehensive test coverage for manifest normalization and tray logic Co-authored-by: Cursor <cursoragent@cursor.com>
Render up to 4 primary progress bars in the system tray and add tray behavior text to About dialog via
|
There was a problem hiding this comment.
💡 Codex Review
Here are some automated review suggestions for this pull request.
Reviewed commit: 23987cfc0d
ℹ️ About Codex in GitHub
Your team has set up Codex to review pull requests in this repo. Reviews are triggered when you
- Open a pull request for review
- Mark a draft as ready
- Comment "@codex review".
If Codex has suggestions, it will comment; otherwise it will react with 👍.
Codex can also answer questions or update the PR. Try commenting "@codex address that feedback".
| trayUpdateTimerRef.current = window.setTimeout(() => { | ||
| trayUpdateTimerRef.current = null | ||
| if (trayUpdatePendingRef.current) return | ||
| trayUpdatePendingRef.current = true | ||
|
|
||
| // Execute directly (no requestAnimationFrame - it's throttled when panel is hidden) | ||
| const tray = trayRef.current | ||
| trayUpdatePendingRef.current = false |
There was a problem hiding this comment.
Prevent stale tray icons from out-of-order renders
trayUpdatePendingRef is cleared before the async renderTrayBarsIcon finishes, so rapid successive updates (e.g., settings change followed by probe results) can run concurrently; if the earlier render resolves last, it overwrites the newer icon and the tray shows stale bars until the next update. Keeping the pending flag (or a sequence token) until setIcon completes would prevent out-of-order icon updates.
Useful? React with 👍 / 👎.
There was a problem hiding this comment.
3 issues found across 24 files
Prompt for AI agents (all issues)
Check if these issues are valid — if so, understand the root cause of each and fix them.
<file name="src/lib/tray-bars-icon.ts">
<violation number="1" location="src/lib/tray-bars-icon.ts:7">
P2: Dead code: `Uint8ClampedArray` is not an instance of `Uint8Array` (they're sibling TypedArray classes), so this condition is always false. Simplify by removing the unnecessary check.</violation>
</file>
<file name="src-tauri/src/plugin_engine/manifest.rs">
<violation number="1" location="src-tauri/src/plugin_engine/manifest.rs:183">
P2: Tests duplicate the normalization logic instead of testing the actual implementation. If `load_single_plugin`'s normalization changes, these tests won't catch regressions. Consider extracting the normalization into a separate function (e.g., `normalize_primary_flags(manifest: &mut PluginManifest)`) that both `load_single_plugin` and tests can call.</violation>
</file>
<file name="src/App.tsx">
<violation number="1" location="src/App.tsx:90">
P2: The `trayUpdatePendingRef` flag is ineffective - it's reset synchronously before the async operations (`renderTrayBarsIcon`, `tray.setIcon`) complete. Move `trayUpdatePendingRef.current = false` into the `.then()` or `.finally()` block to properly guard against concurrent updates.</violation>
</file>
Reply with feedback, questions, or to request a fix. Tag @cubic-dev-ai to re-run a review.
| "#, | ||
| ); | ||
|
|
||
| // Run the same normalization logic used on load. |
There was a problem hiding this comment.
P2: Tests duplicate the normalization logic instead of testing the actual implementation. If load_single_plugin's normalization changes, these tests won't catch regressions. Consider extracting the normalization into a separate function (e.g., normalize_primary_flags(manifest: &mut PluginManifest)) that both load_single_plugin and tests can call.
Prompt for AI agents
Check if this issue is valid — if so, understand the root cause and fix it. At src-tauri/src/plugin_engine/manifest.rs, line 183:
<comment>Tests duplicate the normalization logic instead of testing the actual implementation. If `load_single_plugin`'s normalization changes, these tests won't catch regressions. Consider extracting the normalization into a separate function (e.g., `normalize_primary_flags(manifest: &mut PluginManifest)`) that both `load_single_plugin` and tests can call.</comment>
<file context>
@@ -94,3 +129,117 @@ fn load_single_plugin(
+ "#,
+ );
+
+ // Run the same normalization logic used on load.
+ let mut seen_primary_progress = false;
+ for line in manifest.lines.iter_mut() {
</file context>
There was a problem hiding this comment.
Cursor Bugbot has reviewed your changes and found 3 potential issues.
Bugbot Autofix is OFF. To automatically fix reported issues with Cloud Agents, enable Autofix in the Cursor dashboard.
- Move trayUpdatePendingRef reset to .finally() after async work completes - Store state in refs so scheduleTrayIconUpdate callback is stable (fixes debounce) - Handle gauge icon restore path properly with Promise.all - Remove dead code in rgbaToImageDataBytes (Uint8ClampedArray instanceof check) Co-authored-by: Cursor <cursoragent@cursor.com>
Summary
primary: trueto appear in the trayDescription
primary: booleanfield to plugin manifest lines (Rust + TS)type: "progress"lines, first wins)tray-bars-icon.tsfor SVG generation and canvas rasterizationtray-primary-progress.tsfor extracting bar data from plugin statesprimary: trueon Session/Plan usageTest plan
primary: trueare enabledbun testto verify all tests passMade with Cursor
Note
Medium Risk
Adds new tray/icon rendering and plugin-manifest schema behavior across Rust + frontend, plus updates bundled plugins’ auth/refresh flows; regressions would mainly show up as broken tray updates or failed usage probes.
Overview
Adds a dynamic system tray icon that can render up to 4 horizontal usage bars derived from enabled plugins’ primary progress metric, with debounced updates on probe results and settings changes and a fallback back to the packaged gauge icon when no primaries are available.
Extends the plugin manifest schema with an optional
lines[].primaryflag, normalizes it at plugin-load time (onlyprogress, first wins), and exposesprimaryProgressLabelto the frontend vialist_plugins; bundled plugins (claude,codex,cursor) mark a primary line accordingly.Introduces new tray utilities (
tray-bars-icon,tray-primary-progress) and broad test coverage, plus a small UX addition: an About dialog opened from the footer version label and additional navigation/detail-view + error-logging regression tests.Written by Cursor Bugbot for commit e6d9617. This will update automatically on new commits. Configure here.
Summary by cubic
Adds a dynamic tray icon that shows up to 4 usage bars from enabled plugins’ primary progress metrics for quick, glanceable usage. The icon updates on probe results and settings changes, falls back to the default gauge when no bars are available, and avoids race conditions during updates.
New Features
Bug Fixes
Written for commit e6d9617. Summary will update on new commits.