tray: customizable icon styles and percentage text#78
Conversation
…splay - Added support for multiple tray icon styles: "bars", "circle", and "textOnly". - Introduced functionality to toggle the display of usage percentage next to the tray icon. - Updated settings page to allow users to select their preferred tray icon style and visibility of percentage. - Enhanced tests to cover new tray icon styles and settings functionality. - Refactored related components to accommodate the new features.
- Changed the text from "Show percentage next to icon" to "Show percentage" in the settings page and related tests for consistency. - Updated tests to reflect the new text and ensure proper functionality of the percentage toggle feature. - Enhanced the tray icon rendering logic to accommodate new visual styles and layout adjustments.
- Updated the tray bar rendering logic to remove edge divider lines and ensure a visible tail for high bar percentages. - Enhanced test cases to reflect changes in SVG output and maintain visual consistency. - Refactored preview bar layout functions to improve clarity and maintainability, including adjustments to remainder visibility and fill percentage calculations.
Co-authored-by: Cursor <cursoragent@cursor.com>
Add customizable menu bar icon styles ('bars', 'circle', 'text-only') with optional percentage text and persist settings in
|
There was a problem hiding this comment.
💡 Codex Review
Here are some automated review suggestions for this pull request.
Reviewed commit: 9a1742bfea
ℹ️ 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".
| export async function loadTrayIconStyle(): Promise<TrayIconStyle> { | ||
| const stored = await store.get<unknown>(TRAY_ICON_STYLE_KEY); | ||
| // Backward compatibility with older tray style values. | ||
| if (stored === "barsWithPercentText" || stored === "barWithPercentText") return "bars"; | ||
| if (stored === "circularWithPercentText") return "circle"; | ||
| if (isTrayIconStyle(stored)) return stored; |
There was a problem hiding this comment.
Preserve old percent-text setting on migration
loadTrayIconStyle maps legacy values like barsWithPercentText/circularWithPercentText to "bars"/"circle", but there is no corresponding migration for trayShowPercentage. In App the show-percentage flag then loads as false (default), so users who previously enabled percent text will silently lose it after upgrade. If you want backward-compatibility, you need to set trayShowPercentage to true when these legacy values are encountered (or migrate the stored value before reading the new key).
Useful? React with 👍 / 👎.
| {trayIconStyle !== "textOnly" && ( | ||
| <label className="mt-2 inline-flex items-center gap-2 text-sm text-foreground"> | ||
| <Checkbox | ||
| key={`tray-show-percentage-${trayShowPercentage}`} |
There was a problem hiding this comment.
🟢 Low
pages/settings.tsx:372 The dynamic key based on trayShowPercentage causes the checkbox to remount on every toggle, breaking keyboard focus. Consider using a static key or removing it entirely.
| key={`tray-show-percentage-${trayShowPercentage}`} | |
| <Checkbox |
🚀 Want me to fix this? Reply ex: "fix it for me".
There was a problem hiding this comment.
Cursor Bugbot has reviewed your changes and found 1 potential issue.
Bugbot Autofix is OFF. To automatically fix reported issues with Cloud Agents, enable Autofix in the Cursor dashboard.
| if (stored === "circularWithPercentText") return "circle"; | ||
| if (isTrayIconStyle(stored)) return stored; | ||
| return DEFAULT_TRAY_ICON_STYLE; | ||
| } |
There was a problem hiding this comment.
Migration loses percentage text preference from old styles
Medium Severity
The loadTrayIconStyle migration converts old values like barsWithPercentText and circularWithPercentText to bars and circle, but doesn't coordinate with loadTrayShowPercentage to preserve the percentage visibility preference. Since loadTrayShowPercentage independently defaults to false, users migrating from these old WithPercentText styles will silently lose their percentage text setting.
Additional Locations (1)
There was a problem hiding this comment.
3 issues found across 10 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/pages/settings.tsx">
<violation number="1" location="src/pages/settings.tsx:83">
P1: Hardcoded colors (`white`/`black`) cause poor contrast in Dark Mode.
When the button is active (`isActive={true}`), it uses `variant="default"`. In Dark Mode, this variant typically has a white/light background.
Rendering white text/icons (`text-white`, `bg-white`) on a white background makes the preview invisible.
Using semantic theme tokens ensures visibility in both modes.</violation>
<violation number="2" location="src/pages/settings.tsx:372">
P2: The dynamic `key` that includes `trayShowPercentage` causes the Checkbox to remount on every toggle, which breaks keyboard focus. Use a static key or remove the key prop entirely since it's not needed for a single checkbox.</violation>
</file>
<file name="src/lib/settings.ts">
<violation number="1" location="src/lib/settings.ts:192">
P2: When migrating legacy tray icon settings the new `loadTrayShowPercentage()` does not infer that older `*WithPercentText` styles implied “show percentage”, so existing users lose their overlay after upgrading.</violation>
</file>
Reply with feedback, questions, or to request a fix. Tag @cubic-dev-ai to re-run a review.
| {trayIconStyle !== "textOnly" && ( | ||
| <label className="mt-2 inline-flex items-center gap-2 text-sm text-foreground"> | ||
| <Checkbox | ||
| key={`tray-show-percentage-${trayShowPercentage}`} |
There was a problem hiding this comment.
P2: The dynamic key that includes trayShowPercentage causes the Checkbox to remount on every toggle, which breaks keyboard focus. Use a static key or remove the key prop entirely since it's not needed for a single checkbox.
Prompt for AI agents
Check if this issue is valid — if so, understand the root cause and fix it. At src/pages/settings.tsx, line 372:
<comment>The dynamic `key` that includes `trayShowPercentage` causes the Checkbox to remount on every toggle, which breaks keyboard focus. Use a static key or remove the key prop entirely since it's not needed for a single checkbox.</comment>
<file context>
@@ -195,6 +333,50 @@ export function SettingsPage({
+ {trayIconStyle !== "textOnly" && (
+ <label className="mt-2 inline-flex items-center gap-2 text-sm text-foreground">
+ <Checkbox
+ key={`tray-show-percentage-${trayShowPercentage}`}
+ checked={trayShowPercentage}
+ onCheckedChange={(checked) => onTrayShowPercentageChange(checked === true)}
</file context>
| export async function loadTrayShowPercentage(): Promise<boolean> { | ||
| const stored = await store.get<unknown>(TRAY_SHOW_PERCENTAGE_KEY); | ||
| if (typeof stored === "boolean") return stored; | ||
| return DEFAULT_TRAY_SHOW_PERCENTAGE; | ||
| } |
There was a problem hiding this comment.
P2: When migrating legacy tray icon settings the new loadTrayShowPercentage() does not infer that older *WithPercentText styles implied “show percentage”, so existing users lose their overlay after upgrading.
Prompt for AI agents
Check if this issue is valid — if so, understand the root cause and fix it. At src/lib/settings.ts, line 192:
<comment>When migrating legacy tray icon settings the new `loadTrayShowPercentage()` does not infer that older `*WithPercentText` styles implied “show percentage”, so existing users lose their overlay after upgrading.</comment>
<file context>
@@ -158,6 +171,35 @@ export async function saveDisplayMode(mode: DisplayMode): Promise<void> {
+ await store.save();
+}
+
+export async function loadTrayShowPercentage(): Promise<boolean> {
+ const stored = await store.get<unknown>(TRAY_SHOW_PERCENTAGE_KEY);
+ if (typeof stored === "boolean") return stored;
</file context>
| export async function loadTrayShowPercentage(): Promise<boolean> { | |
| const stored = await store.get<unknown>(TRAY_SHOW_PERCENTAGE_KEY); | |
| if (typeof stored === "boolean") return stored; | |
| return DEFAULT_TRAY_SHOW_PERCENTAGE; | |
| } | |
| export async function loadTrayShowPercentage(): Promise<boolean> { | |
| const stored = await store.get<unknown>(TRAY_SHOW_PERCENTAGE_KEY); | |
| if (typeof stored === "boolean") return stored; | |
| const legacyStyle = await store.get<unknown>(TRAY_ICON_STYLE_KEY); | |
| if ( | |
| legacyStyle === "barsWithPercentText" || | |
| legacyStyle === "barWithPercentText" || | |
| legacyStyle === "circularWithPercentText" | |
| ) { | |
| return true; | |
| } | |
| return DEFAULT_TRAY_SHOW_PERCENTAGE; | |
| } |
…rkaround - Changed tray icon style classes to use new primary foreground colors for improved visual consistency. - Added dynamic key workaround for Checkbox components to address rendering issues in Tauri, ensuring proper visibility after toggling.


Adds configurable tray icon styles (bars, circle, text-only) with optional percentage text overlay, replacing the fixed bars-only rendering.
Description
trayIconStyle,trayShowPercentage) with backward-compat migration for old style keysMade with Cursor
Note
Medium Risk
Touches tray icon generation/rendering and persisted settings, including new non-square icon rasterization paths that could regress tray appearance across platforms. Changes are well-covered by added unit/integration tests, lowering the likelihood of silent breakage.
Overview
Adds customizable menu bar icon styles (bars, circle, text-only) and an optional percentage text display, with new persisted settings (
trayIconStyle,trayShowPercentage) including backward-compatible migration for older stored values.Updates
Appto load/save these preferences, pass them intorenderTrayBarsIcon, adjust primary-metric selection (4 bars vs 1), and fall back to the packaged gauge icon when text-only cannot produce a valid percent. The tray SVG/rasterization pipeline is significantly expanded to support circle gauges, text-only rendering, percent-text layout with variable icon widths, and improved bar visuals (rounded paths + visible remainder tails).Settings UI is updated with a new “Menu Bar Icon” picker with live previews, conditional “Show percentage” toggle, and a renamed usage section heading; the previous “Check for updates” button is removed. Tests are extended across app integration, settings persistence/UI, tray SVG generation, and new pace-status logic + provider card pace indicators.
Written by Cursor Bugbot for commit 915b09e. This will update automatically on new commits. Configure here.
Summary by cubic
Add customizable menu bar icon styles (bars, circle, text-only) with an optional percentage next to the icon. Updates Settings with a live-preview picker and persists choices, replacing the fixed bars-only icon.
New Features
Refactors
Written for commit 915b09e. Summary will update on new commits.