Skip to content

fix: reduce IceBar click jitter with fast-path temporarilyShow#338

Open
CamilleGuillory wants to merge 1 commit intostonerl:developmentfrom
CamilleGuillory:fix/icebar-click-jitter
Open

fix: reduce IceBar click jitter with fast-path temporarilyShow#338
CamilleGuillory wants to merge 1 commit intostonerl:developmentfrom
CamilleGuillory:fix/icebar-click-jitter

Conversation

@CamilleGuillory
Copy link
Copy Markdown
Contributor

@CamilleGuillory CamilleGuillory commented Mar 28, 2026

Problem

When clicking hidden items from the IceBar, the icon visibly jitters/shifts in the menu bar for ~1.8 seconds before the menu opens. This is caused by:

  1. move() retrying 8 times with exponentially increasing timeouts, even though the first attempt always repositions the item close enough (position verification fails due to slight drift after mouseUp)
  2. ~300ms of post-move delays (250ms settle wait, item re-fetch, 25ms extra sleep) before the click fires

Fix

Add a fastPath mode for IceBar clicks that:

  • Limits move() to 1 attempt (via new maxMoveAttempts parameter, default 8 for existing callers)
  • Skips the settle wait, item re-fetch, and extra sleep between move and click

This reduces the visible jitter from ~1.8s to a brief flash (~100ms).

Changes

  • MenuBarItemManager.swift: Add maxMoveAttempts parameter to move() and fastPath parameter to temporarilyShow()
  • IceBar.swift: Pass fastPath: true for both left and right click handlers

Testing

Tested on macOS Tahoe with 30+ menu bar items across two displays. Hidden items clicked from the IceBar now open their menus with minimal visible jitter. No regression for non-IceBar move operations (layout, rehide, etc.) which continue using the default 8-attempt path.

Summary by CodeRabbit

  • Bug Fixes
    • Enhanced menu bar item interaction performance by optimizing the display logic for off-screen items, reducing processing overhead and improving responsiveness when accessing menu bar controls.

When clicking hidden items from the IceBar, the Cmd+drag move() function
retried up to 8 times with exponentially increasing timeouts (~1.8s of
visible icon jitter), even though the first attempt always repositioned
the item close enough. Post-move delays (250ms settle wait, item
re-fetch, 25ms extra sleep) added further latency before the click.

Changes:
- Add maxMoveAttempts parameter to move() (default 8, no behavior change
  for existing callers)
- Add fastPath parameter to temporarilyShow() that limits move to 1
  attempt and skips post-move delays
- IceBar click handlers now use fastPath: true for both left and right
  clicks
@coderabbitai
Copy link
Copy Markdown
Contributor

coderabbitai bot commented Mar 28, 2026

📝 Walkthrough

Walkthrough

The changes introduce a "fast path" mode for menu bar item visibility handling. The temporarilyShow function now accepts a fastPath parameter that reduces retry attempts and skips post-move synchronization when enabled. The move function gains a configurable maxMoveAttempts parameter for flexible retry control.

Changes

Cohort / File(s) Summary
IceBar fallback handling
Thaw/MenuBar/IceBar/IceBar.swift
Updated left-click and right-click fallback control flow to invoke temporarilyShow with fastPath: true, enabling the optimized fast path mode.
MenuBarItemManager coordination
Thaw/MenuBar/MenuBarItems/MenuBarItemManager.swift
Added maxMoveAttempts parameter to move() function; extended temporarilyShow() with fastPath parameter that, when enabled, limits retries to 1 and skips position settlement and re-fetching steps.

Estimated code review effort

🎯 3 (Moderate) | ⏱️ ~20 minutes

Suggested reviewers

  • stonerl

Poem

🐰 A swift path through the menu bar we hop,
Fast moves with fewer retries, we don't stop!
Synchronization streamlined, no extra wait,
Click targets true—optimization, how great! 🚀

🚥 Pre-merge checks | ✅ 3
✅ Passed checks (3 passed)
Check name Status Explanation
Title check ✅ Passed The title clearly and specifically describes the main change: reducing IceBar click jitter through a fast-path optimization.
Description check ✅ Passed The description comprehensively covers the problem, solution, code changes, and testing, providing clear context for the fix.
Docstring Coverage ✅ Passed Docstring coverage is 100.00% which is sufficient. The required threshold is 80.00%.

✏️ Tip: You can configure your own custom pre-merge checks in the settings.

✨ Finishing Touches
🧪 Generate unit tests (beta)
  • Create PR with unit tests

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 and usage tips.

@sonarqubecloud
Copy link
Copy Markdown

Copy link
Copy Markdown
Contributor

@coderabbitai coderabbitai bot left a comment

Choose a reason for hiding this comment

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

Actionable comments posted: 1

🤖 Prompt for all review comments with AI agents
Verify each finding against the current code and only fix it if needed.

Inline comments:
In `@Thaw/MenuBar/MenuBarItems/MenuBarItemManager.swift`:
- Around line 1953-1954: The parameter maxMoveAttempts can be <= 0 and will
crash the loop "for n in 1 ... maxAttempts"; fix this by validating or clamping
it before the range is built (e.g., compute let attempts = max(1,
maxMoveAttempts) or guard maxMoveAttempts > 0 else { return } ), then use that
safe value in the loop ("for n in 1 ... attempts"). Update the initializer/
method signature handling the watchdogTimeout/maxMoveAttempts parameters
(referencing maxMoveAttempts and the loop that uses maxAttempts) to ensure no
runtime trap occurs.
🪄 Autofix (Beta)

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

Run ID: ee5887f9-7241-4a6d-8756-60c17f8f8ef9

📥 Commits

Reviewing files that changed from the base of the PR and between 926d548 and 4460945.

📒 Files selected for processing (2)
  • Thaw/MenuBar/IceBar/IceBar.swift
  • Thaw/MenuBar/MenuBarItems/MenuBarItemManager.swift

Comment on lines +1953 to +1954
watchdogTimeout: DispatchTimeInterval? = nil,
maxMoveAttempts: Int = 8
Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

⚠️ Potential issue | 🟠 Major

🧩 Analysis chain

🏁 Script executed:

#!/bin/bash
# Read-only verification:
# 1) Locate all call sites passing maxMoveAttempts.
# 2) Confirm move() still uses a closed range over maxAttempts.
# 3) Confirm there is (or isn't) a guard/clamp for non-positive values.

rg -nP --type=swift '\bmaxMoveAttempts\s*:'
rg -nP --type=swift 'for\s+\w+\s+in\s+1\s*\.\.\.\s*maxAttempts'
rg -nP --type=swift 'maxMoveAttempts\s*<\s*1|max\(\s*1\s*,\s*maxMoveAttempts\s*\)'

Repository: stonerl/Thaw

Length of output: 515


🏁 Script executed:

# Check the file size first
wc -l Thaw/MenuBar/MenuBarItems/MenuBarItemManager.swift

Repository: stonerl/Thaw

Length of output: 111


🏁 Script executed:

# Read the move() function around lines 1953-2020 to see the full signature and guard logic
sed -n '1945,2020p' Thaw/MenuBar/MenuBarItems/MenuBarItemManager.swift | cat -n

Repository: stonerl/Thaw

Length of output: 3598


🏁 Script executed:

# Also read around the second loop at line 2188 to understand context
sed -n '2180,2200p' Thaw/MenuBar/MenuBarItems/MenuBarItemManager.swift | cat -n

Repository: stonerl/Thaw

Length of output: 1180


Guard maxMoveAttempts to prevent a runtime crash.

At line 2014, for n in 1 ... maxAttempts will trap if maxMoveAttempts <= 0. The parameter accepts any integer without validation, so a caller could pass an invalid value. Add a guard or clamp before building the range.

🛠️ Proposed fix
 func move(
     item: MenuBarItem,
     to destination: MoveDestination,
     on displayID: CGDirectDisplayID? = nil,
     skipInputPause: Bool = false,
     watchdogTimeout: DispatchTimeInterval? = nil,
     maxMoveAttempts: Int = 8
 ) async throws {
+    let maxAttempts = max(1, maxMoveAttempts)
+    if maxMoveAttempts < 1 {
+        MenuBarItemManager.diagLog.warning("move(): received invalid maxMoveAttempts=\(maxMoveAttempts), clamping to 1")
+    }
+
     guard item.isMovable else {
         throw EventError.itemNotMovable(item)
     }
@@
-    let maxAttempts = maxMoveAttempts
     for n in 1 ... maxAttempts {
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@Thaw/MenuBar/MenuBarItems/MenuBarItemManager.swift` around lines 1953 - 1954,
The parameter maxMoveAttempts can be <= 0 and will crash the loop "for n in 1
... maxAttempts"; fix this by validating or clamping it before the range is
built (e.g., compute let attempts = max(1, maxMoveAttempts) or guard
maxMoveAttempts > 0 else { return } ), then use that safe value in the loop
("for n in 1 ... attempts"). Update the initializer/ method signature handling
the watchdogTimeout/maxMoveAttempts parameters (referencing maxMoveAttempts and
the loop that uses maxAttempts) to ensure no runtime trap occurs.

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

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant