From 9c5edf5be0b7416a468f6686543cc3d30a6a5821 Mon Sep 17 00:00:00 2001
From: Ariver <shanghai3168@gmail.com>
Date: Fri, 14 Aug 2026 20:23:23 +0800
Subject: [PATCH] docs: freeze Aligner 0.1.13 baseline
---
C1.source/Sources/Aligner/QuickSwitchSessionController.swift | 167 +++++++++++++++++++++++++++++++++++++++++++++++++++----
1 files changed, 154 insertions(+), 13 deletions(-)
diff --git a/C1.source/Sources/Aligner/QuickSwitchSessionController.swift b/C1.source/Sources/Aligner/QuickSwitchSessionController.swift
index 03e9501..8122f9a 100644
--- a/C1.source/Sources/Aligner/QuickSwitchSessionController.swift
+++ b/C1.source/Sources/Aligner/QuickSwitchSessionController.swift
@@ -19,6 +19,8 @@
private let windowCloseService: any WindowCloseServiceProtocol
private let systemCriticalWindowDetector: any SystemCriticalWindowDetecting
private let disableScreenshotRefresh: Bool
+ private var waterfallViewMode: QuickSwitchWaterfallViewMode
+ private var theme: ThemePreference
private var closeConfirmationRequired: Bool
private let onCloseConfirmationDisabled: (() -> Void)?
private let debugOverlayWidth: CGFloat?
@@ -55,6 +57,7 @@
private var lastActivationWindowID: UInt32?
private var lastActivationResult: WindowActivationResult?
private var lastActivationError: String?
+ private var lastActivationDurationMilliseconds: Double?
private var lastSpaceActivationSpaceID: UInt64?
private var lastSpaceActivationDidRequestFocus: Bool?
private var lastSpaceActivationDisplayIdentifier: String?
@@ -68,6 +71,7 @@
private var lastCloseError: String?
private var pendingCloseVerification: PendingCloseVerification?
private var suppressedCloseTargets: [SuppressedCloseTarget] = []
+ private var suppressedActivationWindowIDs = Set<UInt32>()
private var lastDismissReason: DismissReason?
private var lastSystemCriticalAction: String?
private var lastSystemCriticalMonitorSource: String?
@@ -85,6 +89,8 @@
private var lifecycleResidualOverlayWindows = 0
private var lifecycleResidualVisibleOverlayWindows = 0
var onSnapshotUpdated: (() -> Void)?
+ var onToggleWaterfallViewMode: (() -> Void)?
+ var onToggleTheme: (() -> Void)?
private struct PendingCloseVerification {
let request: QuickSwitchCloseRequest
@@ -120,6 +126,8 @@
),
systemCriticalWindowDetector: any SystemCriticalWindowDetecting = CGWindowSystemCriticalWindowDetector(),
disableScreenshotRefresh: Bool = false,
+ waterfallViewMode: QuickSwitchWaterfallViewMode = .verticalColumns,
+ theme: ThemePreference = .light,
closeConfirmationRequired: Bool = true,
onCloseConfirmationDisabled: (() -> Void)? = nil,
debugHoveredAppGroupIndex: Int? = nil,
@@ -135,6 +143,8 @@
self.windowCloseService = windowCloseService
self.systemCriticalWindowDetector = systemCriticalWindowDetector
self.disableScreenshotRefresh = disableScreenshotRefresh
+ self.waterfallViewMode = waterfallViewMode
+ self.theme = theme
self.closeConfirmationRequired = closeConfirmationRequired
self.onCloseConfirmationDisabled = onCloseConfirmationDisabled
self.debugOverlayWidth = debugOverlayWidth
@@ -165,6 +175,7 @@
lastActivationWindowID = nil
lastActivationResult = nil
lastActivationError = nil
+ lastActivationDurationMilliseconds = nil
lastSpaceActivationSpaceID = nil
lastSpaceActivationDidRequestFocus = nil
lastSpaceActivationDisplayIdentifier = nil
@@ -178,6 +189,7 @@
lastCloseError = nil
pendingCloseVerification = nil
suppressedCloseTargets = []
+ suppressedActivationWindowIDs = []
cancelPendingLockedSpaceUnlock()
lockedSpaceFilterID = nil
selectionBeforeSpaceFilter = nil
@@ -209,6 +221,12 @@
view.onBackgroundClick = { [weak self] in
self?.handleBackgroundClick() ?? false
}
+ view.onToggleWaterfallViewMode = { [weak self] in
+ self?.onToggleWaterfallViewMode?()
+ }
+ view.onToggleTheme = { [weak self] in
+ self?.onToggleTheme?()
+ }
view.setSuppressEventInput(!debugMouseSequence.isEmpty || !debugKeySequence.isEmpty)
view.onRequestClose = { [weak self] request in
self?.requestClose(request)
@@ -219,6 +237,8 @@
self.onCloseConfirmationDisabled?()
}
view.setCloseConfirmationRequired(closeConfirmationRequired)
+ view.setWaterfallViewMode(waterfallViewMode)
+ view.setTheme(theme)
view.apply(viewModel: nil)
view.beginPerformanceFirstFrameMeasurement()
coordinator.openQuickSwitch()
@@ -272,7 +292,58 @@
func retriggerFromShortcut() {
DevelopmentDiagnostics.log("quickSwitch.session.retriggerFromShortcut")
- show()
+ guard isVisible else {
+ show()
+ return
+ }
+
+ guard currentViewModel != nil else {
+ DevelopmentDiagnostics.log("quickSwitch.session.retriggerFromShortcut.noSnapshot", [
+ "action": "fallbackShow"
+ ])
+ show()
+ return
+ }
+
+ let didCycle = view.hoverNextAppGroupIndex()
+ DevelopmentDiagnostics.log("quickSwitch.session.retriggerFromShortcut.hoverApp", [
+ "didCycle": didCycle
+ ])
+
+ if !didCycle {
+ show()
+ }
+ }
+
+ func setWaterfallViewMode(_ mode: QuickSwitchWaterfallViewMode) {
+ guard waterfallViewMode != mode else { return }
+
+ waterfallViewMode = mode
+ DevelopmentDiagnostics.log("quickSwitch.session.waterfallViewModeChanged", [
+ "mode": mode.rawValue,
+ "visible": isVisible,
+ "hasViewModel": currentViewModel != nil
+ ])
+
+ view.setWaterfallViewMode(mode)
+ if isVisible, let currentViewModel {
+ view.applyProjected(viewModel: currentViewModel)
+ }
+ onSnapshotUpdated?()
+ }
+
+ func setTheme(_ theme: ThemePreference) {
+ guard self.theme != theme else { return }
+
+ self.theme = theme
+ DevelopmentDiagnostics.log("quickSwitch.session.themeChanged", [
+ "theme": theme.rawValue,
+ "visible": isVisible,
+ "hasViewModel": currentViewModel != nil
+ ])
+
+ view.setTheme(theme)
+ onSnapshotUpdated?()
}
func reportDictionary() -> [String: Any] {
@@ -309,6 +380,7 @@
"lastActivationWindowID": lastActivationWindowID ?? NSNull(),
"lastActivationResult": lastActivationResult.map(Self.activationResultString) ?? NSNull(),
"lastActivationError": lastActivationError ?? NSNull(),
+ "lastActivationDurationMilliseconds": lastActivationDurationMilliseconds ?? NSNull(),
"lastSpaceActivationSpaceID": lastSpaceActivationSpaceID ?? NSNull(),
"lastSpaceActivationDidRequestFocus": lastSpaceActivationDidRequestFocus ?? NSNull(),
"lastSpaceActivationDisplayIdentifier": lastSpaceActivationDisplayIdentifier ?? NSNull(),
@@ -321,6 +393,8 @@
"lastCloseResult": lastCloseResult.map(Self.closeResultString) ?? NSNull(),
"lastCloseError": lastCloseError ?? NSNull(),
"suppressedCloseTargetCount": suppressedCloseTargets.count,
+ "suppressedActivationWindowCount": suppressedActivationWindowIDs.count,
+ "suppressedActivationWindowIDs": Array(suppressedActivationWindowIDs).sorted(),
"lastDismissReason": lastDismissReason.map(Self.dismissReasonString) ?? NSNull(),
"overlayLevel": Int(coordinator.currentLevel),
"overlayMainMenuLevel": Int(CGWindowLevelForKey(.mainMenuWindow)),
@@ -501,9 +575,11 @@
lastCommitSource = source
lastActivationWindowID = selection.windowID
lastActivationError = nil
+ lastActivationDurationMilliseconds = nil
guard let window = window(for: selection, in: viewModel ?? currentViewModel) else {
lastActivationResult = .windowNotFound
+ suppressActivationWindowID(selection.windowID, reason: "selectionMissing")
DevelopmentDiagnostics.log("quickSwitch.activation.commit.windowNotFound", [
"source": source.rawValue,
"appGroupIndex": selection.appGroupIndex,
@@ -520,8 +596,10 @@
])
hide(reason: .userClosed)
+ let activationStartedAt = CACurrentMediaTime()
do {
lastActivationResult = try windowActivationService.activate(window: window)
+ lastActivationDurationMilliseconds = (CACurrentMediaTime() - activationStartedAt) * 1_000
DevelopmentDiagnostics.log("quickSwitch.activation.commit.result", [
"source": source.rawValue,
"windowID": window.id,
@@ -530,9 +608,11 @@
"titleHash": DevelopmentDiagnostics.stableFingerprint(window.title),
"titleLength": window.title.count,
"titleIsEmpty": window.title.isEmpty,
- "result": lastActivationResult.map(Self.activationResultString)
+ "result": lastActivationResult.map(Self.activationResultString),
+ "durationMilliseconds": lastActivationDurationMilliseconds
])
} catch {
+ lastActivationDurationMilliseconds = (CACurrentMediaTime() - activationStartedAt) * 1_000
let errorSummary = DevelopmentDiagnostics.errorSummaryString(error)
lastActivationResult = .failed(errorSummary)
lastActivationError = errorSummary
@@ -542,10 +622,15 @@
"appName": window.app.name,
"titleHash": DevelopmentDiagnostics.stableFingerprint(window.title),
"titleLength": window.title.count,
- "titleIsEmpty": window.title.isEmpty
+ "titleIsEmpty": window.title.isEmpty,
+ "durationMilliseconds": lastActivationDurationMilliseconds
]
DevelopmentDiagnostics.errorSummaryFields(error).forEach { fields[$0.key] = $0.value }
DevelopmentDiagnostics.log("quickSwitch.activation.commit.error", fields)
+ }
+
+ if shouldSuppressAfterActivationResult(lastActivationResult) {
+ suppressActivationWindowID(window.id, reason: lastActivationResult.map(Self.activationResultString) ?? "unknown")
}
if shouldCloseAfterActivation(lastActivationResult) {
@@ -562,7 +647,7 @@
private func handleSpaceLaneClick(_ spaceID: UInt64, clickCount: Int) {
if clickCount >= 2 {
- if pendingLockedSpaceUnlockSpaceID == spaceID {
+ if pendingLockedSpaceUnlockSpaceID == spaceID || lockedSpaceFilterID == spaceID {
cancelPendingLockedSpaceUnlock()
activateLockedSpace(spaceID)
} else {
@@ -819,12 +904,12 @@
"result": Self.closeResultString(result)
])
if result == .requested {
- suppressCloseTarget(request)
DevelopmentDiagnostics.log("quickSwitch.close.request.refreshAfterClose", [
"targetKind": request.kindDescription,
"appGroupIndex": request.appGroupIndex,
"appName": request.appName,
- "windowID": request.windowID
+ "windowID": request.windowID,
+ "optimisticRemove": false
])
pendingCloseVerification = PendingCloseVerification(
request: request,
@@ -894,18 +979,37 @@
])
}
+ private func suppressActivationWindowID(_ windowID: UInt32, reason: String) {
+ guard windowID != 0 else { return }
+ suppressedActivationWindowIDs.insert(windowID)
+
+ guard let sourceViewModel else { return }
+ let visibleViewModel = projectedViewModel(from: sourceViewModel, preferredSelection: view.effectiveSelectionForProjection())
+ self.currentViewModel = visibleViewModel
+ view.applyProjected(viewModel: visibleViewModel)
+ DevelopmentDiagnostics.log("quickSwitch.activation.suppressWindow", [
+ "windowID": windowID,
+ "reason": reason,
+ "remainingWindowCount": visibleViewModel.windowCount,
+ "suppressedActivationWindowCount": suppressedActivationWindowIDs.count
+ ])
+ }
+
private func viewModel(
_ viewModel: QuickSwitchViewModel,
- excluding suppressedTargets: [SuppressedCloseTarget]
+ excluding suppressedTargets: [SuppressedCloseTarget],
+ suppressedActivationWindowIDs: Set<UInt32> = []
) -> QuickSwitchViewModel {
- guard !suppressedTargets.isEmpty else { return viewModel }
+ guard !suppressedTargets.isEmpty || !suppressedActivationWindowIDs.isEmpty else { return viewModel }
var nextGlobalIndex = 0
let columns = viewModel.waterfallColumns.compactMap { column -> QuickSwitchWaterfallColumnViewModel? in
let windows = column.windows.filter { card in
- !suppressedTargets.contains { target in
+ let isActivationSuppressed = suppressedActivationWindowIDs.contains(card.window.id)
+ let isCloseSuppressed = suppressedTargets.contains { target in
suppressedTarget(target, matches: card)
}
+ return !isActivationSuppressed && !isCloseSuppressed
}
guard !windows.isEmpty else { return nil }
@@ -954,7 +1058,11 @@
from sourceViewModel: QuickSwitchViewModel,
preferredSelection: QuickSwitchSelection? = nil
) -> QuickSwitchViewModel {
- let closeProjectedViewModel = viewModel(sourceViewModel, excluding: suppressedCloseTargets)
+ let closeProjectedViewModel = viewModel(
+ sourceViewModel,
+ excluding: suppressedCloseTargets,
+ suppressedActivationWindowIDs: suppressedActivationWindowIDs
+ )
return QuickSwitchSpaceFilterPolicy.projectedViewModel(
from: closeProjectedViewModel,
lockedSpaceID: lockedSpaceFilterID,
@@ -1011,9 +1119,18 @@
private func shouldCloseAfterActivation(_ result: WindowActivationResult?) -> Bool {
switch result {
- case .activated, .restored, .appActivatedOnly:
+ case .activated, .restored:
return true
- case .windowNotFound, .unsupported, .activationFailed, .failed, nil:
+ case .appActivatedOnly, .windowNotFound, .unsupported, .activationFailed, .failed, nil:
+ return false
+ }
+ }
+
+ private func shouldSuppressAfterActivationResult(_ result: WindowActivationResult?) -> Bool {
+ switch result {
+ case .windowNotFound, .unsupported, .activationFailed, .failed:
+ return true
+ case .activated, .restored, .appActivatedOnly, nil:
return false
}
}
@@ -1453,6 +1570,8 @@
let fixtureCandidateFiltering: Bool
let fixtureActivation: Bool
let fixtureSpaceFilter: Bool
+ let fixtureSplitView: Bool
+ let fixtureMultiPageIdentity: Bool
let debugHoveredAppGroupIndex: Int?
let debugOverlayWidth: CGFloat?
let debugKeySequence: [String]
@@ -1461,9 +1580,11 @@
let debugWindowActivation: Bool
let debugWindowClose: Bool
let disableScreenshotRefresh: Bool
+ let waterfallViewMode: QuickSwitchWaterfallViewMode?
let lifecycleCycles: Int?
let lifecycleInterval: TimeInterval
let lifecycleVisibleDuration: TimeInterval
+ let simulateDockReopenAfter: TimeInterval?
static func parse(arguments: [String]) -> Round1QuickSwitchLaunchOptions {
Round1QuickSwitchLaunchOptions(
@@ -1480,6 +1601,8 @@
fixtureCandidateFiltering: arguments.contains("--round01-fixture-candidate-filtering"),
fixtureActivation: arguments.contains("--round01-fixture-window-activation"),
fixtureSpaceFilter: arguments.contains("--round01-fixture-space-filter"),
+ fixtureSplitView: arguments.contains("--round01-fixture-split-view"),
+ fixtureMultiPageIdentity: arguments.contains("--round01-fixture-multi-page-identity"),
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),
@@ -1488,9 +1611,11 @@
debugWindowActivation: arguments.contains("--round01-debug-window-activation"),
debugWindowClose: arguments.contains("--round01-debug-window-close"),
disableScreenshotRefresh: arguments.contains("--round01-disable-screenshot-refresh"),
+ waterfallViewMode: waterfallViewModeValue(for: "--round01-waterfall-view-mode", in: arguments),
lifecycleCycles: intValue(for: "--round01-quick-switch-lifecycle-cycles", in: arguments),
lifecycleInterval: timeInterval(for: "--round01-quick-switch-lifecycle-interval", in: arguments) ?? 0.02,
- lifecycleVisibleDuration: timeInterval(for: "--round01-quick-switch-lifecycle-visible-duration", in: arguments) ?? 0.16
+ lifecycleVisibleDuration: timeInterval(for: "--round01-quick-switch-lifecycle-visible-duration", in: arguments) ?? 0.16,
+ simulateDockReopenAfter: timeInterval(for: "--round01-simulate-dock-reopen-after", in: arguments)
)
}
@@ -1512,6 +1637,22 @@
return String(argument.dropFirst(prefix.count))
}
+ private static func waterfallViewModeValue(
+ for key: String,
+ in arguments: [String]
+ ) -> QuickSwitchWaterfallViewMode? {
+ guard let value = stringValue(for: key, in: arguments) else { return nil }
+
+ switch value {
+ case "vertical", "vertical-columns", "verticalColumns":
+ return .verticalColumns
+ case "horizontal", "horizontal-masonry", "horizontalMasonry":
+ return .horizontalMasonry
+ default:
+ return nil
+ }
+ }
+
private static func intValue(for key: String, in arguments: [String]) -> Int? {
stringValue(for: key, in: arguments).flatMap(Int.init)
}
--
Gitblit v1.9.3