Skip to content

fix(capture): tolerate menu-bar item bounds that overshoot NSScreen.frame#614

Merged
stonerl merged 1 commit into
developmentfrom
fix/perf-memleak-regression
May 24, 2026
Merged

fix(capture): tolerate menu-bar item bounds that overshoot NSScreen.frame#614
stonerl merged 1 commit into
developmentfrom
fix/perf-memleak-regression

Conversation

@nightah
Copy link
Copy Markdown
Collaborator

@nightah nightah commented May 24, 2026

What does this PR do?

Fixes a regression introduced by #613 where the strict display.frame.contains(...) precondition in Bridging.captureWindowsImageSCK rejected status-item windows whose bounds report a few pixels past NSScreen.frame.maxX (observed on the Clock and Thaw's own status item). The SCK capture silently returned nil, so the affected icons disappeared from Settings → Menu Bar Layout and the Search bar. Replaces the contains guard with a largest-intersection-area display selection that tolerates small edge overshoots while still rejecting truly orphan windows.

PR Type

  • Bugfix
  • CI/CD related changes
  • Code style update (formatting, renaming)
  • Documentation
  • Feature
  • Refactor
  • Performance improvement
  • Test addition or update
  • Other (please describe):

Does this PR introduce a breaking change?

  • Yes
  • No

If yes, please describe the impact and migration path:

N/A. Function signature and external behaviour for healthy captures are unchanged. The fix only relaxes a precondition that was rejecting valid captures.

What is the current behavior?

After #613, Bridging.captureWindowsImageSCK selects the host display via content.displays.first(where: { $0.frame.contains(effectiveBounds) && $0.frame.contains(unionBounds) }). macOS 26's WindowServer routinely reports status-item bounds that overshoot NSScreen.frame.maxX by a couple of pixels (observed example: Clock at (1029, 0, 443, 33) on a 1470-wide display: the bounds end at x=1472, two pixels past the display's right edge). The strict contains rejects every such case as "no display fully contains effectiveBounds", returns nil, and the downstream consumer (Layout settings preview, Search panel icon strip) renders nothing for that item. The Clock and Thaw's own status item are the most consistently affected because they sit at the trailing end of the menu bar.

Issue Number: N/A

What is the new behavior?

captureWindowsImageSCK now picks the display whose intersection with unionBounds has the largest area. The display holding the bulk of the window wins for edge-overshoot cases (SCK still clips the trailing few pixels at display.frame.maxX, which matches the legacy SLWindowListCreateImageFromArray behaviour that also clipped at the display edge, so the captured image is visually identical). Cross-display spans pick the majority side instead of accepting whichever display arbitrarily intersected first. Truly orphan windows whose unionBounds doesn't intersect any display still return nil with a warning, so the defensive intent of #613's change is preserved for the case that warranted it. Diagnostic warning is updated to report both effectiveBounds and unionBounds so future rejections are debuggable.

PR Checklist

  • I've built and run the app locally
  • I've checked for console errors or crashes
  • I've run relevant tests and they pass
  • I've added or updated tests (if applicable)
  • I've updated documentation as needed

Other information

Reproduction was driven from the user-facing report: open Settings → Menu Bar Layout and check that the Clock and Thaw status items render in the layout preview; open the menu-bar Search panel and check that those icons appear in the row. Before this fix, both surfaces displayed empty slots for those items. Diagnostic confirmation in ~/Library/Logs/Thaw/thaw_*.log showed the rejection lines captureWindowsImageSCK: no display fully contains effectiveBounds=(1029.0, 0.0, 443.0, 33.0) and unionBounds=(1029.0, 0.0, 443.0, 33.0) firing once per affected item per refresh tick. After the fix, those warnings disappear and captureWindowsImageSCK: captured N windows -> WxHpx lines replace them.

Notes for reviewers

Largest-intersection was preferred over reverting to the original intersects-with-fallback chain because the fallback chain ended in content.displays.first (any display), which was looser than necessary and could have picked an unrelated display. Largest-intersection is genuinely better than both: it tolerates the macOS-26 overshoot we observed empirically, picks the right display for cross-monitor spans, and rejects truly orphan windows so callers still fall back / skip cleanly. The diagnostic warning now includes both effectiveBounds and unionBounds so future regressions report enough state to investigate without instrumentation.

Summary by CodeRabbit

Bug Fixes

  • Improved window capture reliability on multi-monitor setups. Enhanced display selection logic now identifies the most appropriate display based on visual intersection area, providing more robust handling of complex display configurations.

Review Change Stack

…rame

Status-item windows on macOS 26 routinely report bounds that extend a couple of pixels past `NSScreen.frame.maxX` (observed on the Clock and Thaw's own status item: bounds = (1029, 0, 443, 33) on a 1470-wide display, two pixels past the right edge). The strict `frame.contains` requirement introduced in 2ddc419 rejected every such capture as "no display fully contains effectiveBounds", so the SCK path silently returned nil and the affected icons disappeared from Settings → Menu Bar Layout and the Search bar.

Replaces the contains-both guard with a largest-intersection-area pick: each display's intersection with `unionBounds` is measured and the display holding the largest area wins. Edge-overshoot cases capture against the display that holds the bulk of the window and SCK clips the trailing few pixels (visually identical to the legacy `SLWindowListCreateImageFromArray` behaviour, which also clipped at the display edge). Cross-display spans pick the majority side instead of "any silent partial". Truly orphan windows whose `unionBounds` doesn't intersect any display still return nil with a warning, so the defensive intent of the original change is preserved for the case that actually warranted it.
@coderabbitai
Copy link
Copy Markdown
Contributor

coderabbitai Bot commented May 24, 2026

No actionable comments were generated in the recent review. 🎉

ℹ️ Recent review info
⚙️ Run configuration

Configuration used: defaults

Review profile: CHILL

Plan: Pro

Run ID: 59d3f11e-68b0-4b89-be4c-835dab33fffc

📥 Commits

Reviewing files that changed from the base of the PR and between ea420b8 and 3e9d0fd.

📒 Files selected for processing (1)
  • Shared/Bridging/Bridging.swift

📝 Walkthrough

Walkthrough

The captureWindowsImageSCK function in Shared/Bridging/Bridging.swift updates its display-selection logic. Instead of requiring a single display whose frame fully contains both effectiveBounds and unionBounds, the code now selects the display with the largest intersection area against unionBounds, improving robustness when windows span multiple displays.

Changes

Display Intersection-Based Selection

Layer / File(s) Summary
Display intersection selection algorithm
Shared/Bridging/Bridging.swift
captureWindowsImageSCK computes the intersection area between each display's frame and unionBounds, selects the display with maximum overlap, and fails with an updated warning if no intersection exists.

Estimated code review effort

🎯 3 (Moderate) | ⏱️ ~20 minutes

Possibly related PRs

  • stonerl/Thaw#613: Earlier modification to the same captureWindowsImageSCK display-selection logic that this PR iterates upon.

Suggested reviewers

  • stonerl

Poem

🐰 A window's bounds spread far and wide,
Across the displays, side by side.
We find the best frame's intersection zone,
The largest match, we now have known.
Smart capture picks its perfect stage! ✨

🚥 Pre-merge checks | ✅ 5
✅ Passed checks (5 passed)
Check name Status Explanation
Title check ✅ Passed The title accurately and specifically describes the primary change: fixing display selection logic to tolerate status-item bounds that overshoot NSScreen.frame, which directly addresses the regression mentioned in the PR.
Description check ✅ Passed The description is comprehensive and complete, covering all required template sections including what the PR does, type (Bugfix), breaking changes (No), current behavior, new behavior, checklist items, and detailed notes for reviewers.
Docstring Coverage ✅ Passed Docstring coverage is 100.00% which is sufficient. The required threshold is 80.00%.
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.

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

✨ Finishing Touches
📝 Generate docstrings
  • Create stacked PR
  • Commit on current branch
🧪 Generate unit tests (beta)
  • Create PR with unit tests
  • Commit unit tests in branch fix/perf-memleak-regression

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

@stonerl stonerl merged commit 856d08f into development May 24, 2026
7 checks passed
@nightah nightah deleted the fix/perf-memleak-regression branch May 25, 2026 08:02
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.

2 participants