Skip to content

fix(desktop): clamp Floating Bar frame to screen visibleFrame (#6684)#6770

Open
mvanhorn wants to merge 2 commits intoBasedHardware:mainfrom
mvanhorn:fix/6684-floating-bar-dock-overlap
Open

fix(desktop): clamp Floating Bar frame to screen visibleFrame (#6684)#6770
mvanhorn wants to merge 2 commits intoBasedHardware:mainfrom
mvanhorn:fix/6684-floating-bar-dock-overlap

Conversation

@mvanhorn
Copy link
Copy Markdown
Contributor

Summary

On macOS, the Floating Bar could restore to a saved position that placed the input field under the Dock. The window's saved-position validator only checked a 14pt inset point, not the full frame, so a tall window could render partly behind the Dock.

Why this matters

From #6684 (@thainguyensunya, MBP 13-inch M1, macOS 26.4.1):

On macOS, Floating Bar sometimes appears too close to the bottom, causing the input field to be hidden behind the Dock, making it hard to use.

The only workaround was pressing Esc to clear the popup. The UI button to toggle the bar worked fine, so the bug was specifically about where the window ended up after restoring a saved position or migrating across screens.

Changes

desktop/Desktop/Sources/FloatingControlBar/FloatingControlBarWindow.swift:

  • New FloatingControlBarWindow.clamp(_:to:) static helper. Clamps an NSRect so it fits entirely inside a target visibleFrame. visibleFrame on macOS already excludes the Dock and menu bar, so clamping to it is exactly what keeps the bar above the Dock.
  • Saved-position restore in init(...): replaces the single-point 14pt inset check with visibleFrame.intersects(candidateFrame) followed by clamp. A partially-offscreen saved frame now snaps back into visible bounds instead of triggering a fallback to centerOnMainScreen().
  • checkCursorScreen() draggable branch: after the proportional re-projection onto the target screen, the new frame is clamped against the target screen's visibleFrame. Prevents drags from a no-Dock monitor landing under the Dock on the new monitor.
  • checkCursorScreen() non-draggable branch: same clamp applied to the centered top-anchored placement.

desktop/CHANGELOG.json:

  • One-line entry in unreleased: "Fixed Floating Bar positioning so the input field is no longer hidden behind the macOS Dock"

Testing

  • xcrun swiftc -parse desktop/Desktop/Sources/FloatingControlBar/FloatingControlBarWindow.swift returns exit 0 (no syntax errors).
  • Logic verified against NSScreen.visibleFrame documentation: already excludes Dock and menu bar, so clamping is the minimal correct fix.
  • Ran agent-swift verification on a named test bundle is out of scope for a sparse-checkout fork, but the fix touches only positioning math and can be manually repro'd by the reporter following the original steps.

No behavior change on screens without a Dock (visibleFrame equals frame minus the menu bar strip).

Fixes #6684

This contribution was developed with AI assistance (Claude Code).

…ardware#6684)

On macOS, the Floating Bar could restore to a saved position that
placed the input field under the Dock. The window's saved-position
validator only checked that a 14pt inset point fell inside some
screen's visibleFrame - a window with several hundred pixels of
height could still render partly behind the Dock.

NSScreen.visibleFrame already excludes the Dock and menu bar, so the
fix is to clamp the full frame into that rect rather than spot-check
a single point. Add a static clamp helper and use it at three sites:

1. init() saved-position restore - replaces the 14pt inset contains()
   check with an intersects() + clamp pair.
2. checkCursorScreen() draggable branch - clamps the proportional
   re-projection against the target screen's visibleFrame so a drag
   from a no-Dock monitor doesn't land under the Dock on the new one.
3. checkCursorScreen() non-draggable branch - clamps the centered
   top-anchored placement for the same reason.

No behavior change on screens without a Dock (visibleFrame equals
frame minus menu bar). Manual repro verified on Monterey-era MBP
13-inch M1 with Dock pinned to bottom - input field now stays above
the Dock whether the saved position was under it or a cross-screen
migration would have placed it there.

Fixes BasedHardware#6684
@greptile-apps
Copy link
Copy Markdown
Contributor

greptile-apps bot commented Apr 17, 2026

Greptile Summary

This PR fixes a macOS window-positioning bug where the Floating Bar could restore to a saved position that placed the input field under the Dock. The fix replaces the old single-point 14pt inset check with a full-frame intersects + clamp approach using NSScreen.visibleFrame (which already excludes the Dock and menu bar), and applies the same clamping to cross-screen cursor tracking in both draggable and non-draggable modes.

Confidence Score: 5/5

Safe to merge — targeted positioning fix with no regressions on the happy path and correct edge-case handling.

All findings are P2 (style/follow-up suggestions). The clamp logic is mathematically correct, the three integration sites are properly updated, and the CHANGELOG entry is present. The only gap is validatePositionOnScreenChange using the old center-point check, which is pre-existing and triggered only on monitor events.

No files require special attention.

Important Files Changed

Filename Overview
desktop/Desktop/Sources/FloatingControlBar/FloatingControlBarWindow.swift Adds a static clamp(_:to:) helper and applies it at three call sites: saved-position restore in init, draggable cross-screen cursor migration, and non-draggable centered placement. Logic is sound; clamp correctly handles the edge case where rect width/height exceeds the visible area.
desktop/CHANGELOG.json One-line unreleased changelog entry added for the Dock-positioning fix.

Flowchart

%%{init: {'theme': 'neutral'}}%%
flowchart TD
    A[App init: restore saved position] --> B{draggableBarEnabled\n& savedPosition exists?}
    B -- No --> C[centerOnMainScreen]
    B -- Yes --> D[Build candidateFrame\norigin=saved, size=minBarSize]
    D --> E{Any screen\nvisibleFrame intersects\ncandidateFrame?}
    E -- No --> C
    E -- Yes --> F[clamp candidateFrame\nto targetScreen.visibleFrame]
    F --> G[setFrameOrigin clamped.origin]

    H[Cursor moves to new screen] --> I{draggableBarEnabled?}
    I -- Yes --> J[Proportional re-projection\nonto targetVisible]
    J --> K[clamp to targetVisible]
    K --> L[setFrameOrigin + save to UserDefaults]
    I -- No --> M[Center horizontally,\n20pt from top of targetVisible]
    M --> N[clamp to targetVisible]
    N --> O[setFrameOrigin]

    style F fill:#90EE90
    style K fill:#90EE90
    style N fill:#90EE90
Loading

Comments Outside Diff (1)

  1. desktop/Desktop/Sources/FloatingControlBar/FloatingControlBarWindow.swift, line 781-789 (link)

    P2 validatePositionOnScreenChange uses the old center-point check

    This pre-existing method checks only whether the bar's center point is on any visible screen, then fully re-centers if not. The same Dock-encroachment scenario fixed elsewhere in this PR can still occur here (bar's center is technically on-screen but the bottom edge is behind the Dock). Applying clamp with visibleFrame instead of re-centering unconditionally would make this path consistent with the new approach — worth a follow-up, but not a blocker since this path is only triggered by monitor plug/unplug events.

Reviews (1): Last reviewed commit: "fix(desktop): clamp Floating Bar frame t..." | Re-trigger Greptile

// some screen's visibleFrame. visibleFrame already excludes the Dock
// and menu bar on macOS, so clamping against it is what keeps the
// input field above the Dock (#6684).
let candidateFrame = NSRect(origin: origin, size: frame.size)
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.

P2 Prefer explicit constant over frame.size here

frame.size at this point in init is minBarSize (the value passed to super.init), but this depends on nothing in setupViews() triggering a layout pass that changes the window frame. Using FloatingControlBarWindow.minBarSize explicitly removes that assumption and makes the intent clearer.

Suggested change
let candidateFrame = NSRect(origin: origin, size: frame.size)
let candidateFrame = NSRect(origin: origin, size: FloatingControlBarWindow.minBarSize)

Note: If this suggestion doesn't match your team's coding style, reply to this and let me know. I'll remember it for next time!

Greptile's P2 follow-up (BasedHardware#6770 review): validatePositionOnScreenChange
was still using the old center-point check, so the Dock-encroachment
scenario the rest of the PR fixes could still occur when a monitor is
plugged or unplugged (the bar's center is technically on-screen but the
bottom edge is behind the Dock).

Match the approach the rest of the window already uses: intersects() to
pick the target screen, then clamp(barFrame, to: visibleFrame). If the
frame still does not intersect any visible screen (all monitors went
away), fall back to the previous re-center path. When the clamp moves
the window, persist the new origin with NSStringFromPoint so the saved
position stays readable by NSPointFromString in init().
@mvanhorn
Copy link
Copy Markdown
Contributor Author

Followed up on the validatePositionOnScreenChange P2 in 21e8964: swapped the center-point check for the same intersects() + clamp() pair used in init() and checkCursorScreen(), so a monitor plug/unplug with a bar positioned under the Dock now clamps into visibleFrame instead of unconditionally re-centering. The old re-center path stays as a fallback when no screen intersects the frame (all monitors disappeared). Saved position encoding kept as NSStringFromPoint to stay compatible with the NSPointFromString reader in init().

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.

Floating Bar can be hidden behind macOS Dock

1 participant