From 554dce57269e2ae24f5de9c39972cacbc0a350dc Mon Sep 17 00:00:00 2001
From: Ariver <shanghai3168@gmail.com>
Date: Tue, 19 May 2026 03:03:21 +0800
Subject: [PATCH] Fix stale app hover bubbles

---
 Apptag/ApptagApp.swift |   58 ++++++++++++++++++++++++++++++++++++++++++++++++++--------
 1 files changed, 50 insertions(+), 8 deletions(-)

diff --git a/Apptag/ApptagApp.swift b/Apptag/ApptagApp.swift
index daf5d45..72a1665 100644
--- a/Apptag/ApptagApp.swift
+++ b/Apptag/ApptagApp.swift
@@ -29,6 +29,9 @@
 
 final class AppDelegate: NSObject, NSApplicationDelegate {
     private static let showDockIconKey = "showDockIcon"
+    private static let statusItemAutosaveName = "com.apptag.launcher.statusItem"
+    private static let statusItemButtonIdentifier = NSUserInterfaceItemIdentifier("TagLauncherStatusItemButton")
+    private static let statusItemAccessibilityLabel = "TagLauncher"
     private static let showAppListMenuItemIdentifier = NSUserInterfaceItemIdentifier("TagLauncherShowAppListMenuItem")
     private static let showAppListShortcutGlyphs = "⌥⇧␣"
     private static let overlayDefaultLevel = NSWindow.Level(rawValue: Int(CGWindowLevelForKey(.maximumWindow)))
@@ -192,12 +195,19 @@
     // MARK: - Menu Bar
 
     private func setupMenuBar() {
-        removeMenuBarItem()
-        statusItem = NSStatusBar.system.statusItem(withLength: NSStatusItem.squareLength)
+        if statusItem == nil {
+            statusItem = NSStatusBar.system.statusItem(withLength: NSStatusItem.squareLength)
+        } else {
+            statusItem?.length = NSStatusItem.squareLength
+        }
         guard let statusItem else { return }
+        statusItem.autosaveName = Self.statusItemAutosaveName
         statusItem.isVisible = true
 
         if let button = statusItem.button {
+            button.identifier = Self.statusItemButtonIdentifier
+            button.setAccessibilityIdentifier(Self.statusItemAutosaveName)
+            button.setAccessibilityLabel(Self.statusItemAccessibilityLabel)
             button.image = makeMenuBarIcon()
             button.imageScaling = .scaleProportionallyDown
             button.imagePosition = .imageOnly
@@ -499,15 +509,22 @@
             NSMouseInRect(mousePoint, $0.frame, false)
         }) ?? NSScreen.main ?? NSScreen.screens.first else { return }
 
-        overlayWindow?.orderOut(nil)
-        overlayWindow = makeOverlayWindow(on: screen)
-        overlayWindow?.setFrame(screen.frame, display: true)
-        overlayWindow?.level = isEditingAppNote ? Self.overlayTextInputLevel : Self.overlayDefaultLevel
+        let window: NSWindow
+        if let existingWindow = overlayWindow {
+            window = existingWindow
+        } else {
+            window = makeOverlayWindow(on: screen)
+            overlayWindow = window
+        }
+
+        window.setFrame(screen.frame, display: true)
+        window.level = isEditingAppNote ? Self.overlayTextInputLevel : Self.overlayDefaultLevel
 
         installOverlayKeyMonitor()
 
-        overlayWindow?.makeKeyAndOrderFront(nil)
-        overlayWindow?.orderFrontRegardless()
+        window.makeKeyAndOrderFront(nil)
+        window.orderFrontRegardless()
+        NotificationCenter.default.post(name: .tagLauncherOverlayDidShow, object: nil)
     }
 
     private func installOverlayKeyMonitor() {
@@ -569,6 +586,7 @@
         }
         overlayWindow?.orderOut(nil)
         removeOverlayKeyMonitor()
+        NotificationCenter.default.post(name: .tagLauncherOverlayDidHide, object: nil)
         if force {
             overlayWindow = nil
         }
@@ -819,15 +837,25 @@
 
 final class DismissibleHostingView<Content: View>: NSHostingView<Content> {
     private let onBackdropTap: () -> Void
+    private var suppressBackdropDismiss = false
+    private var modalInteractionObserver: NSObjectProtocol?
 
     @MainActor required init(rootView: Content) {
         self.onBackdropTap = {}
         super.init(rootView: rootView)
+        observeModalInteractionChanges()
     }
 
     init(rootView: Content, onBackdropTap: @escaping () -> Void) {
         self.onBackdropTap = onBackdropTap
         super.init(rootView: rootView)
+        observeModalInteractionChanges()
+    }
+
+    deinit {
+        if let modalInteractionObserver {
+            NotificationCenter.default.removeObserver(modalInteractionObserver)
+        }
     }
 
     @available(*, unavailable)
@@ -840,6 +868,10 @@
             return
         }
         if hit == self {
+            if suppressBackdropDismiss {
+                super.mouseDown(with: event)
+                return
+            }
             onBackdropTap()
             return
         }
@@ -862,6 +894,16 @@
         super.mouseDown(with: event)
     }
 
+    private func observeModalInteractionChanges() {
+        modalInteractionObserver = NotificationCenter.default.addObserver(
+            forName: .tagLauncherModalInteractionChanged,
+            object: nil,
+            queue: .main
+        ) { [weak self] notification in
+            self?.suppressBackdropDismiss = (notification.userInfo?["active"] as? Bool) ?? false
+        }
+    }
+
     private func findTextFieldContainer(in view: NSView) -> TextFieldContainer? {
         if let container = view as? TextFieldContainer { return container }
         for sub in view.subviews {

--
Gitblit v1.9.3