Ariver
2026-07-02 cf4a1ad4dbc8f4a3f61a0a1a32f63d31c3562b27
src/Apptag/ApptagApp.swift
@@ -28,7 +28,14 @@
// 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")
@@ -58,6 +65,7 @@
    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
@@ -72,6 +80,11 @@
    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(
@@ -220,6 +233,7 @@
        observeApplicationMenuChanges()
        observeChromeSettings()
        observeLanguageChanges()
        observeProEntitlementChanges()
        setupLaunchAtLogin()
        suppressReopenUntil = Date().addingTimeInterval(1.0)
        didFinishLaunching = true
@@ -889,7 +903,7 @@
        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() {
@@ -1267,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'
@@ -1291,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)
        }
    }
@@ -1306,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)
        }
@@ -1818,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
        }
@@ -1886,7 +2003,7 @@
    }
    @objc private func openProStatusFromStatusMenu(_ sender: NSMenuItem) {
        openPreferences(targetTab: SettingsTabTarget.about)
        openPreferences(targetTab: SettingsTabTarget.pro)
    }
    private func openPreferences(targetTab: String?) {
@@ -2052,12 +2169,15 @@
        // NSHostingView.hitTest may return a SwiftUI-internal wrapper — the
        // actual AppKit subview may be nested deeper.
        if let container = findTextFieldContainer(in: hit) {
            container.mouseDown(with: event)
            container.focusTextField()
            return
        }
        if let tf = findNSTextField(in: hit) {
            let shouldSelectAll = tf.currentEditor() == nil
            tf.window?.makeFirstResponder(tf)
            tf.mouseDown(with: event)
            if shouldSelectAll {
                tf.selectText(nil)
            }
            return
        }
        super.mouseDown(with: event)