Ariver
2026-06-11 3d8d37306e39db5c8eb41fafd95a13112d573f62
C1.source/Sources/Aligner/QuickSwitchRootView.swift
@@ -78,8 +78,16 @@
    private var keyboardResponseLatencyMilliseconds: [Double] = []
    private var mouseResponseLatencyMilliseconds: [Double] = []
    private var hoverResponseLatencyMilliseconds: [Double] = []
    private var lastProjectionTransitionMovedLayerCount = 0
    private var lastProjectionTransitionFadeInLayerCount = 0
    private var lastProjectionTransitionFadeOutLayerCount = 0
    private var lastProjectionTransitionDurationMilliseconds: Double = 0
    private var suppressEventInput = false
    private var suppressEventBackgroundClicks = false
    var onEscape: (() -> Void)?
    var onCommitSelection: ((QuickSwitchSelection, QuickSwitchCommitSource) -> Void)?
    var onSpaceLaneClick: ((UInt64, Int) -> Void)?
    var onBackgroundClick: (() -> Bool)?
    var onRequestClose: ((QuickSwitchCloseRequest) -> Void)?
    var onCloseConfirmationDisabled: (() -> Void)?
@@ -258,6 +266,22 @@
        let closeButton: CloseButtonLayers
    }
    private struct ProjectionLayerSnapshot {
        let layer: CALayer
        let frame: CGRect
    }
    private struct ProjectionColumnSnapshot {
        let layer: CALayer
        let frame: CGRect
        let cardsByWindowID: [UInt32: ProjectionLayerSnapshot]
    }
    private struct ProjectionTransitionSnapshot {
        let appShelfItemsByAppGroupIndex: [Int: ProjectionLayerSnapshot]
        let waterfallColumnsByAppGroupIndex: [Int: ProjectionColumnSnapshot]
    }
    private enum AppShelfMetrics {
        static let normalIconSize: CGFloat = 56
        static let hoverIconSize: CGFloat = 68
@@ -358,6 +382,13 @@
    }
    override func keyDown(with event: NSEvent) {
        if suppressEventInput {
            DevelopmentDiagnostics.log("quickSwitch.view.keyDown.ignoredForDebugSequence", [
                "keyCode": event.keyCode
            ])
            return
        }
        switch event.keyCode {
        case TriggerKeyCode.escape where Self.shouldDismissOnEscape(event):
            onEscape?()
@@ -441,6 +472,15 @@
    }
    override func mouseDown(with event: NSEvent) {
        if suppressEventInput {
            DevelopmentDiagnostics.log("quickSwitch.view.mouseDown.ignoredForDebugSequence", [
                "x": event.locationInWindow.x,
                "y": event.locationInWindow.y,
                "bounds": NSStringFromRect(bounds)
            ])
            return
        }
        if handleCloseConfirmationMouseDown(at: event.locationInWindow) {
            return
        }
@@ -456,7 +496,7 @@
        }
        if let segment = spaceLaneSegment(at: event.locationInWindow) {
            clickSpaceLaneSegment(segment)
            clickSpaceLaneSegment(segment, clickCount: max(1, event.clickCount))
            return
        }
@@ -470,6 +510,9 @@
            "y": event.locationInWindow.y,
            "bounds": NSStringFromRect(bounds)
        ])
        if !suppressEventBackgroundClicks, onBackgroundClick?() == true {
            return
        }
        super.mouseDown(with: event)
    }
@@ -511,6 +554,15 @@
                break
            }
        }
    }
    func setSuppressEventBackgroundClicks(_ suppressed: Bool) {
        suppressEventBackgroundClicks = suppressed
    }
    func setSuppressEventInput(_ suppressed: Bool) {
        suppressEventInput = suppressed
        suppressEventBackgroundClicks = suppressed
    }
    func beginPerformanceFirstFrameMeasurement() {
@@ -608,35 +660,73 @@
        }
    }
    func effectiveSelectionForProjection() -> QuickSwitchSelection? {
        effectiveSelection
    }
    func apply(viewModel: QuickSwitchViewModel?) {
        apply(viewModel: viewModel, preservingInteractionState: false, animatedProjection: false)
    }
    func applyProjected(viewModel: QuickSwitchViewModel?) {
        apply(viewModel: viewModel, preservingInteractionState: true, animatedProjection: true)
    }
    private func apply(
        viewModel: QuickSwitchViewModel?,
        preservingInteractionState: Bool,
        animatedProjection: Bool
    ) {
        let transitionSnapshot = animatedProjection ? captureProjectionTransitionSnapshot() : nil
        if !animatedProjection {
            resetProjectionTransitionReport()
        }
        currentViewModel = viewModel
        screenshotSourcesByWindowID = [:]
        screenshotSkippedReasonsByWindowID = [:]
        currentSelection = viewModel?.initialSelection
        columnSelectionHistory = [:]
        keyboardCommandsApplied = []
        lastKeyboardCommand = nil
        tabIgnoredCount = 0
        lastCommittedSelection = nil
        lastCommitSource = nil
        mouseCommandsApplied = []
        lastMouseCommand = nil
        lastClickedAppGroupIndex = nil
        lastAppShelfClickChangedSelection = nil
        lastClickedWindowID = nil
        lastSpaceFocusedAppGroupIndex = nil
        lastSpaceLaneClickSpaceID = nil
        lastAppShelfScrollSyncedAppGroupIndex = nil
        focusedSpaceID = nil
        hoveredCloseTarget = nil
        pendingCloseTarget = nil
        pendingCloseRequest = nil
        pendingCloseOptOut = false
        closeConfirmationLayers.containerLayer.isHidden = true
        closeConfirmationArrowEdge = nil
        clearCloseFeedback()
        setHoverTarget(.none)
        hoveredAppGroupIndex = debugHoveredAppGroupIndex
        if preservingInteractionState {
            currentSelection = validSelection(currentSelection, in: viewModel) ?? viewModel?.initialSelection
            columnSelectionHistory = columnSelectionHistory.filter { appGroupIndex, _ in
                viewModel?.waterfallColumns.contains { $0.appGroupIndex == appGroupIndex } == true
            }
            if let currentSelection {
                columnSelectionHistory[currentSelection.appGroupIndex] = currentSelection.windowIndex
            }
            hoveredCloseTarget = nil
            pendingCloseTarget = nil
            pendingCloseRequest = nil
            pendingCloseOptOut = false
            closeConfirmationLayers.containerLayer.isHidden = true
            closeConfirmationArrowEdge = nil
        } else {
            screenshotSourcesByWindowID = [:]
            screenshotSkippedReasonsByWindowID = [:]
            currentSelection = viewModel?.initialSelection
            columnSelectionHistory = [:]
            keyboardCommandsApplied = []
            lastKeyboardCommand = nil
            tabIgnoredCount = 0
            lastCommittedSelection = nil
            lastCommitSource = nil
            mouseCommandsApplied = []
            lastMouseCommand = nil
            lastClickedAppGroupIndex = nil
            lastAppShelfClickChangedSelection = nil
            lastClickedWindowID = nil
            lastSpaceFocusedAppGroupIndex = nil
            lastSpaceLaneClickSpaceID = nil
            lastAppShelfScrollSyncedAppGroupIndex = nil
            focusedSpaceID = nil
            hoveredCloseTarget = nil
            pendingCloseTarget = nil
            pendingCloseRequest = nil
            pendingCloseOptOut = false
            closeConfirmationLayers.containerLayer.isHidden = true
            closeConfirmationArrowEdge = nil
            clearCloseFeedback()
            setHoverTarget(.none)
            hoveredAppGroupIndex = debugHoveredAppGroupIndex
        }
        if let currentSelection {
            columnSelectionHistory[currentSelection.appGroupIndex] = currentSelection.windowIndex
        }
@@ -646,6 +736,250 @@
        needsLayout = true
        ensureCurrentSelectionVisible(horizontalIntent: .alignWithAppShelf(animated: false))
        needsLayout = true
        if animatedProjection, let transitionSnapshot {
            layoutSubtreeIfNeeded()
            applyProjectionTransitionAnimations(from: transitionSnapshot)
        }
    }
    private func validSelection(
        _ selection: QuickSwitchSelection?,
        in viewModel: QuickSwitchViewModel?
    ) -> QuickSwitchSelection? {
        guard let selection, let viewModel else { return nil }
        for column in viewModel.waterfallColumns {
            if let card = column.windows.first(where: { $0.window.id == selection.windowID }) {
                return QuickSwitchSelection(
                    appGroupIndex: card.appGroupIndex,
                    windowIndex: card.windowIndex,
                    windowID: card.window.id
                )
            }
        }
        return nil
    }
    private func captureProjectionTransitionSnapshot() -> ProjectionTransitionSnapshot {
        ProjectionTransitionSnapshot(
            appShelfItemsByAppGroupIndex: Dictionary(
                uniqueKeysWithValues: appShelfItems.map { item in
                    (
                        item.item.appGroupIndex,
                        ProjectionLayerSnapshot(
                            layer: item.containerLayer,
                            frame: item.containerLayer.frame
                        )
                    )
                }
            ),
            waterfallColumnsByAppGroupIndex: Dictionary(
                uniqueKeysWithValues: waterfallColumns.map { column in
                    (
                        column.column.appGroupIndex,
                        ProjectionColumnSnapshot(
                            layer: column.containerLayer,
                            frame: column.containerLayer.frame,
                            cardsByWindowID: Dictionary(
                                uniqueKeysWithValues: column.cards.map { card in
                                    (
                                        card.item.window.id,
                                        ProjectionLayerSnapshot(
                                            layer: card.containerLayer,
                                            frame: card.containerLayer.frame
                                        )
                                    )
                                }
                            )
                        )
                    )
                }
            )
        )
    }
    private func applyProjectionTransitionAnimations(from snapshot: ProjectionTransitionSnapshot) {
        let reduceMotion = NSWorkspace.shared.accessibilityDisplayShouldReduceMotion
        let duration: CFTimeInterval = reduceMotion ? 0.12 : 0.21
        let timing = CAMediaTimingFunction(name: reduceMotion ? .linear : .easeOut)
        var movedLayerCount = 0
        var fadeInLayerCount = 0
        var fadeOutLayerCount = 0
        for item in appShelfItems {
            guard let oldSnapshot = snapshot.appShelfItemsByAppGroupIndex[item.item.appGroupIndex] else {
                if addFadeInAnimation(to: item.containerLayer, duration: duration, timing: timing) {
                    fadeInLayerCount += 1
                }
                continue
            }
            if addPositionAnimation(to: item.containerLayer, from: oldSnapshot.frame, duration: duration, timing: timing) {
                movedLayerCount += 1
            }
        }
        let visibleAppGroupIndexes = Set(appShelfItems.map(\.item.appGroupIndex))
        for (appGroupIndex, oldSnapshot) in snapshot.appShelfItemsByAppGroupIndex
            where !visibleAppGroupIndexes.contains(appGroupIndex) {
            if addFadeOutSnapshot(
                oldSnapshot,
                to: appShelfContentLayer,
                frame: oldSnapshot.frame,
                duration: duration,
                timing: timing
            ) {
                fadeOutLayerCount += 1
            }
        }
        for column in waterfallColumns {
            guard let oldSnapshot = snapshot.waterfallColumnsByAppGroupIndex[column.column.appGroupIndex] else {
                if addFadeInAnimation(to: column.containerLayer, duration: duration, timing: timing) {
                    fadeInLayerCount += 1
                }
                continue
            }
            if addPositionAnimation(to: column.containerLayer, from: oldSnapshot.frame, duration: duration, timing: timing) {
                movedLayerCount += 1
            }
            for card in column.cards {
                guard let oldCardSnapshot = oldSnapshot.cardsByWindowID[card.item.window.id] else {
                    if addFadeInAnimation(to: card.containerLayer, duration: duration, timing: timing) {
                        fadeInLayerCount += 1
                    }
                    continue
                }
                if addPositionAnimation(to: card.containerLayer, from: oldCardSnapshot.frame, duration: duration, timing: timing) {
                    movedLayerCount += 1
                }
            }
        }
        let visibleColumnsByAppGroupIndex = Dictionary(
            uniqueKeysWithValues: waterfallColumns.map { ($0.column.appGroupIndex, $0) }
        )
        for (appGroupIndex, oldSnapshot) in snapshot.waterfallColumnsByAppGroupIndex {
            guard let visibleColumn = visibleColumnsByAppGroupIndex[appGroupIndex] else {
                if addFadeOutSnapshot(
                    ProjectionLayerSnapshot(layer: oldSnapshot.layer, frame: oldSnapshot.frame),
                    to: waterfallContentLayer,
                    frame: oldSnapshot.frame,
                    duration: duration,
                    timing: timing
                ) {
                    fadeOutLayerCount += 1
                }
                continue
            }
            let visibleWindowIDs = Set(visibleColumn.cards.map(\.item.window.id))
            for (windowID, oldCardSnapshot) in oldSnapshot.cardsByWindowID
                where !visibleWindowIDs.contains(windowID) {
                let absoluteFrame = oldCardSnapshot.frame.offsetBy(
                    dx: oldSnapshot.frame.minX,
                    dy: oldSnapshot.frame.minY
                )
                if addFadeOutSnapshot(
                    oldCardSnapshot,
                    to: waterfallContentLayer,
                    frame: absoluteFrame,
                    duration: duration,
                    timing: timing
                ) {
                    fadeOutLayerCount += 1
                }
            }
        }
        lastProjectionTransitionMovedLayerCount = movedLayerCount
        lastProjectionTransitionFadeInLayerCount = fadeInLayerCount
        lastProjectionTransitionFadeOutLayerCount = fadeOutLayerCount
        lastProjectionTransitionDurationMilliseconds = duration * 1_000
    }
    private func resetProjectionTransitionReport() {
        lastProjectionTransitionMovedLayerCount = 0
        lastProjectionTransitionFadeInLayerCount = 0
        lastProjectionTransitionFadeOutLayerCount = 0
        lastProjectionTransitionDurationMilliseconds = 0
    }
    @discardableResult
    private func addPositionAnimation(
        to layer: CALayer,
        from oldFrame: CGRect,
        duration: CFTimeInterval,
        timing: CAMediaTimingFunction
    ) -> Bool {
        let oldPosition = CGPoint(x: oldFrame.midX, y: oldFrame.midY)
        let newPosition = layer.position
        guard abs(oldPosition.x - newPosition.x) > 0.5 || abs(oldPosition.y - newPosition.y) > 0.5 else {
            return false
        }
        let animation = CABasicAnimation(keyPath: "position")
        animation.fromValue = oldPosition
        animation.toValue = newPosition
        animation.duration = duration
        animation.timingFunction = timing
        animation.isRemovedOnCompletion = true
        layer.add(animation, forKey: "quickSwitchProjectionPosition")
        return true
    }
    @discardableResult
    private func addFadeInAnimation(
        to layer: CALayer,
        duration: CFTimeInterval,
        timing: CAMediaTimingFunction
    ) -> Bool {
        let animation = CABasicAnimation(keyPath: "opacity")
        animation.fromValue = 0
        animation.toValue = layer.opacity
        animation.duration = duration
        animation.timingFunction = timing
        animation.isRemovedOnCompletion = true
        layer.add(animation, forKey: "quickSwitchProjectionFadeIn")
        return true
    }
    @discardableResult
    private func addFadeOutSnapshot(
        _ snapshot: ProjectionLayerSnapshot,
        to parentLayer: CALayer,
        frame: CGRect,
        duration: CFTimeInterval,
        timing: CAMediaTimingFunction
    ) -> Bool {
        let layer = snapshot.layer
        layer.removeAllAnimations()
        layer.removeFromSuperlayer()
        CATransaction.begin()
        CATransaction.setDisableActions(true)
        layer.frame = frame
        layer.opacity = max(layer.opacity, 0.01)
        layer.zPosition = max(layer.zPosition, 240)
        parentLayer.addSublayer(layer)
        CATransaction.commit()
        let animation = CABasicAnimation(keyPath: "opacity")
        animation.fromValue = layer.opacity
        animation.toValue = 0
        animation.duration = duration
        animation.timingFunction = timing
        animation.isRemovedOnCompletion = true
        CATransaction.begin()
        CATransaction.setDisableActions(true)
        layer.opacity = 0
        CATransaction.commit()
        layer.add(animation, forKey: "quickSwitchProjectionFadeOut")
        DispatchQueue.main.asyncAfter(deadline: .now() + duration + 0.05) { [weak layer] in
            layer?.removeFromSuperlayer()
        }
        return true
    }
    func reportDictionary() -> [String: Any] {
@@ -685,6 +1019,8 @@
            "spaceLaneHoveredSpaceID": hoveredSpaceLaneID ?? NSNull(),
            "spaceLaneFocusedSpaceID": focusedSpaceID ?? NSNull(),
            "activeSpaceFocusID": activeSpaceFocusID ?? NSNull(),
            "spaceFilterLockedSpaceID": currentViewModel?.lockedSpaceID ?? NSNull(),
            "spaceFilterActive": currentViewModel?.lockedSpaceID != nil,
            "spaceFocusAppGroupIndexes": spaceFocusedAppGroupIndexes(),
            "appHoverAssociatedSpaceIDs": appHoverAssociatedSpaceIDs.sorted(),
            "appHoverAssociatedSpaceLabels": appHoverAssociatedSpaceLabels,
@@ -722,7 +1058,8 @@
            "waterfallMaxScrollOffset": Double(waterfallMaxScrollOffset),
            "waterfallAlignmentMinScrollOffset": Double(waterfallAlignmentScrollRange.lowerBound),
            "waterfallAlignmentMaxScrollOffset": Double(waterfallAlignmentScrollRange.upperBound),
            "waterfallScrollable": waterfallAlignmentScrollable,
            "waterfallScrollable": waterfallMaxScrollOffset > 0,
            "waterfallAlignmentScrollable": waterfallAlignmentScrollable,
            "waterfallColumnCount": waterfallColumns.count,
            "waterfallColumnNames": waterfallColumns.map(\.column.app.name),
            "waterfallClipsToBounds": waterfallLayer.masksToBounds,
@@ -731,6 +1068,10 @@
            "waterfallAlignmentTolerance": Double(WaterfallAlignmentMetrics.tolerance),
            "waterfallAlignmentAnimationDurationMilliseconds": Double(WaterfallAlignmentMetrics.animationDuration * 1_000),
            "waterfallAlignmentAnimationTimingName": "easeOut",
            "projectionTransitionMovedLayerCount": lastProjectionTransitionMovedLayerCount,
            "projectionTransitionFadeInLayerCount": lastProjectionTransitionFadeInLayerCount,
            "projectionTransitionFadeOutLayerCount": lastProjectionTransitionFadeOutLayerCount,
            "projectionTransitionDurationMilliseconds": lastProjectionTransitionDurationMilliseconds,
            "reduceMotionEffective": NSWorkspace.shared.accessibilityDisplayShouldReduceMotion,
            "selectedAppGroupIndex": effectiveSelection?.appGroupIndex ?? NSNull(),
            "selectedWindowIndex": effectiveSelection?.windowIndex ?? NSNull(),
@@ -897,8 +1238,21 @@
            }
            recordMouseCommand("click-space:\(spaceID)")
            if let segment = spaceLaneSegment(spaceID: spaceID) {
                clickSpaceLaneSegment(segment)
                clickSpaceLaneSegment(segment, clickCount: 1)
            }
        case "double-click-space" where parts.count == 2:
            guard let spaceID = UInt64(parts[1]) else {
                recordMouseCommand("invalid:\(command)")
                return
            }
            recordMouseCommand("double-click-space:\(spaceID)")
            if let segment = spaceLaneSegment(spaceID: spaceID) {
                clickSpaceLaneSegment(segment, clickCount: 1)
                clickSpaceLaneSegment(segment, clickCount: 2)
            }
        case "click-background":
            recordMouseCommand("click-background")
            _ = onBackgroundClick?()
        case "hover-card" where parts.count == 3:
            guard let appGroupIndex = Int(parts[1]), let windowIndex = Int(parts[2]) else {
                recordMouseCommand("invalid:\(command)")
@@ -1014,22 +1368,15 @@
        focusSpace(segment.space.id, persistent: false, source: source)
    }
    private func clickSpaceLaneSegment(_ segment: SpaceLaneSegmentLayers) {
    private func clickSpaceLaneSegment(_ segment: SpaceLaneSegmentLayers, clickCount: Int = 1) {
        DevelopmentDiagnostics.log("quickSwitch.view.clickSpaceLane", [
            "spaceID": segment.space.id,
            "label": segment.space.label
            "label": segment.space.label,
            "clickCount": clickCount
        ])
        lastSpaceLaneClickSpaceID = segment.space.id
        guard let firstCard = firstWaterfallCard(inSpaceID: segment.space.id) else {
            DevelopmentDiagnostics.log("quickSwitch.view.clickSpaceLane.emptySpaceUnsupported", [
                "spaceID": segment.space.id,
                "label": segment.space.label
            ])
            return
        }
        commitWindowCard(firstCard, trigger: "spaceLane")
        onSpaceLaneClick?(segment.space.id, clickCount)
        lastSpaceLaneClickSpaceID = segment.space.id
    }
    private func focusSpace(_ spaceID: UInt64, persistent: Bool, source: HoverTargetSource = .event) {
@@ -1321,6 +1668,11 @@
            return
        }
        if waterfallShouldKeepFilteredColumnsCentered {
            ensureWaterfallCardVisibleVertically(selection)
            return
        }
        switch horizontalIntent {
        case .reveal:
            ensureWaterfallColumnVisible(column)
@@ -1404,6 +1756,10 @@
    }
    private var waterfallAlignmentScrollRange: ClosedRange<CGFloat> {
        if waterfallShouldKeepFilteredColumnsCentered {
            return 0...0
        }
        var minimumOffset: CGFloat = 0
        var maximumOffset = waterfallMaxScrollOffset
@@ -1428,6 +1784,10 @@
    private func clampedWaterfallAlignmentOffset(_ offset: CGFloat) -> CGFloat {
        let range = waterfallAlignmentScrollRange
        return min(max(range.lowerBound, offset), range.upperBound)
    }
    private var waterfallShouldKeepFilteredColumnsCentered: Bool {
        currentViewModel?.lockedSpaceID != nil && waterfallMaxScrollOffset <= 0.5
    }
    private func setWaterfallScrollOffset(_ offset: CGFloat, animated: Bool) {
@@ -1835,12 +2195,14 @@
            groupContainer.shadowOffset = .zero
            let segments = display.spaces.map { space in
                let container = CALayer()
                let container = CAGradientLayer()
                container.name = space.label
                container.cornerRadius = 10
                container.borderWidth = 1
                container.borderColor = NSColor.separatorColor.withAlphaComponent(0.34).cgColor
                container.backgroundColor = spaceColor(for: space).cgColor
                container.backgroundColor = NSColor.clear.cgColor
                container.startPoint = CGPoint(x: 0.5, y: 1.0)
                container.endPoint = CGPoint(x: 0.5, y: 0.0)
                container.shadowColor = NSColor.black.cgColor
                let labelLayer = makeTextLayer(
@@ -2115,6 +2477,7 @@
            segment.space.id == activeSpaceFocusID
                || appHoverAssociatedSpaceIDs.contains(segment.space.id)
                || segment.space.id == windowHoverAssociatedSpaceID
                || segment.space.id == currentViewModel?.lockedSpaceID
        }
        let hasCurrentSegment = group.display.spaces.contains { $0.isCurrent }
        group.containerLayer.backgroundColor = NSColor.windowBackgroundColor.cgColor
@@ -2130,16 +2493,19 @@
    private func applySpaceLaneVisualState(_ segment: SpaceLaneSegmentLayers) {
        let isHovered = segment.space.id == activeSpaceFocusID
        let isFocused = segment.space.id == activeSpaceFocusID
        let isLocked = segment.space.id == currentViewModel?.lockedSpaceID
        let isCurrent = segment.space.isCurrent
        let hasWindows = segment.space.windowCount > 0
        let isAppAssociated = appHoverAssociatedSpaceIDs.contains(segment.space.id)
        let isWindowAssociated = segment.space.id == windowHoverAssociatedSpaceID
        let isActive = isHovered || isFocused
        let isActive = isHovered || isFocused || isLocked
        let isAssociated = (isAppAssociated || isWindowAssociated) && !isActive
        segment.containerLayer.borderWidth = (hasWindows || isCurrent || isActive || isAssociated) ? 1.5 : 1
        segment.containerLayer.borderWidth = isLocked
            ? 6
            : (hasWindows || isCurrent || isActive || isAssociated) ? 1.5 : 1
        segment.containerLayer.borderColor = (isActive
            ? spaceLaneOccupiedColor.withAlphaComponent(0.62)
            ? spaceLaneOccupiedColor.withAlphaComponent(isLocked ? 0.92 : 0.62)
            : isAssociated
                ? spaceLaneOccupiedColor.withAlphaComponent(0.50)
                : hasWindows
@@ -2148,28 +2514,49 @@
                    ? NSColor.labelColor.withAlphaComponent(0.34)
                : NSColor.separatorColor.withAlphaComponent(0.34)
        ).cgColor
        segment.containerLayer.backgroundColor = (isActive
            ? accentTintedWindowBackground(fraction: 0.10, alpha: 0.94)
            : isAssociated
                ? accentTintedWindowBackground(fraction: 0.07, alpha: 0.90)
                : spaceColor(for: segment.space)
        ).cgColor
        segment.containerLayer.shadowOpacity = isActive ? 0.26 : isAssociated ? 0.18 : isCurrent ? 0.14 : 0
        segment.containerLayer.shadowRadius = isActive ? 24 : isAssociated ? 18 : isCurrent ? 12 : 0
        if !hasWindows {
            applySpaceLaneSolidFill(to: segment.containerLayer, color: .clear)
        } else if isActive {
            applySpaceLaneSolidFill(
                to: segment.containerLayer,
                color: accentTintedWindowBackground(fraction: 0.10, alpha: 0.94)
            )
        } else if isAssociated {
            applySpaceLaneSolidFill(
                to: segment.containerLayer,
                color: accentTintedWindowBackground(fraction: 0.07, alpha: 0.90)
            )
        } else if hasWindows {
            applySpaceLaneGradientFill(
                to: segment.containerLayer,
                colors: spaceLaneIdleOccupiedGradientColors()
            )
        }
        segment.containerLayer.shadowColor = isLocked
            ? NSColor.white.withAlphaComponent(0.98).cgColor
            : NSColor.black.cgColor
        segment.containerLayer.shadowOpacity = (hasWindows || isLocked)
            ? (isLocked ? 0.72 : isActive ? 0.26 : isAssociated ? 0.18 : isCurrent ? 0.14 : 0)
            : 0
        segment.containerLayer.shadowRadius = (hasWindows || isLocked)
            ? (isLocked ? 30 : isActive ? 24 : isAssociated ? 18 : isCurrent ? 12 : 0)
            : 0
        segment.containerLayer.shadowOffset = CGSize(
            width: 0,
            height: isActive ? -6 : isAssociated ? -4 : isCurrent ? -2.5 : 0
            height: (hasWindows || isLocked) ? (isLocked ? 0 : isActive ? -6 : isAssociated ? -4 : isCurrent ? -2.5 : 0) : 0
        )
        segment.containerLayer.zPosition = isActive ? 30 : isAssociated ? 18 : isCurrent ? 10 : 0
        segment.containerLayer.zPosition = isLocked ? 42 : isActive ? 30 : isAssociated ? 18 : isCurrent ? 10 : 0
        segment.containerLayer.transform = (isActive || isAssociated)
            ? CATransform3DMakeScale(isActive ? 1.03 : 1.015, isActive ? 1.03 : 1.015, 1)
            : CATransform3DIdentity
        segment.containerLayer.shadowPath = CGPath(
            roundedRect: segment.containerLayer.bounds,
            cornerWidth: segment.containerLayer.cornerRadius,
            cornerHeight: segment.containerLayer.cornerRadius,
            transform: nil
        )
        segment.containerLayer.shadowPath = hasWindows
            ? CGPath(
                roundedRect: segment.containerLayer.bounds,
                cornerWidth: segment.containerLayer.cornerRadius,
                cornerHeight: segment.containerLayer.cornerRadius,
                transform: nil
            )
            : nil
    }
    private func shouldScrollSpaceLane(for event: NSEvent) -> Bool {
@@ -2645,9 +3032,9 @@
        }
        let columnWidth = waterfallColumnWidth()
        let totalWidth = WaterfallMetrics.contentHorizontalPadding * 2
            + CGFloat(waterfallColumns.count) * columnWidth
        let columnsWidth = CGFloat(waterfallColumns.count) * columnWidth
            + CGFloat(max(0, waterfallColumns.count - 1)) * WaterfallMetrics.gap
        let totalWidth = WaterfallMetrics.contentHorizontalPadding * 2 + columnsWidth
        waterfallContentWidth = max(waterfallLayer.bounds.width, totalWidth)
        waterfallScrollOffset = clampedWaterfallAlignmentOffset(waterfallScrollOffset)
        waterfallContentLayer.frame = CGRect(
@@ -2657,7 +3044,7 @@
            height: waterfallLayer.bounds.height
        )
        var x = WaterfallMetrics.contentHorizontalPadding
        var x = waterfallColumnStartX(columnsWidth: columnsWidth)
        for column in waterfallColumns {
            column.containerLayer.frame = CGRect(
                x: x,
@@ -2668,6 +3055,15 @@
            layoutWaterfallColumn(column)
            x += columnWidth + WaterfallMetrics.gap
        }
    }
    private func waterfallColumnStartX(columnsWidth: CGFloat) -> CGFloat {
        guard currentViewModel?.lockedSpaceID != nil else {
            return WaterfallMetrics.contentHorizontalPadding
        }
        let centeredStart = (waterfallContentWidth - columnsWidth) / 2
        return max(WaterfallMetrics.contentHorizontalPadding, centeredStart)
    }
    private func layoutWaterfallColumn(_ column: WaterfallColumnLayers) {
@@ -3440,20 +3836,43 @@
        return states.joined(separator: " / ")
    }
    private func spaceColor(for space: QuickSwitchSpaceViewModel) -> NSColor {
        if space.windowCount > 0 {
            return NSColor.controlBackgroundColor.withAlphaComponent(0.36)
        }
        return NSColor.clear
    }
    private var spaceLaneOccupiedColor: NSColor {
        if effectiveAppearance.bestMatch(from: [.darkAqua, .aqua]) == .darkAqua {
            return NSColor(calibratedRed: 10 / 255, green: 132 / 255, blue: 1, alpha: 1)
        }
        return NSColor(calibratedRed: 0, green: 122 / 255, blue: 1, alpha: 1)
    }
    private func spaceLaneIdleOccupiedGradientColors() -> [NSColor] {
        if effectiveAppearance.bestMatch(from: [.darkAqua, .aqua]) == .darkAqua {
            let blue = NSColor(calibratedRed: 36 / 255, green: 54 / 255, blue: 72 / 255, alpha: 1)
            let surface = NSColor.windowBackgroundColor.withAlphaComponent(1)
            return [blue, surface]
        }
        return [
            NSColor(calibratedRed: 232 / 255, green: 246 / 255, blue: 1, alpha: 1),
            NSColor.white
        ]
    }
    private func applySpaceLaneGradientFill(to layer: CALayer, colors: [NSColor]) {
        if let gradientLayer = layer as? CAGradientLayer {
            gradientLayer.colors = colors.map(\.cgColor)
            gradientLayer.locations = [0, 1]
            gradientLayer.startPoint = CGPoint(x: 0.5, y: 1.0)
            gradientLayer.endPoint = CGPoint(x: 0.5, y: 0.0)
        }
        layer.backgroundColor = NSColor.clear.cgColor
    }
    private func applySpaceLaneSolidFill(to layer: CALayer, color: NSColor) {
        if let gradientLayer = layer as? CAGradientLayer {
            gradientLayer.colors = nil
            gradientLayer.locations = nil
        }
        layer.backgroundColor = color.cgColor
    }
    private func makeFullscreenMarkerLayer(for space: QuickSwitchSpaceViewModel) -> CALayer? {
@@ -3675,11 +4094,18 @@
    }
    private func fullscreenMarkerFrame(in segmentBounds: CGRect) -> CGRect {
        CGRect(
        let topReserved: CGFloat = 32
        let bottomReserved: CGFloat = 16
        let availableHeight = max(20, segmentBounds.height - topReserved - bottomReserved)
        let markerHeight = min(30, max(20, availableHeight))
        let markerCenterY = bottomReserved + availableHeight * 0.5
        let markerY = max(8, min(segmentBounds.height - topReserved - markerHeight, markerCenterY - markerHeight * 0.5))
        return CGRect(
            x: 10,
            y: 30,
            y: markerY,
            width: max(1, segmentBounds.width - 20),
            height: max(1, segmentBounds.height - 58)
            height: markerHeight
        )
    }
@@ -4049,6 +4475,10 @@
                    dx: (group?.containerLayer.frame.minX ?? 0) + spaceLaneContentLayer.frame.minX,
                    dy: group?.containerLayer.frame.minY ?? 0
                )
                let fullscreenMarkerFrame = segment.fullscreenMarkerLayer?.frame
                let fullscreenMarkerCenterRatio = fullscreenMarkerFrame.map {
                    Double($0.midY / max(1, segment.containerLayer.bounds.height))
                }
                return [
                    "displayUUID": display.displayUUID,
                    "displayLabel": display.displayLabel,
@@ -4058,15 +4488,23 @@
                    "visualStates": visualStates(for: space),
                    "fullscreenMarkerVisible": segment.fullscreenMarkerLayer != nil,
                    "fullscreenMarkerKind": segment.fullscreenMarkerLayer == nil ? "none" : "cornerBracket",
                    "fullscreenMarkerFrame": segment.fullscreenMarkerLayer.map { dictionary(from: $0.frame) } ?? NSNull(),
                    "fullscreenMarkerFrame": fullscreenMarkerFrame.map { dictionary(from: $0) } ?? NSNull(),
                    "fullscreenMarkerCenterRatio": fullscreenMarkerCenterRatio ?? NSNull(),
                    "windowBlockCount": segment.windowBlocks.count,
                    "windowBlockColors": segment.windowBlocks.map { colorDictionary(from: $0.backgroundColor) },
                    "windowBlockFrames": segment.windowBlocks.map { dictionary(from: $0.frame) },
                    "backgroundKind": spaceLaneFillKind(for: segment.containerLayer),
                    "backgroundColor": colorDictionary(from: segment.containerLayer.backgroundColor),
                    "gradientColors": gradientColorDictionaries(from: segment.containerLayer),
                    "borderColor": colorDictionary(from: segment.containerLayer.borderColor),
                    "borderWidth": Double(segment.containerLayer.borderWidth),
                    "shadowColor": colorDictionary(from: segment.containerLayer.shadowColor),
                    "shadowOpacity": Double(segment.containerLayer.shadowOpacity),
                    "shadowRadius": Double(segment.containerLayer.shadowRadius),
                    "shadowOffset": [
                        "width": Double(segment.containerLayer.shadowOffset.width),
                        "height": Double(segment.containerLayer.shadowOffset.height)
                    ],
                    "appFrame": dictionary(from: segment.appLayer.frame),
                    "frame": dictionary(from: segment.containerLayer.frame),
                    "visibleFrame": dictionary(from: visibleFrame),
@@ -4105,6 +4543,9 @@
        }
        if space.id == activeSpaceFocusID {
            states.append("focused")
        }
        if space.id == currentViewModel?.lockedSpaceID {
            states.append("locked")
        }
        if space.type == .fullscreen {
            states.append("fullscreen")
@@ -4153,4 +4594,23 @@
            "alpha": Double(nsColor.alphaComponent)
        ]
    }
    private func gradientColorDictionaries(from layer: CALayer) -> [[String: Double]] {
        guard let gradientLayer = layer as? CAGradientLayer else { return [] }
        return (gradientLayer.colors ?? []).map { color in
            let cgColor = color as! CGColor
            return colorDictionary(from: cgColor)
        }
    }
    private func spaceLaneFillKind(for layer: CALayer) -> String {
        if !gradientColorDictionaries(from: layer).isEmpty {
            return "gradient"
        }
        let background = colorDictionary(from: layer.backgroundColor)
        if background["alpha", default: 0] <= 0.01 {
            return "clear"
        }
        return "solid"
    }
}