Ariver
2026-06-30 70a1cc2e63d2c49a135bd89cec3ffb1d427d90ee
src/Apptag/ApptagApp.swift
@@ -17,7 +17,7 @@
        Settings {
            PreferencesView()
        }
        .defaultSize(width: 1000, height: 480)
        .defaultSize(width: 1000, height: 640)
        .commands {
            CommandGroup(replacing: .systemServices) { }
            CommandGroup(replacing: .appVisibility) { }
@@ -28,29 +28,44 @@
// MARK: - App Delegate (menubar + overlay window + hotkey)
private enum HotkeyRegistrationAttempt {
    case success(EventHotKeyRef)
    case failure(OSStatus)
}
final class AppDelegate: NSObject, NSApplicationDelegate, NSMenuDelegate {
    static private(set) weak var shared: AppDelegate?
    private static let showDockIconKey = "showDockIcon"
    private static let statusItemAutosaveName = AppIdentity.statusItemAutosaveName
    private static let statusItemButtonIdentifier = NSUserInterfaceItemIdentifier("TagLauncherStatusItemButton")
    private static let statusItemAccessibilityLabel = AppIdentity.displayName
    private static let showAppListMenuItemIdentifier = NSUserInterfaceItemIdentifier("TagLauncherShowAppListMenuItem")
    private static let proStatusMenuItemIdentifier = NSUserInterfaceItemIdentifier("TagLauncherProStatusMenuItem")
    private static let helpMenuItemIdentifier = NSUserInterfaceItemIdentifier("TagLauncherHelpMenu")
    private static let downloadHelpMenuItemIdentifier = NSUserInterfaceItemIdentifier("TagLauncherDownloadHelpMenuItem")
    private static let externalActivationNotification = Notification.Name("TagLauncherExternalActivationRequested")
    private static let externalActivationObject = AppIdentity.bundleIdentifier
    private static let externalInvocationScheme = "taglauncher"
    private static let externalInvocationShowHost = "show"
    private static let duplicateLaunchSuppressReopenKey = "duplicateLaunchSuppressReopenAt"
    private static let overlaySpaceHotkeyID: UInt32 = 3
    private static let launcherOverlayLevel = NSWindow.Level(rawValue: NSWindow.Level.mainMenu.rawValue - 1)
    private static let overlayDefaultLevel = launcherOverlayLevel
    private static let overlayTextInputLevel = launcherOverlayLevel
    private static let settingsContentSize = NSSize(width: 1000, height: 640)
    private var statusItem: NSStatusItem?
    private var overlayKeyMonitor: Any?
    private var quickSearchLocalMouseMonitor: Any?
    private var quickSearchExternalMouseMonitor: Any?
    private var settingsWindow: NSWindow?    // Track Settings window to keep it above overlay
    private var explicitPreferencesOpenRequestedAt = Date.distantPast
    private var mainHotkeyRef: EventHotKeyRef?
    private var quickSearchHotkeyRef: EventHotKeyRef?
    private var overlaySpaceHotkeyRef: EventHotKeyRef?
    private var hotkeyEventHandlerInstalled = false
    private var configuredHotkeysSuspendedForRecording = false
    private var isQuickSearchOpen = false
    private var quickSearchShouldHideOverlayOnClose = false
    private var quickSearchOnlyOverlaySession = false
@@ -63,6 +78,13 @@
    private var statusMenuScreenForNextOverlay: NSScreen?
    private var suppressReopenUntil = Date.distantPast
    private var overlayOpenedByQuickSearchOnly = false
    private var didFinishLaunching = false
    private var pendingShowOverlayInvocation = false
    override init() {
        super.init()
        Self.shared = self
    }
    private lazy var overlayController = OverlayWindowController(
        dependencies: OverlayWindowController.Dependencies(
@@ -125,14 +147,16 @@
            refreshChromeState: { [weak self] activate, avoidSpaceSwitch in
                self?.refreshLauncherChromeState(activate: activate, avoidSpaceSwitch: avoidSpaceSwitch)
            },
            onWillHide: {
            onWillHide: { [weak self] in
                self?.unregisterOverlaySpaceHotkey()
                TagDatabase.flushPendingCategorySchemeBackupBatch()
            },
            onDidHide: { [weak self] in
                self?.overlayOpenedByQuickSearchOnly = false
                NotificationCenter.default.post(name: .tagLauncherOverlayDidHide, object: nil)
            },
            onDidShow: {
            onDidShow: { [weak self] in
                self?.refreshOverlaySpaceHotkeyRegistration()
                NotificationCenter.default.post(name: .tagLauncherOverlayDidShow, object: nil)
            }
        )
@@ -192,8 +216,10 @@
    func applicationDidFinishLaunching(_ notification: Notification) {
        AppDefaults.register()
        L10n.setup()
        ProEntitlementCenter.shared.start()
        migrateDefaultGroupName()
        TagDatabase.seedDefaultTags()
        _ = TagDatabase.relocalizeSystemTagsForCurrentLanguage()
        syncChromeSettings(force: true)
        observeHotkeyStatusChanges()
        registerConfiguredHotkeys()
@@ -207,8 +233,12 @@
        observeApplicationMenuChanges()
        observeChromeSettings()
        observeLanguageChanges()
        observeProEntitlementChanges()
        setupLaunchAtLogin()
        suppressReopenUntil = Date().addingTimeInterval(1.0)
        didFinishLaunching = true
        closeRestoredPreferencesWindowsDuringLaunch()
        consumePendingShowOverlayInvocationIfNeeded()
        configureApplicationMenuWhenAvailable(retries: 200)
        warmAppIndexInBackground()
        relocalizeDefaultAppNotesForCurrentLanguageAsync()
@@ -241,6 +271,54 @@
        return false  // Suppress default "unhide all windows" behavior
    }
    func application(_ application: NSApplication, open urls: [URL]) {
        for url in urls {
            handleExternalInvocationURL(url)
        }
    }
    private enum ExternalInvocationRoute {
        case showOverlay
    }
    private func handleExternalInvocationURL(_ url: URL) {
        guard externalInvocationRoute(for: url) == .showOverlay else {
            Diagnostics.log("app.externalInvocation.ignored", [
                "url": url.absoluteString
            ])
            return
        }
        requestShowOverlayFromExternalInvocation()
    }
    private func externalInvocationRoute(for url: URL) -> ExternalInvocationRoute? {
        guard url.scheme?.lowercased() == Self.externalInvocationScheme else {
            return nil
        }
        let host = url.host?.lowercased()
        let path = url.path
        if host == Self.externalInvocationShowHost, path.isEmpty || path == "/" {
            return .showOverlay
        }
        return nil
    }
    private func requestShowOverlayFromExternalInvocation() {
        guard didFinishLaunching else {
            pendingShowOverlayInvocation = true
            return
        }
        pendingShowOverlayInvocation = false
        suppressReopenUntil = Date().addingTimeInterval(1.0)
        dismissQuickSearchIfNeeded()
        showOrFocusOverlay()
    }
    private func consumePendingShowOverlayInvocationIfNeeded() {
        guard pendingShowOverlayInvocation else { return }
        requestShowOverlayFromExternalInvocation()
    }
    private func isRecentUserClickNearDockArea() -> Bool {
        let lastMouseDown = CGEventSource.secondsSinceLastEventType(
            .combinedSessionState,
@@ -250,12 +328,15 @@
            .combinedSessionState,
            eventType: .leftMouseUp
        )
        let recentMouseClick = min(lastMouseDown, lastMouseUp) < 1.2
        let recentMouseClick = min(lastMouseDown, lastMouseUp) < 0.9
        return recentMouseClick && isPointerNearDockArea()
    }
    private func isPointerNearDockArea() -> Bool {
        let mouse = NSEvent.mouseLocation
        let dockOrientation = UserDefaults(suiteName: "com.apple.dock")?
            .string(forKey: "orientation") ?? "bottom"
        let hiddenDockEdgeTolerance: CGFloat = 96
        return NSScreen.screens.contains { screen in
            let frame = screen.frame
            let visible = screen.visibleFrame
@@ -269,7 +350,16 @@
            let rightDock = visible.maxX < frame.maxX
                && mouse.x <= frame.maxX
                && mouse.x >= visible.maxX - 24
            return bottomDock || leftDock || rightDock
            let hiddenDockFallback: Bool
            switch dockOrientation {
            case "left":
                hiddenDockFallback = mouse.x <= frame.minX + hiddenDockEdgeTolerance
            case "right":
                hiddenDockFallback = mouse.x >= frame.maxX - hiddenDockEdgeTolerance
            default:
                hiddenDockFallback = mouse.y <= frame.minY + hiddenDockEdgeTolerance
            }
            return bottomDock || leftDock || rightDock || hiddenDockFallback
        }
    }
@@ -277,6 +367,7 @@
        hideOverlay(force: true, discardWindow: true)
        unregisterHotkey(for: .main)
        unregisterHotkey(for: .quickSearch)
        unregisterOverlaySpaceHotkey()
        TagDatabase.flushPendingCategorySchemeBackupBatch()
        removeOverlayKeyMonitor()
        removeQuickSearchExternalMouseMonitor()
@@ -329,11 +420,53 @@
        }
    }
    private func foregroundWindowForActivationPolicyRestore() -> NSWindow? {
        if let settingsWindow, settingsWindow.isVisible {
            return settingsWindow
        }
        if let overlayWindow, overlayWindow.isVisible {
            return overlayWindow
        }
        return NSApp.keyWindow
    }
    private func setLauncherActivationPolicy(_ desiredPolicy: NSApplication.ActivationPolicy) {
        guard NSApp.activationPolicy() != desiredPolicy else { return }
        let restoreWindow = desiredPolicy == .accessory && NSApp.isActive
            ? foregroundWindowForActivationPolicyRestore()
            : nil
        // Deactivate before switching regular -> accessory; otherwise Dock can
        // keep a stale running tile until Dock itself restarts.
        if desiredPolicy == .accessory && NSApp.isActive {
            NSApp.deactivate()
        }
        NSApp.setActivationPolicy(desiredPolicy)
        guard desiredPolicy == .accessory,
              let restoreWindow,
              restoreWindow.isVisible
        else { return }
        DispatchQueue.main.async { [weak self, weak restoreWindow] in
            guard let self,
                  let restoreWindow,
                  restoreWindow.isVisible,
                  self.requiresForegroundOwnership
            else { return }
            NSApp.activate(ignoringOtherApps: true)
            restoreWindow.makeKeyAndOrderFront(nil)
            restoreWindow.orderFrontRegardless()
        }
    }
    private func beginLauncherForegroundOwnership(activate: Bool = true, keyWindow: NSWindow? = nil) {
        let showDock = UserDefaults.standard.bool(forKey: Self.showDockIconKey)
        let desiredPolicy: NSApplication.ActivationPolicy = showDock ? .regular : .accessory
        if NSApp.activationPolicy() != desiredPolicy {
            NSApp.setActivationPolicy(desiredPolicy)
            setLauncherActivationPolicy(desiredPolicy)
        }
        if activate, showDock {
            claimLauncherForeground(keyWindow: keyWindow)
@@ -350,7 +483,7 @@
        overlayGeneration expectedOverlayGeneration: Int? = nil
    ) {
        if NSApp.activationPolicy() != .regular {
            NSApp.setActivationPolicy(.regular)
            setLauncherActivationPolicy(.regular)
        }
        if isOverlayVisible && !NSApp.presentationOptions.contains(.hideDock) {
@@ -412,7 +545,7 @@
            ? .regular
            : (showDock ? .regular : .accessory))))
        if NSApp.activationPolicy() != desiredPolicy {
            NSApp.setActivationPolicy(desiredPolicy)
            setLauncherActivationPolicy(desiredPolicy)
        }
        let desiredPresentation: NSApplication.PresentationOptions = isOverlayVisible ? [.hideDock] : []
@@ -422,14 +555,17 @@
        if activate && requiresForegroundOwnership
            && !shouldStayAccessoryForCurrentFullscreenSpace
            && !shouldStayAccessoryForHiddenDockChrome
            && !shouldStayAccessoryForQuickOnlySearch {
            let keyWindow = isSettingsVisible ? settingsWindow : (isOverlayVisible ? overlayWindow : nil)
            claimLauncherForeground(
                keyWindow: keyWindow,
                retries: isOverlayVisible && !isSettingsVisible ? 5 : 0,
                overlayGeneration: isOverlayVisible ? overlayGeneration : nil
            )
            if shouldStayAccessoryForHiddenDockChrome {
                beginLauncherForegroundOwnership(activate: true, keyWindow: keyWindow)
            } else {
                claimLauncherForeground(
                    keyWindow: keyWindow,
                    retries: isOverlayVisible && !isSettingsVisible ? 5 : 0,
                    overlayGeneration: isOverlayVisible ? overlayGeneration : nil
                )
            }
        }
    }
@@ -604,6 +740,9 @@
        versionItem.isEnabled = false
        menu.addItem(versionItem)
        let proStatusItem = makeProStatusMenuItem()
        menu.addItem(proStatusItem)
        menu.addItem(.separator())
        let prefsItem = NSMenuItem(
            title: tr("menu.preferences"),
@@ -640,6 +779,7 @@
    }
    func menuWillOpen(_ menu: NSMenu) {
        configureProStatusMenuItem(in: menu)
        statusMenuScreenForNextOverlay = overlayController.screenContainingCurrentPointer()
            ?? statusItem?.button?.window?.screen
            ?? NSScreen.main
@@ -712,11 +852,58 @@
        return image
    }
    private func makeProStatusMenuItem() -> NSMenuItem {
        let item = NSMenuItem(
            title: "",
            action: #selector(openProStatusFromStatusMenu(_:)),
            keyEquivalent: ""
        )
        item.identifier = Self.proStatusMenuItemIdentifier
        item.target = self
        configureProStatusMenuItem(item)
        return item
    }
    private func configureProStatusMenuItem(in menu: NSMenu) {
        guard let item = menu.items.first(where: { $0.identifier == Self.proStatusMenuItemIdentifier }) else {
            return
        }
        configureProStatusMenuItem(item)
    }
    private func configureProStatusMenuItem(_ item: NSMenuItem) {
        let isUnlocked = ProEntitlementPolicy.accessState().isUnlocked
        item.title = tr(isUnlocked ? "menu.proStatus.unlocked" : "menu.proStatus.free")
        item.action = #selector(openProStatusFromStatusMenu(_:))
        item.target = self
        item.isEnabled = true
        item.image = isUnlocked ? makeProStatusMenuIcon() : nil
    }
    private func makeProStatusMenuIcon() -> NSImage? {
        guard let symbol = NSImage(systemSymbolName: "crown.fill", accessibilityDescription: "Pro") else {
            return nil
        }
        let configuration = NSImage.SymbolConfiguration(pointSize: 13, weight: .semibold)
        let configuredSymbol = symbol.withSymbolConfiguration(configuration) ?? symbol
        let image = NSImage(size: NSSize(width: 16, height: 16))
        image.lockFocus()
        NSColor(red: 0.96, green: 0.63, blue: 0.10, alpha: 1.0).set()
        let iconRect = NSRect(x: 1.5, y: 1.5, width: 13, height: 13)
        configuredSymbol.draw(in: iconRect, from: .zero, operation: .sourceOver, fraction: 1.0)
        NSColor(red: 0.96, green: 0.63, blue: 0.10, alpha: 1.0).set()
        iconRect.fill(using: .sourceAtop)
        image.unlockFocus()
        image.isTemplate = false
        image.accessibilityDescription = "Pro"
        return image
    }
    private var showAppListMenuTitle: String {
        if LauncherHotkeyRegistrationStore.state(for: .main) == .failed {
            return tr("menu.showShortcutUnavailable")
        }
        return "\(tr("menu.showAppList"))  \(LauncherHotkey.main.displayString)"
        return "\(tr("menu.showAppList"))  \(LauncherHotkeySettings.effectiveHotkey(for: .main).displayString)"
    }
    private func observeApplicationMenuChanges() {
@@ -1094,14 +1281,106 @@
    // MARK: - Global Hotkeys
    private func registerConfiguredHotkeys() {
        guard !configuredHotkeysSuspendedForRecording else { return }
        installHotkeyEventHandlerIfNeeded()
        registerFixedHotkey(.main)
        registerFixedHotkey(.quickSearch)
        registerEffectiveHotkey(.main)
        registerEffectiveHotkey(.quickSearch)
    }
    private func registerFixedHotkey(_ kind: LauncherHotkeyKind) {
    func suspendConfiguredHotkeysForRecording() {
        guard !configuredHotkeysSuspendedForRecording else { return }
        configuredHotkeysSuspendedForRecording = true
        unregisterHotkey(for: .main)
        unregisterHotkey(for: .quickSearch)
    }
    func resumeConfiguredHotkeysAfterRecording() {
        guard configuredHotkeysSuspendedForRecording else { return }
        configuredHotkeysSuspendedForRecording = false
        registerConfiguredHotkeys()
    }
    private func registerEffectiveHotkey(_ kind: LauncherHotkeyKind) {
        unregisterHotkey(for: kind)
        let hotkey = kind.hotkey
        let hotkey = LauncherHotkeySettings.effectiveHotkey(for: kind)
        registerAndStoreHotkey(hotkey, for: kind, markFailure: true)
    }
    @discardableResult
    func applyCustomHotkey(_ hotkey: LauncherHotkey, for kind: LauncherHotkeyKind) -> LauncherHotkeyCustomizationResult {
        guard ProEntitlementPolicy.isUnlocked(.customHotkeys) else {
            return .locked
        }
        if let error = LauncherHotkeySettings.validationError(for: hotkey, kind: kind) {
            return .invalid(error)
        }
        return activateHotkey(hotkey, for: kind, persistCustom: true)
    }
    @discardableResult
    func restoreDefaultHotkey(for kind: LauncherHotkeyKind) -> LauncherHotkeyCustomizationResult {
        guard ProEntitlementPolicy.isUnlocked(.customHotkeys) else {
            return .locked
        }
        return activateHotkey(LauncherHotkeySettings.defaultHotkey(for: kind), for: kind, persistCustom: false)
    }
    private func activateHotkey(
        _ hotkey: LauncherHotkey,
        for kind: LauncherHotkeyKind,
        persistCustom: Bool
    ) -> LauncherHotkeyCustomizationResult {
        installHotkeyEventHandlerIfNeeded()
        if LauncherHotkeySettings.effectiveHotkey(for: kind) == hotkey,
           hotkeyRef(for: kind) != nil {
            if persistCustom {
                LauncherHotkeySettings.save(hotkey, for: kind)
                LauncherHotkeyRegistrationStore.setActive(for: kind)
                return .saved
            }
            LauncherHotkeySettings.clearCustomHotkey(for: kind)
            LauncherHotkeyRegistrationStore.setActive(for: kind)
            return .restored
        }
        switch tryRegisterHotkey(hotkey, for: kind, markFailure: false) {
        case .success(let newRef):
            if let oldRef = hotkeyRef(for: kind) {
                UnregisterEventHotKey(oldRef)
            }
            setHotkeyRef(newRef, for: kind)
            if persistCustom {
                LauncherHotkeySettings.save(hotkey, for: kind)
                LauncherHotkeyRegistrationStore.setActive(for: kind)
                return .saved
            }
            LauncherHotkeySettings.clearCustomHotkey(for: kind)
            LauncherHotkeyRegistrationStore.setActive(for: kind)
            return .restored
        case .failure(let status):
            return .registrationFailed(status)
        }
    }
    private func registerAndStoreHotkey(
        _ hotkey: LauncherHotkey,
        for kind: LauncherHotkeyKind,
        markFailure: Bool
    ) {
        switch tryRegisterHotkey(hotkey, for: kind, markFailure: markFailure) {
        case .success(let newRef):
            setHotkeyRef(newRef, for: kind)
        case .failure:
            setHotkeyRef(nil, for: kind)
        }
    }
    private func tryRegisterHotkey(
        _ hotkey: LauncherHotkey,
        for kind: LauncherHotkeyKind,
        markFailure: Bool
    ) -> HotkeyRegistrationAttempt {
        var hotkeyID = EventHotKeyID()
        hotkeyID.signature = OSType(0x41505447) // 'APTG'
@@ -1118,12 +1397,16 @@
        )
        if status == noErr, let newRef {
            setHotkeyRef(newRef, for: kind)
            LauncherHotkeyRegistrationStore.setActive(for: kind)
            if markFailure {
                LauncherHotkeyRegistrationStore.setActive(for: kind)
            }
            return .success(newRef)
        } else {
            setHotkeyRef(nil, for: kind)
            LauncherHotkeyRegistrationStore.setFailed(status, for: kind)
            if markFailure {
                LauncherHotkeyRegistrationStore.setFailed(status, for: kind)
            }
            print("[TagLauncher] Fixed hotkey registration failed for \(kind.rawValue): \(status)")
            return .failure(status)
        }
    }
@@ -1133,6 +1416,18 @@
            object: nil,
            queue: .main
        ) { [weak self] _ in
            self?.setupMenuBar()
            self?.configureApplicationMenuWhenAvailable(retries: 2)
        }
    }
    private func observeProEntitlementChanges() {
        NotificationCenter.default.addObserver(
            forName: .tagLauncherProEntitlementChanged,
            object: nil,
            queue: .main
        ) { [weak self] _ in
            self?.registerConfiguredHotkeys()
            self?.setupMenuBar()
            self?.configureApplicationMenuWhenAvailable(retries: 2)
        }
@@ -1189,9 +1484,27 @@
        ])
        if id == LauncherHotkeyKind.quickSearch.eventID {
            showQuickSearchFromGlobalHotkey()
        } else if id == Self.overlaySpaceHotkeyID {
            handleOverlaySpaceHotkey()
        } else {
            performToggleOverlay(preferredScreen: nil)
        }
    }
    private func handleOverlaySpaceHotkey() {
        guard canUseOverlaySpaceHotkey else { return }
        unregisterOverlaySpaceHotkey()
        requestQuickSearch(source: QuickSearchOpenSource.mainOverlay)
    }
    private var canUseOverlaySpaceHotkey: Bool {
        overlayWindow?.isVisible == true
            && !isSettingsVisible
            && !isQuickSearchOpen
            && !quickSearchOnlyOverlaySession
            && !isInEditMode
            && !isEditingAppNote
            && !isModalInteractionActive
    }
    private func showQuickSearchFromGlobalHotkey() {
@@ -1249,6 +1562,46 @@
        }
    }
    private func refreshOverlaySpaceHotkeyRegistration() {
        if canUseOverlaySpaceHotkey {
            registerOverlaySpaceHotkeyIfNeeded()
        } else {
            unregisterOverlaySpaceHotkey()
        }
    }
    private func registerOverlaySpaceHotkeyIfNeeded() {
        guard overlaySpaceHotkeyRef == nil else { return }
        var hotkeyID = EventHotKeyID()
        hotkeyID.signature = OSType(0x41505447) // 'APTG'
        hotkeyID.id = Self.overlaySpaceHotkeyID
        var newRef: EventHotKeyRef?
        let status = RegisterEventHotKey(
            UInt32(kVK_Space),
            0,
            hotkeyID,
            GetApplicationEventTarget(),
            0,
            &newRef
        )
        if status == noErr, let newRef {
            overlaySpaceHotkeyRef = newRef
        } else {
            overlaySpaceHotkeyRef = nil
            Diagnostics.log("app.overlay.spaceHotkey.failed", ["status": status])
        }
    }
    private func unregisterOverlaySpaceHotkey() {
        if let overlaySpaceHotkeyRef {
            UnregisterEventHotKey(overlaySpaceHotkeyRef)
            self.overlaySpaceHotkeyRef = nil
        }
    }
    // MARK: - Preferences
    /// Hide overlay when any other window becomes key (catches Cmd+, via SwiftUI Settings).
@@ -1295,15 +1648,22 @@
    /// Settings must always appear centered over the current overlay view and float above it.
    private func prepareSettingsWindow(_ window: NSWindow) {
        dismissQuickSearchIfNeeded()
        let settingsSize = NSSize(width: 1000, height: 480)
        let settingsSize = Self.settingsContentSize
        let settingsFrameSize = window.frameRect(
            forContentRect: NSRect(origin: .zero, size: settingsSize)
        ).size
        window.identifier = NSUserInterfaceItemIdentifier("TagLauncherPreferencesWindow")
        window.minSize = settingsSize
        window.maxSize = settingsSize
        if abs(window.frame.width - settingsSize.width) > 0.5 || abs(window.frame.height - settingsSize.height) > 0.5 {
            window.setFrame(
                NSRect(origin: window.frame.origin, size: settingsSize),
                display: false
            )
        window.isRestorable = false
        window.restorationClass = nil
        _ = window.setFrameAutosaveName("")
        window.contentMinSize = settingsSize
        window.contentMaxSize = settingsSize
        window.minSize = settingsFrameSize
        window.maxSize = settingsFrameSize
        let currentContentSize = window.contentView?.bounds.size ?? window.contentLayoutRect.size
        if abs(currentContentSize.width - settingsSize.width) > 0.5
            || abs(currentContentSize.height - settingsSize.height) > 0.5 {
            window.setContentSize(settingsSize)
        }
        if let overlayWindow, overlayWindow.isVisible {
@@ -1328,6 +1688,7 @@
        window.makeKeyAndOrderFront(nil)
        window.orderFrontRegardless()
        settingsWindow = window
        refreshOverlaySpaceHotkeyRegistration()
        refreshLauncherChromeState(
            activate: !overlayAvoidsSpaceSwitch,
            avoidSpaceSwitch: overlayAvoidsSpaceSwitch
@@ -1432,6 +1793,7 @@
                activate: shouldRefocusOverlay && !self.overlayAvoidsSpaceSwitch,
                avoidSpaceSwitch: self.overlayAvoidsSpaceSwitch
            )
            self.refreshOverlaySpaceHotkeyRegistration()
            guard shouldRefocusOverlay else { return }
            DispatchQueue.main.asyncAfter(deadline: .now() + 0.12) { [weak self] in
                self?.showOrFocusOverlay(preferredScreen: preferredScreen)
@@ -1446,7 +1808,9 @@
            object: nil,
            queue: .main
        ) { [weak self] notification in
            self?.isInEditMode = (notification.userInfo?["active"] as? Bool) ?? false
            guard let self else { return }
            self.isInEditMode = (notification.userInfo?["active"] as? Bool) ?? false
            self.refreshOverlaySpaceHotkeyRegistration()
        }
    }
@@ -1463,6 +1827,7 @@
                self.promoteOverlayToForegroundInput()
            }
            self.updateOverlayLevelForTextInput()
            self.refreshOverlaySpaceHotkeyRegistration()
        }
    }
@@ -1529,6 +1894,7 @@
                    self.hideOverlay(force: true, discardWindow: wasQuickSearchOnlyOverlay)
                }
            }
            self.refreshOverlaySpaceHotkeyRegistration()
        }
        NotificationCenter.default.addObserver(
@@ -1536,7 +1902,9 @@
            object: nil,
            queue: .main
        ) { [weak self] notification in
            self?.isModalInteractionActive = (notification.userInfo?["active"] as? Bool) ?? false
            guard let self else { return }
            self.isModalInteractionActive = (notification.userInfo?["active"] as? Bool) ?? false
            self.refreshOverlaySpaceHotkeyRegistration()
        }
        NotificationCenter.default.addObserver(
@@ -1572,12 +1940,7 @@
        ) { [weak self] event in
            guard let self, self.isQuickSearchOpen else { return event }
            if event.window === self.overlayWindow {
                NotificationCenter.default.post(
                    name: .tagLauncherQuickSearchDismissRequested,
                    object: nil,
                    userInfo: ["source": QuickSearchDismissSource.mouse]
                )
                return nil
                return event
            }
            return event
        }
@@ -1608,9 +1971,13 @@
            forName: .tagLauncherOpenPreferencesRequested,
            object: nil,
            queue: .main
        ) { [weak self] _ in
            self?.openPreferences()
        ) { [weak self] notification in
            self?.openPreferences(targetTab: Self.preferencesTabTarget(from: notification))
        }
    }
    private static func preferencesTabTarget(from notification: Notification) -> String? {
        notification.userInfo?[SettingsTabTarget.userInfoKey] as? String
    }
    private func observeExternalActivationRequests() {
@@ -1632,6 +1999,15 @@
    }
    @objc private func openPreferences(_ sender: Any? = nil) {
        openPreferences(targetTab: nil)
    }
    @objc private func openProStatusFromStatusMenu(_ sender: NSMenuItem) {
        openPreferences(targetTab: SettingsTabTarget.pro)
    }
    private func openPreferences(targetTab: String?) {
        explicitPreferencesOpenRequestedAt = Date()
        dismissQuickSearchIfNeeded()
        TagDatabase.flushPendingCategorySchemeBackupBatch()
        if overlayAvoidsSpaceSwitch {
@@ -1647,21 +2023,49 @@
        if let settingsWindow {
            prepareSettingsWindow(settingsWindow)
            requestPreferencesTabSelection(targetTab)
            return
        }
        let settingsSize = NSSize(width: 1000, height: 480)
        let window = NSWindow(
            contentRect: NSRect(origin: .zero, size: settingsSize),
            contentRect: NSRect(origin: .zero, size: Self.settingsContentSize),
            styleMask: [.titled, .closable],
            backing: .buffered,
            defer: false
        )
        window.title = tr("menu.preferences").replacingOccurrences(of: "…", with: "")
        window.contentView = NSHostingView(rootView: PreferencesView())
        window.contentView = NSHostingView(rootView: PreferencesView(initialTabRawValue: targetTab))
        window.isReleasedWhenClosed = false
        settingsWindow = window
        prepareSettingsWindow(window)
    }
    private func closeRestoredPreferencesWindowsDuringLaunch() {
        for delay in [0.25, 0.8, 1.6, 2.6] {
            DispatchQueue.main.asyncAfter(deadline: .now() + delay) { [weak self] in
                self?.closeRestoredPreferencesWindowIfNeeded()
            }
        }
    }
    private func closeRestoredPreferencesWindowIfNeeded() {
        guard Date().timeIntervalSince(explicitPreferencesOpenRequestedAt) > 3.0 else { return }
        for window in NSApp.windows where isSettingsWindowCandidate(window) {
            detachSettingsWindow(window)
            window.close()
            if settingsWindow == window {
                settingsWindow = nil
            }
        }
    }
    private func requestPreferencesTabSelection(_ targetTab: String?) {
        guard let targetTab else { return }
        NotificationCenter.default.post(
            name: .tagLauncherPreferencesTabRequested,
            object: nil,
            userInfo: [SettingsTabTarget.userInfoKey: targetTab]
        )
    }
    private func dismissQuickSearchIfNeeded() {
@@ -1735,7 +2139,13 @@
            super.mouseDown(with: event)
            return
        }
        if routeUsageTipsMouseDownIfNeeded(event) {
            return
        }
        if hit == self {
            if shouldSwallowUsageTipsBackdropClick(at: location) {
                return
            }
            if quickSearchSuppressesBackdropDismiss {
                NotificationCenter.default.post(
                    name: .tagLauncherQuickSearchDismissRequested,
@@ -1770,6 +2180,32 @@
        super.mouseDown(with: event)
    }
    private func routeUsageTipsMouseDownIfNeeded(_ event: NSEvent) -> Bool {
        guard let appGridHost = findAppGridCollectionHost(in: self) else { return false }
        return appGridHost.handleUsageTipsMouseDown(event)
    }
    private func findAppGridCollectionHost(in view: NSView) -> AppGridCollectionHostView? {
        if let host = view as? AppGridCollectionHostView {
            return host
        }
        for subview in view.subviews {
            if let host = findAppGridCollectionHost(in: subview) {
                return host
            }
        }
        return nil
    }
    private func shouldSwallowUsageTipsBackdropClick(at location: NSPoint) -> Bool {
        guard !UserDefaults.standard.bool(forKey: "hideUsageTips") else { return false }
        let height = min(bounds.height, AppGridUsageTipsMetrics.reservedHeight)
        guard height > 0 else { return false }
        let y = isFlipped ? max(0, bounds.height - height) : 0
        let region = NSRect(x: 0, y: y, width: bounds.width, height: height)
        return region.contains(location)
    }
    private func observeBackdropDismissSuppressionChanges() {
        modalInteractionObserver = NotificationCenter.default.addObserver(
            forName: .tagLauncherModalInteractionChanged,