Skip to content

Destroy Break Overlay Window After Dismissal + Lazy NSPopover creation - #27

Open
karmeye wants to merge 2 commits into
preetsuthar17:mainfrom
karmeye:main
Open

Destroy Break Overlay Window After Dismissal + Lazy NSPopover creation#27
karmeye wants to merge 2 commits into
preetsuthar17:mainfrom
karmeye:main

Conversation

@karmeye

@karmeye karmeye commented Aug 5, 2026

Copy link
Copy Markdown

Destroy Break Overlay Window After Dismissal

🤖:

Problem

BreakOverlayWindowController.hide() called OverlayWindowHelper.dismissOverlay() which only performed window.orderOut(nil) — the NSWindow was never destroyed. The window, its NSHostingView, and the FloatingOrb CALayers with .repeatForever blur+offset animations persisted indefinitely on the hidden window, consuming CPU after every break overlay dismissed.

Changes (2 files, +20/-2 lines)

Sources/AppShell/OverlayWindowHelper.swift:

  1. cancelAnimations: Added window.contentView?.layer?.removeAllAnimations() — stops stale FloatingOrb .repeatForever CAAnimations immediately when a window is being recycled for a new overlay
  2. dismissOverlay: Added completion: @escaping @MainActor () -> Void = {} parameter — allows callers to perform post-dismiss cleanup. Default {} preserves backward compatibility with OnboardingFlowWindowController

Sources/AppShell/BreakOverlayWindowController.swift:

  1. hide(): Passes a completion handler to dismissOverlay that nils self.window after the fade-out completes, guarded by self?.window === window identity check — this prevents the window from being prematurely deallocated if show() reuses it concurrently

Safety

  • The existing dismissGeneration guard in dismissOverlay (line 93) prevents the completion from firing if a new overlay was shown (presentOverlay increments dismissGeneration)
  • The self?.window === window identity check is an additional safety net: if show() reuses the window before hide()'s completion fires, the reference count won't be incorrectly nilled
  • OnboardingFlowWindowController.hide() is unaffected (uses default {} completion)
  • The window is recreated fresh on next show() via self.window ?? OverlayWindowHelper.makeFullscreenWindow()

Impact

  • Before: .repeatForever blur animations on FloatingOrb layers continued running on hidden-but-retained windows at 60-120 Hz indefinitely after overlay dismissal
  • After: Window is fully deallocated after the 0.4s fade-out → ARC releases NSHostingView → CALayer tree deallocates → all CAAnimations immediately stop

Lazy NSPopover creation

🤖:

The NSPopover + StatusMenuView SwiftUI tree is now created lazily on first openPopover and destroyed (popover = nil) on closePopover. This stops the 1Hz objectWillChange re-renders from hitting the hidden StatusMenuView with its expensive MenuRowShape path calculations.

@karmeye karmeye changed the title Destroy Break Overlay Window After Dismissal Destroy Break Overlay Window After Dismissal + Lazy NSPopover creation Aug 5, 2026
@karmeye

karmeye commented Aug 5, 2026

Copy link
Copy Markdown
Author

Improments from these changes:

2

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