From 86f0b4bd20cdec86dfb5bdb7978d1f6ff4af111a Mon Sep 17 00:00:00 2001
From: Ariver <shanghai3168@gmail.com>
Date: Fri, 14 Aug 2026 22:41:54 +0800
Subject: [PATCH] [verified] feat: export performance diagnostics

---
 C1.source/Sources/Aligner/AlignerApplicationDelegate.swift |  180 ++++++++++++++++++++++++++++++++++++++++++++++++++++++++----
 1 files changed, 168 insertions(+), 12 deletions(-)

diff --git a/C1.source/Sources/Aligner/AlignerApplicationDelegate.swift b/C1.source/Sources/Aligner/AlignerApplicationDelegate.swift
index 3334d05..83d35ba 100644
--- a/C1.source/Sources/Aligner/AlignerApplicationDelegate.swift
+++ b/C1.source/Sources/Aligner/AlignerApplicationDelegate.swift
@@ -14,6 +14,7 @@
     private let round1SnapshotDumpOptions = Round1SnapshotDumpOptions.parse(arguments: ProcessInfo.processInfo.arguments)
     private let round1ScreenshotProbeOptions = Round1ScreenshotProbeOptions.parse(arguments: ProcessInfo.processInfo.arguments)
     private let round1QuickSwitchOptions = Round1QuickSwitchLaunchOptions.parse(arguments: ProcessInfo.processInfo.arguments)
+    private let round015EntitlementOptions = Round015EntitlementLaunchOptions.parse(arguments: ProcessInfo.processInfo.arguments)
     private var permissionWindowController: PermissionsWindowController?
     private var debugOverlayController: Round0DebugOverlayController?
     private var round1PerformancePoCController: Round1PerformancePoCController?
@@ -22,9 +23,11 @@
     private var quickSwitchTriggerService: CGEventTapTriggerService?
     private var settingsWindowController: SettingsWindowController?
     private var isRunningQuickSwitchLifecycle = false
+    private var lastQuickSwitchOpenRequestSource: String?
     private var statusItem: NSStatusItem?
     private var verticalWaterfallViewModeMenuItems: [NSMenuItem] = []
     private var horizontalWaterfallViewModeMenuItems: [NSMenuItem] = []
+    private let performanceDiagnosticsExporter = PerformanceDiagnosticsExporter()
 
     func applicationDidFinishLaunching(_ notification: Notification) {
         let bundlePathSummary = DevelopmentDiagnostics.pathSummary(Bundle.main.bundlePath)
@@ -87,7 +90,8 @@
             "hasVisibleWindows": flag,
             "activationPolicy": sender.activationPolicy().rawValue
         ])
-        return true
+        openQuickSwitchFromDockReopen(hasVisibleWindows: flag)
+        return false
     }
 
     private func configureStatusItem() {
@@ -101,6 +105,7 @@
         menu.addItem(menuItem(title: "Settings...", action: #selector(showSettingsFromMenu), keyEquivalent: ""))
         menu.addItem(.separator())
         menu.addItem(menuItem(title: "Check Permissions...", action: #selector(showPermissions), keyEquivalent: ""))
+        menu.addItem(menuItem(title: "Export Performance Diagnostics...", action: #selector(exportPerformanceDiagnostics), keyEquivalent: ""))
         menu.addItem(.separator())
         menu.addItem(menuItem(title: "Show Quick Switch", action: #selector(showQuickSwitchFromMenu), keyEquivalent: ""))
         menu.addItem(menuItem(title: "Hide Quick Switch", action: #selector(hideQuickSwitchFromMenu), keyEquivalent: ""))
@@ -136,6 +141,7 @@
         appMenu.addItem(menuItem(title: "Settings...", action: #selector(showSettingsFromMenu), keyEquivalent: ","))
         appMenu.addItem(.separator())
         appMenu.addItem(menuItem(title: "Check Permissions...", action: #selector(showPermissions), keyEquivalent: ""))
+        appMenu.addItem(menuItem(title: "Export Performance Diagnostics...", action: #selector(exportPerformanceDiagnostics), keyEquivalent: ""))
         appMenu.addItem(menuItem(title: "Show Quick Switch", action: #selector(showQuickSwitchFromMenu), keyEquivalent: ""))
         appMenu.addItem(menuItem(title: "Hide Quick Switch", action: #selector(hideQuickSwitchFromMenu), keyEquivalent: ""))
         appMenu.addItem(quickSwitchViewModeMenuItem())
@@ -441,10 +447,13 @@
     }
 
     private func showQuickSwitchIfRequested() {
-        guard round1QuickSwitchOptions.openQuickSwitch || round1QuickSwitchOptions.lifecycleCycles != nil else { return }
+        guard round1QuickSwitchOptions.openQuickSwitch
+                || round1QuickSwitchOptions.lifecycleCycles != nil
+                || round1QuickSwitchOptions.simulateDockReopenAfter != nil else { return }
         DevelopmentDiagnostics.log("app.quickSwitch.launchOptionRequested", [
             "openQuickSwitch": round1QuickSwitchOptions.openQuickSwitch,
-            "lifecycleCycles": round1QuickSwitchOptions.lifecycleCycles
+            "lifecycleCycles": round1QuickSwitchOptions.lifecycleCycles,
+            "simulateDockReopenAfter": round1QuickSwitchOptions.simulateDockReopenAfter
         ])
 
         if let lifecycleCycles = round1QuickSwitchOptions.lifecycleCycles {
@@ -452,9 +461,23 @@
             return
         }
 
-        openQuickSwitchFromTrigger()
-        writeQuickSwitchReportIfNeeded()
+        if let simulateDockReopenAfter = round1QuickSwitchOptions.simulateDockReopenAfter {
+            DispatchQueue.main.asyncAfter(deadline: .now() + simulateDockReopenAfter) { [weak self] in
+                guard let self else { return }
+                let hasVisibleWindows = NSApp.windows.contains { $0.isVisible }
+                self.openQuickSwitchFromDockReopen(hasVisibleWindows: hasVisibleWindows)
+                self.writeQuickSwitchReportIfNeeded()
+                self.scheduleQuickSwitchAutoHideAndQuitIfNeeded()
+            }
+            return
+        }
 
+        openQuickSwitchFromTrigger(source: "launchOption")
+        writeQuickSwitchReportIfNeeded()
+        scheduleQuickSwitchAutoHideAndQuitIfNeeded()
+    }
+
+    private func scheduleQuickSwitchAutoHideAndQuitIfNeeded() {
         if let autoHideAfter = round1QuickSwitchOptions.autoHideAfter {
             DispatchQueue.main.asyncAfter(deadline: .now() + autoHideAfter) { [weak self] in
                 self?.hideQuickSwitchSession(reason: .timeout)
@@ -481,6 +504,7 @@
                 windowCloseService: quickSwitchWindowCloseService(),
                 disableScreenshotRefresh: round1QuickSwitchOptions.disableScreenshotRefresh,
                 waterfallViewMode: quickSwitchWaterfallViewMode(),
+                theme: quickSwitchThemePreference(),
                 closeConfirmationRequired: quickSwitchConfirmBeforeClose,
                 onCloseConfirmationDisabled: { [weak self] in
                     self?.setQuickSwitchConfirmBeforeClose(false)
@@ -494,8 +518,11 @@
             controller.onSnapshotUpdated = { [weak self] in
                 self?.writeQuickSwitchReportIfNeeded()
             }
-            controller.onOpenSettings = { [weak self] in
-                self?.showSettingsFromQuickSwitch()
+            controller.onToggleWaterfallViewMode = { [weak self] in
+                self?.toggleQuickSwitchWaterfallViewModeFromQuickSwitch()
+            }
+            controller.onToggleTheme = { [weak self] in
+                self?.toggleQuickSwitchThemeFromQuickSwitch()
             }
             quickSwitchSessionController = controller
         }
@@ -529,9 +556,19 @@
         }
     }
 
-    private func openQuickSwitchFromTrigger() {
+    private func openQuickSwitchFromDockReopen(hasVisibleWindows: Bool) {
+        DevelopmentDiagnostics.log("app.quickSwitch.openFromDockReopen", [
+            "hasVisibleWindows": hasVisibleWindows,
+            "alreadyVisible": quickSwitchSessionController?.isVisible ?? false
+        ])
+        openQuickSwitchFromTrigger(source: "dockReopen")
+    }
+
+    private func openQuickSwitchFromTrigger(source: String = "trigger") {
+        lastQuickSwitchOpenRequestSource = source
         let accessibility = accessibilityStatus()
         DevelopmentDiagnostics.log("app.quickSwitch.openFromTrigger", [
+            "source": source,
             "accessibility": permissionStatusString(accessibility),
             "screenRecording": permissionStatusString(screenRecordingStatus()),
             "alreadyVisible": quickSwitchSessionController?.isVisible ?? false
@@ -560,6 +597,7 @@
                 windowCloseService: quickSwitchWindowCloseService(),
                 disableScreenshotRefresh: round1QuickSwitchOptions.disableScreenshotRefresh,
                 waterfallViewMode: quickSwitchWaterfallViewMode(),
+                theme: quickSwitchThemePreference(),
                 closeConfirmationRequired: quickSwitchConfirmBeforeClose,
                 onCloseConfirmationDisabled: { [weak self] in
                     self?.setQuickSwitchConfirmBeforeClose(false)
@@ -573,8 +611,11 @@
             controller.onSnapshotUpdated = { [weak self] in
                 self?.writeQuickSwitchReportIfNeeded()
             }
-            controller.onOpenSettings = { [weak self] in
-                self?.showSettingsFromQuickSwitch()
+            controller.onToggleWaterfallViewMode = { [weak self] in
+                self?.toggleQuickSwitchWaterfallViewModeFromQuickSwitch()
+            }
+            controller.onToggleTheme = { [weak self] in
+                self?.toggleQuickSwitchThemeFromQuickSwitch()
             }
             quickSwitchSessionController = controller
         }
@@ -607,6 +648,10 @@
         let versionBuild = appVersionBuild()
         report["preferenceWaterfallViewMode"] = preferenceStore.read().quickSwitchWaterfallViewMode.rawValue
         report["resolvedWaterfallViewMode"] = quickSwitchWaterfallViewMode().rawValue
+        report["preferenceTheme"] = preferenceStore.read().theme.rawValue
+        report["resolvedQuickSwitchTheme"] = quickSwitchThemePreference().rawValue
+        report["lastQuickSwitchOpenRequestSource"] = lastQuickSwitchOpenRequestSource
+        report["authorization"] = round015EntitlementOptions.authorizationReportDictionary()
         report["aboutVersion"] = versionBuild.version
         report["aboutBuild"] = versionBuild.build
         report["statusMenu"] = statusMenuReportDictionary()
@@ -666,6 +711,10 @@
             return FixtureQuickSwitchSnapshotLoader(appCount: 1, mode: .splitView)
         }
 
+        if round1QuickSwitchOptions.fixtureMultiPageIdentity {
+            return FixtureQuickSwitchSnapshotLoader(appCount: 1, mode: .multiPageIdentity)
+        }
+
         if let fixtureAppCount = round1QuickSwitchOptions.fixtureAppCount {
             return FixtureQuickSwitchSnapshotLoader(
                 appCount: fixtureAppCount,
@@ -688,12 +737,21 @@
             return DebugWindowActivationService()
         }
 
-        return CGWindowAXWindowService(spaceIDsByWindowIDProvider: { _ in [:] })
+        let context = quickSwitchWindowSpaceContext()
+        return CGWindowAXWindowService(
+            spaceIDsByWindowIDProvider: context.spaceIDsByWindowIDProvider,
+            fullscreenSpaceIDs: context.fullscreenSpaceIDs
+        )
     }
 
     private func quickSwitchWaterfallViewMode() -> QuickSwitchWaterfallViewMode {
         round1QuickSwitchOptions.waterfallViewMode
             ?? preferenceStore.read().quickSwitchWaterfallViewMode
+    }
+
+    private func quickSwitchThemePreference() -> ThemePreference {
+        let theme = preferenceStore.read().theme
+        return theme == .dark ? .dark : .light
     }
 
     private func quickSwitchSpaceActivationService() -> any SpaceActivationServiceProtocol {
@@ -709,7 +767,39 @@
             return DebugWindowCloseService()
         }
 
-        return CGWindowAXWindowService(spaceIDsByWindowIDProvider: { _ in [:] })
+        let context = quickSwitchWindowSpaceContext()
+        return CGWindowAXWindowService(
+            spaceIDsByWindowIDProvider: context.spaceIDsByWindowIDProvider,
+            fullscreenSpaceIDs: context.fullscreenSpaceIDs
+        )
+    }
+
+    private func quickSwitchWindowSpaceContext() -> (
+        spaceIDsByWindowIDProvider: ([UInt32]) -> [UInt32: [UInt64]],
+        fullscreenSpaceIDs: Set<UInt64>
+    ) {
+        do {
+            let spaceEnumerator = try SkyLightSpaceEnumerator()
+            let displays = try spaceEnumerator.displays()
+            let fullscreenSpaceIDs = Set(
+                displays
+                    .flatMap(\.spaces)
+                    .filter { $0.type == .fullscreen }
+                    .map(\.id)
+            )
+            return (
+                spaceIDsByWindowIDProvider: spaceEnumerator.spaceIDsByWindowID(windowIDs:),
+                fullscreenSpaceIDs: fullscreenSpaceIDs
+            )
+        } catch {
+            DevelopmentDiagnostics.log("app.quickSwitch.windowSpaceContextUnavailable", [
+                "error": DevelopmentDiagnostics.errorSummaryString(error)
+            ])
+            return (
+                spaceIDsByWindowIDProvider: { _ in [:] },
+                fullscreenSpaceIDs: []
+            )
+        }
     }
 
     private var quickSwitchConfirmBeforeClose: Bool {
@@ -745,6 +835,50 @@
         ])
     }
 
+    private func toggleQuickSwitchWaterfallViewModeFromQuickSwitch() {
+        let currentMode = quickSwitchWaterfallViewMode()
+        let nextMode: QuickSwitchWaterfallViewMode = currentMode == .horizontalMasonry
+            ? .verticalColumns
+            : .horizontalMasonry
+        DevelopmentDiagnostics.log("app.quickSwitch.waterfallViewModeToggle", [
+            "currentMode": currentMode.rawValue,
+            "nextMode": nextMode.rawValue,
+            "quickSwitchVisible": quickSwitchSessionController?.isVisible ?? false
+        ])
+        setQuickSwitchWaterfallViewMode(nextMode)
+    }
+
+    private func setQuickSwitchThemePreference(_ theme: ThemePreference) {
+        let normalizedTheme: ThemePreference = theme == .dark ? .dark : .light
+        let preferences = preferenceStore.read()
+        let updatedPreferences = AlignerPreferences(
+            showMinimizedWindows: preferences.showMinimizedWindows,
+            showFullscreenWindows: preferences.showFullscreenWindows,
+            quickSwitchWaterfallViewMode: preferences.quickSwitchWaterfallViewMode,
+            theme: normalizedTheme,
+            language: preferences.language,
+            spaceDisplayStrategy: preferences.spaceDisplayStrategy
+        )
+        preferenceStore.write(updatedPreferences)
+        quickSwitchSessionController?.setTheme(normalizedTheme)
+        writeQuickSwitchReportIfNeeded()
+        DevelopmentDiagnostics.log("app.quickSwitch.themePreferenceChanged", [
+            "theme": normalizedTheme.rawValue,
+            "quickSwitchVisible": quickSwitchSessionController?.isVisible ?? false
+        ])
+    }
+
+    private func toggleQuickSwitchThemeFromQuickSwitch() {
+        let currentTheme = quickSwitchThemePreference()
+        let nextTheme: ThemePreference = currentTheme == .dark ? .light : .dark
+        DevelopmentDiagnostics.log("app.quickSwitch.themeToggle", [
+            "currentTheme": currentTheme.rawValue,
+            "nextTheme": nextTheme.rawValue,
+            "quickSwitchVisible": quickSwitchSessionController?.isVisible ?? false
+        ])
+        setQuickSwitchThemePreference(nextTheme)
+    }
+
     private func appVersionBuild() -> (version: String, build: String) {
         let info = Bundle.main.infoDictionary ?? [:]
         let version = info["CFBundleShortVersionString"] as? String ?? "Unknown"
@@ -759,6 +893,7 @@
         return [
             "hasAboutItem": statusItem?.menu?.item(withTitle: "About Aligner...") != nil,
             "hasSettingsItem": statusItem?.menu?.item(withTitle: "Settings...") != nil,
+            "hasExportPerformanceDiagnosticsItem": statusItem?.menu?.item(withTitle: "Export Performance Diagnostics...") != nil,
             "hasQuickSwitchViewSubmenu": statusItem?.menu?.item(withTitle: "Quick Switch View")?.submenu != nil,
             "hasVerticalColumnsItem": verticalItem != nil,
             "hasHorizontalWaterfallItem": horizontalItem != nil,
@@ -776,6 +911,7 @@
             "hasAppMenu": appMenu != nil,
             "hasAboutItem": appMenu?.item(withTitle: "About Aligner...") != nil,
             "hasSettingsItem": appMenu?.item(withTitle: "Settings...") != nil,
+            "hasExportPerformanceDiagnosticsItem": appMenu?.item(withTitle: "Export Performance Diagnostics...") != nil,
             "settingsKeyEquivalent": appMenu?.item(withTitle: "Settings...")?.keyEquivalent ?? "",
             "hasQuickSwitchViewSubmenuInAppMenu": appMenu?.item(withTitle: "Quick Switch View")?.submenu != nil,
             "hasQuickSwitchViewSubmenuInViewMenu": viewMenu?.item(withTitle: "Quick Switch View")?.submenu != nil
@@ -817,6 +953,26 @@
         openSettingsWindow(source: "menu")
     }
 
+    @objc private func exportPerformanceDiagnostics() {
+        let versionBuild = appVersionBuild()
+        let preferences = preferenceStore.read()
+        let quickSwitch = quickSwitchSessionController?.performanceDiagnosticSummary() ?? [
+            "snapshotLoaded": false,
+            "quickSwitchVisible": false,
+            "applicationCount": 0,
+            "windowCount": 0,
+            "applications": []
+        ]
+        performanceDiagnosticsExporter.export(
+            version: versionBuild.version,
+            build: versionBuild.build,
+            preferences: preferences,
+            accessibility: permissionStatusString(accessibilityStatus()),
+            screenRecording: permissionStatusString(screenRecordingStatus()),
+            quickSwitch: quickSwitch
+        )
+    }
+
     private func showSettingsFromQuickSwitch() {
         openSettingsWindow(source: "quickSwitch")
     }

--
Gitblit v1.9.3