Ariver
2026-06-11 72219babd9bf0a4ce6fc26af91ee969a8705f053
C1.source/Sources/Aligner/QuickSwitchSessionController.swift
@@ -15,6 +15,7 @@
    private let snapshotLoader: any QuickSwitchSnapshotLoading
    private let screenshotProvider: any ScreenshotProviderProtocol
    private let windowActivationService: any WindowActivationServiceProtocol
    private let spaceActivationService: any SpaceActivationServiceProtocol
    private let windowCloseService: any WindowCloseServiceProtocol
    private let systemCriticalWindowDetector: any SystemCriticalWindowDetecting
    private let disableScreenshotRefresh: Bool
@@ -30,7 +31,13 @@
        debugOverlayWidth: debugOverlayWidth
    )
    private var currentSnapshot: QuickSwitchSnapshot?
    private var sourceViewModel: QuickSwitchViewModel?
    private var currentViewModel: QuickSwitchViewModel?
    private var lockedSpaceFilterID: UInt64?
    private var selectionBeforeSpaceFilter: QuickSwitchSelection?
    private var lastSpaceFilterAction: String?
    private var pendingLockedSpaceUnlockSpaceID: UInt64?
    private var pendingLockedSpaceUnlockTask: Task<Void, Never>?
    private var lastSnapshotError: String?
    private var snapshotTask: Task<Void, Never>?
    private var screenshotTask: Task<Void, Never>?
@@ -48,6 +55,11 @@
    private var lastActivationWindowID: UInt32?
    private var lastActivationResult: WindowActivationResult?
    private var lastActivationError: String?
    private var lastSpaceActivationSpaceID: UInt64?
    private var lastSpaceActivationDidRequestFocus: Bool?
    private var lastSpaceActivationDisplayIdentifier: String?
    private var lastSpaceActivationPreviousCurrentSpaceID: UInt64?
    private var lastSpaceActivationError: String?
    private var lastCloseTargetKind: String?
    private var lastCloseAppGroupIndex: Int?
    private var lastCloseAppName: String?
@@ -102,6 +114,7 @@
        windowActivationService: any WindowActivationServiceProtocol = CGWindowAXWindowService(
            spaceIDsByWindowIDProvider: { _ in [:] }
        ),
        spaceActivationService: any SpaceActivationServiceProtocol = PrivateSpaceActivationService(),
        windowCloseService: any WindowCloseServiceProtocol = CGWindowAXWindowService(
            spaceIDsByWindowIDProvider: { _ in [:] }
        ),
@@ -118,6 +131,7 @@
        self.snapshotLoader = snapshotLoader
        self.screenshotProvider = screenshotProvider
        self.windowActivationService = windowActivationService
        self.spaceActivationService = spaceActivationService
        self.windowCloseService = windowCloseService
        self.systemCriticalWindowDetector = systemCriticalWindowDetector
        self.disableScreenshotRefresh = disableScreenshotRefresh
@@ -151,6 +165,11 @@
        lastActivationWindowID = nil
        lastActivationResult = nil
        lastActivationError = nil
        lastSpaceActivationSpaceID = nil
        lastSpaceActivationDidRequestFocus = nil
        lastSpaceActivationDisplayIdentifier = nil
        lastSpaceActivationPreviousCurrentSpaceID = nil
        lastSpaceActivationError = nil
        lastCloseTargetKind = nil
        lastCloseAppGroupIndex = nil
        lastCloseAppName = nil
@@ -159,6 +178,10 @@
        lastCloseError = nil
        pendingCloseVerification = nil
        suppressedCloseTargets = []
        cancelPendingLockedSpaceUnlock()
        lockedSpaceFilterID = nil
        selectionBeforeSpaceFilter = nil
        lastSpaceFilterAction = nil
        lastDismissReason = nil
        lastSystemCriticalAction = nil
        lastSystemCriticalMonitorSource = nil
@@ -180,6 +203,13 @@
        view.onCommitSelection = { [weak self] selection, source in
            self?.commitSelection(selection, source: source)
        }
        view.onSpaceLaneClick = { [weak self] spaceID, clickCount in
            self?.handleSpaceLaneClick(spaceID, clickCount: clickCount)
        }
        view.onBackgroundClick = { [weak self] in
            self?.handleBackgroundClick() ?? false
        }
        view.setSuppressEventInput(!debugMouseSequence.isEmpty || !debugKeySequence.isEmpty)
        view.onRequestClose = { [weak self] request in
            self?.requestClose(request)
        }
@@ -218,6 +248,7 @@
        snapshotTask = nil
        screenshotTask?.cancel()
        screenshotTask = nil
        cancelPendingLockedSpaceUnlock()
        pendingCloseVerification = nil
        view.clearCloseFeedback()
        invalidateSystemCriticalMonitoring()
@@ -264,6 +295,9 @@
            "initialSelectionWindowID": currentViewModel?.initialSelection?.windowID ?? NSNull(),
            "appShelfNames": currentViewModel?.appShelf.map(\.app.name) ?? [],
            "spaceLabels": currentViewModel?.displays.flatMap { $0.spaces.map(\.label) } ?? [],
            "spaceFilterLockedSpaceID": lockedSpaceFilterID ?? NSNull(),
            "spaceFilterActive": lockedSpaceFilterID != nil,
            "lastSpaceFilterAction": lastSpaceFilterAction ?? NSNull(),
            "overlayOpenElapsedMilliseconds": overlayOpenElapsedMilliseconds ?? NSNull(),
            "snapshotStartElapsedMilliseconds": snapshotStartElapsedMilliseconds ?? NSNull(),
            "snapshotDurationMilliseconds": snapshotDurationMilliseconds ?? NSNull(),
@@ -275,6 +309,11 @@
            "lastActivationWindowID": lastActivationWindowID ?? NSNull(),
            "lastActivationResult": lastActivationResult.map(Self.activationResultString) ?? NSNull(),
            "lastActivationError": lastActivationError ?? NSNull(),
            "lastSpaceActivationSpaceID": lastSpaceActivationSpaceID ?? NSNull(),
            "lastSpaceActivationDidRequestFocus": lastSpaceActivationDidRequestFocus ?? NSNull(),
            "lastSpaceActivationDisplayIdentifier": lastSpaceActivationDisplayIdentifier ?? NSNull(),
            "lastSpaceActivationPreviousCurrentSpaceID": lastSpaceActivationPreviousCurrentSpaceID ?? NSNull(),
            "lastSpaceActivationError": lastSpaceActivationError ?? NSNull(),
            "lastCloseTargetKind": lastCloseTargetKind ?? NSNull(),
            "lastCloseAppGroupIndex": lastCloseAppGroupIndex ?? NSNull(),
            "lastCloseAppName": lastCloseAppName ?? NSNull(),
@@ -375,6 +414,7 @@
    private func clearSnapshotState() {
        currentSnapshot = nil
        sourceViewModel = nil
        currentViewModel = nil
        lastSnapshotError = nil
    }
@@ -446,7 +486,11 @@
        onSnapshotUpdated?()
    }
    private func commitSelection(_ selection: QuickSwitchSelection, source: QuickSwitchCommitSource) {
    private func commitSelection(
        _ selection: QuickSwitchSelection,
        source: QuickSwitchCommitSource,
        in viewModel: QuickSwitchViewModel? = nil
    ) {
        DevelopmentDiagnostics.log("quickSwitch.activation.commit.start", [
            "source": source.rawValue,
            "appGroupIndex": selection.appGroupIndex,
@@ -458,7 +502,7 @@
        lastActivationWindowID = selection.windowID
        lastActivationError = nil
        guard let window = window(for: selection) else {
        guard let window = window(for: selection, in: viewModel ?? currentViewModel) else {
            lastActivationResult = .windowNotFound
            DevelopmentDiagnostics.log("quickSwitch.activation.commit.windowNotFound", [
                "source": source.rawValue,
@@ -513,6 +557,224 @@
                "result": lastActivationResult.map(Self.activationResultString)
            ])
        }
        onSnapshotUpdated?()
    }
    private func handleSpaceLaneClick(_ spaceID: UInt64, clickCount: Int) {
        if clickCount >= 2 {
            if pendingLockedSpaceUnlockSpaceID == spaceID {
                cancelPendingLockedSpaceUnlock()
                activateLockedSpace(spaceID)
            } else {
                DevelopmentDiagnostics.log("quickSwitch.spaceFilter.doubleClick.ignored", [
                    "spaceID": spaceID,
                    "lockedSpaceID": lockedSpaceFilterID ?? NSNull(),
                    "pendingUnlockSpaceID": pendingLockedSpaceUnlockSpaceID ?? NSNull()
                ])
            }
            return
        }
        cancelPendingLockedSpaceUnlock()
        if lockedSpaceFilterID == spaceID {
            scheduleLockedSpaceUnlock(spaceID)
            return
        }
        applySpaceLaneSingleClick(spaceID)
    }
    private func applySpaceLaneSingleClick(_ spaceID: UInt64) {
        guard let sourceViewModel else {
            lastSpaceFilterAction = "blockedNoSourceViewModel"
            DevelopmentDiagnostics.log("quickSwitch.spaceFilter.click.blocked", [
                "spaceID": spaceID,
                "reason": "noSourceViewModel"
            ])
            onSnapshotUpdated?()
            return
        }
        if let fullscreenSelection = QuickSwitchSpaceFilterPolicy.singleFullscreenSelection(
            inSpaceID: spaceID,
            viewModel: sourceViewModel
        ) {
            lockedSpaceFilterID = nil
            selectionBeforeSpaceFilter = nil
            lastSpaceFilterAction = "activateSingleFullscreen"
            let visibleViewModel = projectedViewModel(from: sourceViewModel)
            currentViewModel = visibleViewModel
            view.applyProjected(viewModel: visibleViewModel)
            DevelopmentDiagnostics.log("quickSwitch.spaceFilter.singleFullscreenActivate", [
                "spaceID": spaceID,
                "windowID": fullscreenSelection.windowID
            ])
            commitSelection(fullscreenSelection, source: .mouse, in: sourceViewModel)
            return
        }
        let preferredSelection: QuickSwitchSelection?
        if lockedSpaceFilterID == spaceID {
            lockedSpaceFilterID = nil
            preferredSelection = selectionBeforeSpaceFilter
            selectionBeforeSpaceFilter = nil
            lastSpaceFilterAction = "unlock"
        } else {
            if lockedSpaceFilterID == nil {
                selectionBeforeSpaceFilter = view.effectiveSelectionForProjection()
                lastSpaceFilterAction = "lock"
            } else {
                lastSpaceFilterAction = "switch"
            }
            lockedSpaceFilterID = spaceID
            preferredSelection = nil
        }
        let visibleViewModel = projectedViewModel(
            from: sourceViewModel,
            preferredSelection: preferredSelection
        )
        currentViewModel = visibleViewModel
        view.applyProjected(viewModel: visibleViewModel)
        DevelopmentDiagnostics.log("quickSwitch.spaceFilter.apply", [
            "spaceID": spaceID,
            "lockedSpaceID": lockedSpaceFilterID,
            "action": lastSpaceFilterAction,
            "visibleAppCount": visibleViewModel.appShelf.count,
            "visibleWindowCount": visibleViewModel.windowCount
        ])
        if disableScreenshotRefresh {
            view.markScreenshotsNotRequested(for: visibleViewModel, reason: "disabledByLaunchOption")
            screenshotTask?.cancel()
            screenshotTask = nil
        } else {
            scheduleScreenshotRefresh(for: visibleViewModel, generation: sessionGeneration)
        }
        onSnapshotUpdated?()
    }
    @discardableResult
    private func handleBackgroundClick() -> Bool {
        guard lockedSpaceFilterID != nil else { return false }
        cancelPendingLockedSpaceUnlock()
        let unlocked = unlockSpaceFilter(action: "unlockBackground")
        DevelopmentDiagnostics.log("quickSwitch.spaceFilter.backgroundUnlock", [
            "unlocked": unlocked
        ])
        return unlocked
    }
    private func scheduleLockedSpaceUnlock(_ spaceID: UInt64) {
        pendingLockedSpaceUnlockSpaceID = spaceID
        let delay = max(0.12, NSEvent.doubleClickInterval + 0.03)
        DevelopmentDiagnostics.log("quickSwitch.spaceFilter.unlock.pending", [
            "spaceID": spaceID,
            "delay": delay
        ])
        pendingLockedSpaceUnlockTask = Task { @MainActor [weak self] in
            try? await Task.sleep(nanoseconds: UInt64(delay * 1_000_000_000))
            guard !Task.isCancelled,
                  let self,
                  self.pendingLockedSpaceUnlockSpaceID == spaceID
            else {
                return
            }
            self.pendingLockedSpaceUnlockSpaceID = nil
            self.pendingLockedSpaceUnlockTask = nil
            self.applySpaceLaneSingleClick(spaceID)
        }
    }
    private func cancelPendingLockedSpaceUnlock() {
        pendingLockedSpaceUnlockTask?.cancel()
        pendingLockedSpaceUnlockTask = nil
        pendingLockedSpaceUnlockSpaceID = nil
    }
    @discardableResult
    private func unlockSpaceFilter(action: String) -> Bool {
        guard let sourceViewModel, lockedSpaceFilterID != nil else {
            return false
        }
        lockedSpaceFilterID = nil
        let preferredSelection = selectionBeforeSpaceFilter
        selectionBeforeSpaceFilter = nil
        lastSpaceFilterAction = action
        let visibleViewModel = projectedViewModel(
            from: sourceViewModel,
            preferredSelection: preferredSelection
        )
        currentViewModel = visibleViewModel
        view.applyProjected(viewModel: visibleViewModel)
        if disableScreenshotRefresh {
            view.markScreenshotsNotRequested(for: visibleViewModel, reason: "disabledByLaunchOption")
            screenshotTask?.cancel()
            screenshotTask = nil
        } else {
            scheduleScreenshotRefresh(for: visibleViewModel, generation: sessionGeneration)
        }
        onSnapshotUpdated?()
        return true
    }
    private func activateLockedSpace(_ spaceID: UInt64) {
        guard lockedSpaceFilterID == spaceID else {
            DevelopmentDiagnostics.log("quickSwitch.spaceFilter.activateSpace.ignored", [
                "spaceID": spaceID,
                "lockedSpaceID": lockedSpaceFilterID ?? NSNull()
            ])
            return
        }
        guard let sourceViewModel else {
            lastSpaceFilterAction = "blockedNoSourceViewModel"
            DevelopmentDiagnostics.log("quickSwitch.spaceFilter.activateSpace.blocked", [
                "spaceID": spaceID,
                "reason": "noSourceViewModel"
            ])
            onSnapshotUpdated?()
            return
        }
        lockedSpaceFilterID = nil
        selectionBeforeSpaceFilter = nil
        lastSpaceFilterAction = "activateSpace"
        lastSpaceActivationSpaceID = spaceID
        lastSpaceActivationDidRequestFocus = nil
        lastSpaceActivationDisplayIdentifier = nil
        lastSpaceActivationPreviousCurrentSpaceID = nil
        lastSpaceActivationError = nil
        let visibleViewModel = projectedViewModel(from: sourceViewModel)
        currentViewModel = visibleViewModel
        view.applyProjected(viewModel: visibleViewModel)
        DevelopmentDiagnostics.log("quickSwitch.spaceFilter.activateSpace.start", [
            "spaceID": spaceID
        ])
        hide(reason: .userClosed)
        let outcome = spaceActivationService.activate(spaceID: spaceID)
        lastSpaceActivationSpaceID = outcome.targetSpaceID
        lastSpaceActivationDidRequestFocus = outcome.didRequestFocus
        lastSpaceActivationDisplayIdentifier = outcome.displayIdentifier
        lastSpaceActivationPreviousCurrentSpaceID = outcome.previousCurrentSpaceID
        lastSpaceActivationError = outcome.error
        DevelopmentDiagnostics.log("quickSwitch.spaceFilter.activateSpace.result", [
            "spaceID": outcome.targetSpaceID,
            "displayIdentifier": outcome.displayIdentifier ?? NSNull(),
            "previousCurrentSpaceID": outcome.previousCurrentSpaceID ?? NSNull(),
            "didRequestFocus": outcome.didRequestFocus,
            "error": outcome.error ?? NSNull()
        ])
        onSnapshotUpdated?()
    }
@@ -618,10 +880,10 @@
            suppressedCloseTargets.append(target)
        }
        guard let currentViewModel else { return }
        let visibleViewModel = viewModel(currentViewModel, excluding: suppressedCloseTargets)
        guard let sourceViewModel else { return }
        let visibleViewModel = projectedViewModel(from: sourceViewModel, preferredSelection: view.effectiveSelectionForProjection())
        self.currentViewModel = visibleViewModel
        view.apply(viewModel: visibleViewModel)
        view.applyProjected(viewModel: visibleViewModel)
        DevelopmentDiagnostics.log("quickSwitch.close.optimisticRemove", [
            "targetKind": request.kindDescription,
            "appGroupIndex": request.appGroupIndex,
@@ -683,7 +945,20 @@
            displays: viewModel.displays,
            appShelf: appShelf,
            waterfallColumns: columns,
            initialSelection: initialSelection(in: columns, fallback: viewModel.initialSelection)
            initialSelection: initialSelection(in: columns, fallback: viewModel.initialSelection),
            lockedSpaceID: viewModel.lockedSpaceID
        )
    }
    private func projectedViewModel(
        from sourceViewModel: QuickSwitchViewModel,
        preferredSelection: QuickSwitchSelection? = nil
    ) -> QuickSwitchViewModel {
        let closeProjectedViewModel = viewModel(sourceViewModel, excluding: suppressedCloseTargets)
        return QuickSwitchSpaceFilterPolicy.projectedViewModel(
            from: closeProjectedViewModel,
            lockedSpaceID: lockedSpaceFilterID,
            preferredSelection: preferredSelection
        )
    }
@@ -743,8 +1018,11 @@
        }
    }
    private func window(for selection: QuickSwitchSelection) -> AlignerWindow? {
        currentViewModel?
    private func window(
        for selection: QuickSwitchSelection,
        in viewModel: QuickSwitchViewModel?
    ) -> AlignerWindow? {
        viewModel?
            .waterfallColumns
            .flatMap(\.windows)
            .first { card in
@@ -772,7 +1050,11 @@
        switch result {
        case .success(let success):
            currentSnapshot = success.snapshot
            let visibleViewModel = viewModel(success.viewModel, excluding: suppressedCloseTargets)
            sourceViewModel = success.viewModel
            let visibleViewModel = projectedViewModel(
                from: success.viewModel,
                preferredSelection: view.effectiveSelectionForProjection()
            )
            currentViewModel = visibleViewModel
            lastSnapshotError = nil
            snapshotStartElapsedMilliseconds = success.timing.startElapsedMilliseconds
@@ -808,6 +1090,7 @@
            }
        case .failure(let failure):
            currentSnapshot = nil
            sourceViewModel = nil
            currentViewModel = nil
            lastSnapshotError = failure.message
            snapshotStartElapsedMilliseconds = failure.timing.startElapsedMilliseconds
@@ -1169,6 +1452,7 @@
    let fixtureMultiDisplay: Bool
    let fixtureCandidateFiltering: Bool
    let fixtureActivation: Bool
    let fixtureSpaceFilter: Bool
    let debugHoveredAppGroupIndex: Int?
    let debugOverlayWidth: CGFloat?
    let debugKeySequence: [String]
@@ -1195,6 +1479,7 @@
            fixtureMultiDisplay: arguments.contains("--round01-fixture-multi-display"),
            fixtureCandidateFiltering: arguments.contains("--round01-fixture-candidate-filtering"),
            fixtureActivation: arguments.contains("--round01-fixture-window-activation"),
            fixtureSpaceFilter: arguments.contains("--round01-fixture-space-filter"),
            debugHoveredAppGroupIndex: intValue(for: "--round01-debug-hover-app-index", in: arguments),
            debugOverlayWidth: cgFloatValue(for: "--round01-debug-overlay-width", in: arguments),
            debugKeySequence: stringListValue(for: "--round01-debug-key-sequence", in: arguments),