From 1d03f5b6b433b062a098bf7899a349d550aafac7 Mon Sep 17 00:00:00 2001
From: Ariver <shanghai3168@gmail.com>
Date: Mon, 29 Jun 2026 00:08:52 +0800
Subject: [PATCH] docs: update results map QA evidence

---
 C1.source/Sources/Aligner/AlignerApplicationDelegate.swift |  461 +++++++++++++++++++++++++++++++++++++++++++++++++++++++--
 1 files changed, 444 insertions(+), 17 deletions(-)

diff --git a/C1.source/Sources/Aligner/AlignerApplicationDelegate.swift b/C1.source/Sources/Aligner/AlignerApplicationDelegate.swift
index 3b968f2..9904f96 100644
--- a/C1.source/Sources/Aligner/AlignerApplicationDelegate.swift
+++ b/C1.source/Sources/Aligner/AlignerApplicationDelegate.swift
@@ -3,24 +3,30 @@
 import AlignerCore
 
 @MainActor
-final class AlignerApplicationDelegate: NSObject, NSApplicationDelegate {
+final class AlignerApplicationDelegate: NSObject, NSApplicationDelegate, NSMenuDelegate {
     private static let quickSwitchConfirmBeforeCloseDefaultsKey = "QuickSwitchConfirmBeforeClose"
 
     private let permissionChecker = SystemPermissionChecker()
+    private let preferenceStore = UserDefaultsPreferenceStore()
     private let launchOptions = Round0DebugLaunchOptions.parse(arguments: ProcessInfo.processInfo.arguments)
     private let round1PoCOptions = Round1PerformancePoCOptions.parse(arguments: ProcessInfo.processInfo.arguments)
     private let round1TriggerProbeOptions = Round1TriggerProbeOptions.parse(arguments: ProcessInfo.processInfo.arguments)
     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?
     private var round1TriggerProbe: CGEventTapTriggerProbe?
     private var quickSwitchSessionController: QuickSwitchSessionController?
     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] = []
 
     func applicationDidFinishLaunching(_ notification: Notification) {
         let bundlePathSummary = DevelopmentDiagnostics.pathSummary(Bundle.main.bundlePath)
@@ -44,6 +50,7 @@
             return
         }
 
+        configureMainMenu()
         configureStatusItem()
         if !launchOptions.skipPermissions {
             showPermissionsIfNeeded()
@@ -82,7 +89,8 @@
             "hasVisibleWindows": flag,
             "activationPolicy": sender.activationPolicy().rawValue
         ])
-        return true
+        openQuickSwitchFromDockReopen(hasVisibleWindows: flag)
+        return false
     }
 
     private func configureStatusItem() {
@@ -91,21 +99,139 @@
         item.button?.title = "A"
 
         let menu = NSMenu()
-        menu.addItem(NSMenuItem(title: "Check Permissions...", action: #selector(showPermissions), keyEquivalent: ","))
+        menu.delegate = self
+        menu.addItem(menuItem(title: "About Aligner...", action: #selector(showAbout), keyEquivalent: ""))
+        menu.addItem(menuItem(title: "Settings...", action: #selector(showSettingsFromMenu), keyEquivalent: ""))
         menu.addItem(.separator())
-        menu.addItem(NSMenuItem(title: "Show Quick Switch", action: #selector(showQuickSwitchFromMenu), keyEquivalent: ""))
-        menu.addItem(NSMenuItem(title: "Hide Quick Switch", action: #selector(hideQuickSwitchFromMenu), keyEquivalent: ""))
+        menu.addItem(menuItem(title: "Check Permissions...", action: #selector(showPermissions), keyEquivalent: ""))
         menu.addItem(.separator())
-        menu.addItem(NSMenuItem(title: "Show Round0 Debug Overlay", action: #selector(showRound0DebugOverlay), keyEquivalent: "d"))
-        menu.addItem(NSMenuItem(title: "Hide Round0 Debug Overlay", action: #selector(hideRound0DebugOverlay), keyEquivalent: ""))
+        menu.addItem(menuItem(title: "Show Quick Switch", action: #selector(showQuickSwitchFromMenu), keyEquivalent: ""))
+        menu.addItem(menuItem(title: "Hide Quick Switch", action: #selector(hideQuickSwitchFromMenu), keyEquivalent: ""))
+        menu.addItem(quickSwitchViewModeMenuItem())
         menu.addItem(.separator())
-        menu.addItem(NSMenuItem(title: "Quit Aligner", action: #selector(quit), keyEquivalent: "q"))
+        menu.addItem(menuItem(title: "Show Round0 Debug Overlay", action: #selector(showRound0DebugOverlay), keyEquivalent: "d"))
+        menu.addItem(menuItem(title: "Hide Round0 Debug Overlay", action: #selector(hideRound0DebugOverlay), keyEquivalent: ""))
+        menu.addItem(.separator())
+        menu.addItem(menuItem(title: "Quit Aligner", action: #selector(quit), keyEquivalent: "q"))
         item.menu = menu
 
         statusItem = item
+        updateWaterfallViewModeMenuState()
         DevelopmentDiagnostics.log("app.statusItem.configure.end", [
-            "hasButton": item.button != nil
+            "hasButton": item.button != nil,
+            "hasAboutItem": true,
+            "hasSettingsItem": true,
+            "hasWaterfallViewModeItems": !verticalWaterfallViewModeMenuItems.isEmpty
+                && !horizontalWaterfallViewModeMenuItems.isEmpty
         ])
+    }
+
+    private func configureMainMenu() {
+        DevelopmentDiagnostics.log("app.mainMenu.configure.start")
+
+        let mainMenu = NSMenu(title: "Main Menu")
+        let appMenuItem = NSMenuItem(title: "Aligner", action: nil, keyEquivalent: "")
+        let appMenu = NSMenu(title: "Aligner")
+        appMenu.delegate = self
+        appMenuItem.submenu = appMenu
+        appMenu.addItem(menuItem(title: "About Aligner...", action: #selector(showAbout), keyEquivalent: ""))
+        appMenu.addItem(.separator())
+        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: "Show Quick Switch", action: #selector(showQuickSwitchFromMenu), keyEquivalent: ""))
+        appMenu.addItem(menuItem(title: "Hide Quick Switch", action: #selector(hideQuickSwitchFromMenu), keyEquivalent: ""))
+        appMenu.addItem(quickSwitchViewModeMenuItem())
+        appMenu.addItem(.separator())
+        appMenu.addItem(menuItem(title: "Quit Aligner", action: #selector(quit), keyEquivalent: "q"))
+        mainMenu.addItem(appMenuItem)
+
+        let editMenuItem = NSMenuItem(title: "Edit", action: nil, keyEquivalent: "")
+        let editMenu = NSMenu(title: "Edit")
+        editMenuItem.submenu = editMenu
+        editMenu.addItem(responderMenuItem(title: "Undo", action: Selector(("undo:")), keyEquivalent: "z"))
+        editMenu.addItem(responderMenuItem(title: "Redo", action: Selector(("redo:")), keyEquivalent: "Z"))
+        editMenu.addItem(.separator())
+        editMenu.addItem(responderMenuItem(title: "Cut", action: #selector(NSText.cut(_:)), keyEquivalent: "x"))
+        editMenu.addItem(responderMenuItem(title: "Copy", action: #selector(NSText.copy(_:)), keyEquivalent: "c"))
+        editMenu.addItem(responderMenuItem(title: "Paste", action: #selector(NSText.paste(_:)), keyEquivalent: "v"))
+        editMenu.addItem(responderMenuItem(title: "Select All", action: #selector(NSText.selectAll(_:)), keyEquivalent: "a"))
+        mainMenu.addItem(editMenuItem)
+
+        let viewMenuItem = NSMenuItem(title: "View", action: nil, keyEquivalent: "")
+        let viewMenu = NSMenu(title: "View")
+        viewMenu.delegate = self
+        viewMenuItem.submenu = viewMenu
+        viewMenu.addItem(quickSwitchViewModeMenuItem())
+        mainMenu.addItem(viewMenuItem)
+
+        let windowMenuItem = NSMenuItem(title: "Window", action: nil, keyEquivalent: "")
+        let windowMenu = NSMenu(title: "Window")
+        windowMenuItem.submenu = windowMenu
+        windowMenu.addItem(responderMenuItem(title: "Minimize", action: #selector(NSWindow.performMiniaturize(_:)), keyEquivalent: "m"))
+        windowMenu.addItem(responderMenuItem(title: "Zoom", action: #selector(NSWindow.performZoom(_:)), keyEquivalent: ""))
+        windowMenu.addItem(.separator())
+        windowMenu.addItem(responderMenuItem(title: "Bring All to Front", action: #selector(NSApplication.arrangeInFront(_:)), keyEquivalent: ""))
+        mainMenu.addItem(windowMenuItem)
+        NSApp.windowsMenu = windowMenu
+
+        let helpMenuItem = NSMenuItem(title: "Help", action: nil, keyEquivalent: "")
+        helpMenuItem.submenu = NSMenu(title: "Help")
+        mainMenu.addItem(helpMenuItem)
+
+        NSApp.mainMenu = mainMenu
+        updateWaterfallViewModeMenuState()
+        DevelopmentDiagnostics.log("app.mainMenu.configure.end", [
+            "hasSettingsItem": appMenu.item(withTitle: "Settings...") != nil,
+            "hasQuickSwitchViewSubmenu": appMenu.item(withTitle: "Quick Switch View")?.submenu != nil
+        ])
+    }
+
+    func menuWillOpen(_ menu: NSMenu) {
+        updateWaterfallViewModeMenuState()
+    }
+
+    private func menuItem(title: String, action: Selector?, keyEquivalent: String) -> NSMenuItem {
+        let item = NSMenuItem(title: title, action: action, keyEquivalent: keyEquivalent)
+        item.target = self
+        return item
+    }
+
+    private func responderMenuItem(title: String, action: Selector?, keyEquivalent: String) -> NSMenuItem {
+        NSMenuItem(title: title, action: action, keyEquivalent: keyEquivalent)
+    }
+
+    private func quickSwitchViewModeMenuItem() -> NSMenuItem {
+        let submenu = NSMenu(title: "Quick Switch View")
+        let verticalItem = menuItem(
+            title: "Vertical Columns",
+            action: #selector(useVerticalWaterfallViewMode),
+            keyEquivalent: ""
+        )
+        let horizontalItem = menuItem(
+            title: "Horizontal Waterfall",
+            action: #selector(useHorizontalWaterfallViewMode),
+            keyEquivalent: ""
+        )
+        submenu.addItem(verticalItem)
+        submenu.addItem(horizontalItem)
+
+        verticalWaterfallViewModeMenuItems.append(verticalItem)
+        horizontalWaterfallViewModeMenuItems.append(horizontalItem)
+
+        let rootItem = NSMenuItem(title: "Quick Switch View", action: nil, keyEquivalent: "")
+        rootItem.submenu = submenu
+        return rootItem
+    }
+
+    private func updateWaterfallViewModeMenuState() {
+        let mode = quickSwitchWaterfallViewMode()
+        for item in verticalWaterfallViewModeMenuItems {
+            item.state = mode == .verticalColumns ? .on : .off
+        }
+        for item in horizontalWaterfallViewModeMenuItems {
+            item.state = mode == .horizontalMasonry ? .on : .off
+        }
     }
 
     private func showPermissionsIfNeeded() {
@@ -318,10 +444,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 {
@@ -329,9 +458,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)
@@ -354,8 +497,11 @@
             let controller = QuickSwitchSessionController(
                 snapshotLoader: quickSwitchSnapshotLoader(),
                 windowActivationService: quickSwitchWindowActivationService(),
+                spaceActivationService: quickSwitchSpaceActivationService(),
                 windowCloseService: quickSwitchWindowCloseService(),
                 disableScreenshotRefresh: round1QuickSwitchOptions.disableScreenshotRefresh,
+                waterfallViewMode: quickSwitchWaterfallViewMode(),
+                theme: quickSwitchThemePreference(),
                 closeConfirmationRequired: quickSwitchConfirmBeforeClose,
                 onCloseConfirmationDisabled: { [weak self] in
                     self?.setQuickSwitchConfirmBeforeClose(false)
@@ -368,6 +514,12 @@
             )
             controller.onSnapshotUpdated = { [weak self] in
                 self?.writeQuickSwitchReportIfNeeded()
+            }
+            controller.onToggleWaterfallViewMode = { [weak self] in
+                self?.toggleQuickSwitchWaterfallViewModeFromQuickSwitch()
+            }
+            controller.onToggleTheme = { [weak self] in
+                self?.toggleQuickSwitchThemeFromQuickSwitch()
             }
             quickSwitchSessionController = controller
         }
@@ -401,9 +553,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
@@ -428,8 +590,11 @@
             let controller = QuickSwitchSessionController(
                 snapshotLoader: quickSwitchSnapshotLoader(),
                 windowActivationService: quickSwitchWindowActivationService(),
+                spaceActivationService: quickSwitchSpaceActivationService(),
                 windowCloseService: quickSwitchWindowCloseService(),
                 disableScreenshotRefresh: round1QuickSwitchOptions.disableScreenshotRefresh,
+                waterfallViewMode: quickSwitchWaterfallViewMode(),
+                theme: quickSwitchThemePreference(),
                 closeConfirmationRequired: quickSwitchConfirmBeforeClose,
                 onCloseConfirmationDisabled: { [weak self] in
                     self?.setQuickSwitchConfirmBeforeClose(false)
@@ -442,6 +607,12 @@
             )
             controller.onSnapshotUpdated = { [weak self] in
                 self?.writeQuickSwitchReportIfNeeded()
+            }
+            controller.onToggleWaterfallViewMode = { [weak self] in
+                self?.toggleQuickSwitchWaterfallViewModeFromQuickSwitch()
+            }
+            controller.onToggleTheme = { [weak self] in
+                self?.toggleQuickSwitchThemeFromQuickSwitch()
             }
             quickSwitchSessionController = controller
         }
@@ -464,13 +635,25 @@
         guard !isRunningQuickSwitchLifecycle else { return }
         let reportPathSummary = DevelopmentDiagnostics.pathSummary(reportPath)
 
-        let report = quickSwitchSessionController?.reportDictionary() ?? [
+        var report: [String: Any] = quickSwitchSessionController?.reportDictionary() ?? [
             "snapshotLoaded": false,
             "quickSwitchVisible": false,
             "permissionStatus": "\(accessibilityStatus())",
             "diagnosticLogLocationKind": DevelopmentDiagnostics.pathSummary(DevelopmentDiagnostics.logPath)["locationKind"] ?? "unknown",
             "diagnosticLogBasename": DevelopmentDiagnostics.pathSummary(DevelopmentDiagnostics.logPath)["basename"] ?? "aligner-dev.log"
         ]
+        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()
+        report["mainMenu"] = mainMenuReportDictionary()
+        report["settingsWindow"] = settingsWindowReportDictionary()
 
         do {
             let data = try JSONSerialization.data(withJSONObject: report, options: [.prettyPrinted, .sortedKeys])
@@ -517,6 +700,18 @@
             return FixtureQuickSwitchSnapshotLoader(appCount: 1, mode: .candidateFiltering)
         }
 
+        if round1QuickSwitchOptions.fixtureSpaceFilter {
+            return FixtureQuickSwitchSnapshotLoader(appCount: 1, mode: .spaceFilter)
+        }
+
+        if round1QuickSwitchOptions.fixtureSplitView {
+            return FixtureQuickSwitchSnapshotLoader(appCount: 1, mode: .splitView)
+        }
+
+        if round1QuickSwitchOptions.fixtureMultiPageIdentity {
+            return FixtureQuickSwitchSnapshotLoader(appCount: 1, mode: .multiPageIdentity)
+        }
+
         if let fixtureAppCount = round1QuickSwitchOptions.fixtureAppCount {
             return FixtureQuickSwitchSnapshotLoader(
                 appCount: fixtureAppCount,
@@ -539,7 +734,29 @@
             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 {
+        if round1QuickSwitchOptions.debugWindowActivation {
+            return DebugSpaceActivationService()
+        }
+
+        return PrivateSpaceActivationService()
     }
 
     private func quickSwitchWindowCloseService() -> any WindowCloseServiceProtocol {
@@ -547,7 +764,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 {
@@ -562,6 +811,184 @@
         ])
     }
 
+    private func setQuickSwitchWaterfallViewMode(_ mode: QuickSwitchWaterfallViewMode) {
+        let preferences = preferenceStore.read()
+        let updatedPreferences = AlignerPreferences(
+            showMinimizedWindows: preferences.showMinimizedWindows,
+            showFullscreenWindows: preferences.showFullscreenWindows,
+            quickSwitchWaterfallViewMode: mode,
+            theme: preferences.theme,
+            language: preferences.language,
+            spaceDisplayStrategy: preferences.spaceDisplayStrategy
+        )
+        preferenceStore.write(updatedPreferences)
+        quickSwitchSessionController?.setWaterfallViewMode(mode)
+        updateWaterfallViewModeMenuState()
+        settingsWindowController?.refreshWaterfallViewMode()
+        writeQuickSwitchReportIfNeeded()
+        DevelopmentDiagnostics.log("app.quickSwitch.waterfallViewModePreferenceChanged", [
+            "mode": mode.rawValue,
+            "quickSwitchVisible": quickSwitchSessionController?.isVisible ?? false
+        ])
+    }
+
+    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"
+        let build = info["CFBundleVersion"] as? String ?? "Unknown"
+        return (version, build)
+    }
+
+    private func statusMenuReportDictionary() -> [String: Any] {
+        updateWaterfallViewModeMenuState()
+        let verticalItem = verticalWaterfallViewModeMenuItems.first
+        let horizontalItem = horizontalWaterfallViewModeMenuItems.first
+        return [
+            "hasAboutItem": statusItem?.menu?.item(withTitle: "About Aligner...") != nil,
+            "hasSettingsItem": statusItem?.menu?.item(withTitle: "Settings...") != nil,
+            "hasQuickSwitchViewSubmenu": statusItem?.menu?.item(withTitle: "Quick Switch View")?.submenu != nil,
+            "hasVerticalColumnsItem": verticalItem != nil,
+            "hasHorizontalWaterfallItem": horizontalItem != nil,
+            "verticalColumnsState": verticalItem?.state == .on ? "on" : "off",
+            "horizontalWaterfallState": horizontalItem?.state == .on ? "on" : "off"
+        ]
+    }
+
+    private func mainMenuReportDictionary() -> [String: Any] {
+        updateWaterfallViewModeMenuState()
+        let appMenu = NSApp.mainMenu?.items.first?.submenu
+        let viewMenu = NSApp.mainMenu?.item(withTitle: "View")?.submenu
+        return [
+            "hasMenu": NSApp.mainMenu != nil,
+            "hasAppMenu": appMenu != nil,
+            "hasAboutItem": appMenu?.item(withTitle: "About Aligner...") != nil,
+            "hasSettingsItem": appMenu?.item(withTitle: "Settings...") != 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
+        ]
+    }
+
+    private func settingsWindowReportDictionary() -> [String: Any] {
+        settingsWindowController?.reportDictionary() ?? [
+            "exists": false,
+            "visible": false,
+            "title": "",
+            "selectedTabLabel": "",
+            "selectedTabIdentifier": "",
+            "waterfallViewMode": quickSwitchWaterfallViewMode().rawValue,
+            "hasViewTab": false,
+            "usesSwiftUI": false
+        ]
+    }
+
+    @objc private func showAbout() {
+        let versionBuild = appVersionBuild()
+        DevelopmentDiagnostics.log("app.menu.showAbout", [
+            "version": versionBuild.version,
+            "build": versionBuild.build
+        ])
+
+        NSApp.setActivationPolicy(.regular)
+        let alert = NSAlert()
+        alert.messageText = "Aligner"
+        alert.informativeText = "Version / Build: \(versionBuild.version) / \(versionBuild.build)"
+        alert.alertStyle = .informational
+        alert.addButton(withTitle: "OK")
+        alert.icon = NSApp.applicationIconImage
+        NSApp.activate(ignoringOtherApps: true)
+        alert.runModal()
+    }
+
+    @objc private func showSettingsFromMenu() {
+        openSettingsWindow(source: "menu")
+    }
+
+    private func showSettingsFromQuickSwitch() {
+        openSettingsWindow(source: "quickSwitch")
+    }
+
+    private func openSettingsWindow(source: String) {
+        DevelopmentDiagnostics.log("app.settings.show", [
+            "source": source,
+            "quickSwitchVisible": quickSwitchSessionController?.isVisible ?? false,
+            "mode": quickSwitchWaterfallViewMode().rawValue
+        ])
+        hideQuickSwitchSession(reason: .userClosed)
+        NSApp.setActivationPolicy(.regular)
+
+        if settingsWindowController == nil {
+            settingsWindowController = SettingsWindowController(
+                modeProvider: { [weak self] in
+                    self?.quickSwitchWaterfallViewMode() ?? .verticalColumns
+                },
+                onWaterfallViewModeChanged: { [weak self] mode in
+                    self?.setQuickSwitchWaterfallViewMode(mode)
+                }
+            )
+        }
+
+        settingsWindowController?.showWindow(nil)
+        NSApp.activate(ignoringOtherApps: true)
+        writeQuickSwitchReportIfNeeded()
+        DevelopmentDiagnostics.log("app.settings.windowShown", [
+            "windowVisible": settingsWindowController?.window?.isVisible ?? false,
+            "activationPolicy": NSApp.activationPolicy().rawValue
+        ])
+    }
+
+    @objc private func useVerticalWaterfallViewMode() {
+        setQuickSwitchWaterfallViewMode(.verticalColumns)
+    }
+
+    @objc private func useHorizontalWaterfallViewMode() {
+        setQuickSwitchWaterfallViewMode(.horizontalMasonry)
+    }
+
     @objc private func showPermissions() {
         DevelopmentDiagnostics.log("app.permissions.show", [
             "accessibility": permissionStatusString(accessibilityStatus()),

--
Gitblit v1.9.3