Ariver
2026-06-04 933e0a66306b7bd53d67f48834cb7f56f5cd8d1b
fix quick switch shelf badges and space labels
4 files modified
70 ■■■■■ changed files
C1.source/Resources/Aligner-Info.plist 4 ●●●● patch | view | raw | blame | history
C1.source/Sources/Aligner/QuickSwitchRootView.swift 23 ●●●● patch | view | raw | blame | history
C1.source/Sources/AlignerCore/Windows/QuickSwitchViewModel.swift 26 ●●●●● patch | view | raw | blame | history
C1.source/Tests/AlignerCoreTests/AlignerCoreTests.swift 17 ●●●●● patch | view | raw | blame | history
C1.source/Resources/Aligner-Info.plist
@@ -17,9 +17,9 @@
    <key>CFBundlePackageType</key>
    <string>APPL</string>
    <key>CFBundleShortVersionString</key>
    <string>0.0.21</string>
    <string>0.0.22</string>
    <key>CFBundleVersion</key>
    <string>20260604.0030</string>
    <string>20260604.1515</string>
    <key>LSMinimumSystemVersion</key>
    <string>26.0</string>
    <key>NSHighResolutionCapable</key>
C1.source/Sources/Aligner/QuickSwitchRootView.swift
@@ -1251,17 +1251,12 @@
            badge.masksToBounds = true
            let selectedIndicator = CALayer()
            selectedIndicator.cornerRadius = 2
            selectedIndicator.backgroundColor = (item.appGroupIndex == selectedAppGroupIndex
                ? NSColor.controlAccentColor
                : NSColor.clear
            ).cgColor
            selectedIndicator.isHidden = true
            container.addSublayer(background)
            container.addSublayer(icon)
            container.addSublayer(label)
            container.addSublayer(badge)
            container.addSublayer(selectedIndicator)
            appShelfContentLayer.addSublayer(container)
            return AppShelfItemLayers(
@@ -1837,21 +1832,13 @@
            : NSColor.controlAccentColor.withAlphaComponent(0.90)
        ).cgColor
        item.badgeLayer.frame = CGRect(
            x: iconX + visualIconSize - badgeWidth + 3,
            y: iconY + visualIconSize - 14,
            x: iconX - 3,
            y: iconY - 2,
            width: badgeWidth,
            height: 16
        )
        item.selectedIndicatorLayer.frame = CGRect(
            x: bounds.midX - 8,
            y: 0,
            width: 16,
            height: 4
        )
        item.selectedIndicatorLayer.backgroundColor = (isSelected
            ? NSColor.controlAccentColor
            : NSColor.clear
        ).cgColor
        item.selectedIndicatorLayer.frame = .zero
        item.selectedIndicatorLayer.backgroundColor = NSColor.clear.cgColor
    }
    private func appShelfRows() -> [[AppShelfItemLayers]] {
C1.source/Sources/AlignerCore/Windows/QuickSwitchViewModel.swift
@@ -208,8 +208,11 @@
        appCountsBySpace: [UInt64: Int],
        appNamesBySpace: [UInt64: [String]]
    ) -> [QuickSwitchDisplayViewModel] {
        displays
        let sortedDisplays = displays
            .sorted { $0.uuid.localizedStandardCompare($1.uuid) == .orderedAscending }
        let displayCount = sortedDisplays.count
        return sortedDisplays
            .enumerated()
            .map { displayIndex, display in
                let displayLabel = displayLabel(for: displayIndex)
@@ -220,7 +223,7 @@
                            id: space.id,
                            displayUUID: space.displayUUID,
                            index: space.index,
                            label: "\(displayLabel)\(space.index)",
                            label: spaceLabel(for: space, displayIndex: displayIndex, displayCount: displayCount),
                            type: space.type,
                            isCurrent: currentSpaceIDs.contains(space.id),
                            windowCount: windowCountsBySpace[space.id] ?? 0,
@@ -256,14 +259,13 @@
    private static func labelsBySpaceID(from displays: [AlignerDisplay]) -> [UInt64: String] {
        var labels: [UInt64: String] = [:]
        for (displayIndex, display) in displays
        let sortedDisplays = displays
            .sorted(by: { $0.uuid.localizedStandardCompare($1.uuid) == .orderedAscending })
            .enumerated()
        {
            let displayLabel = displayLabel(for: displayIndex)
        let displayCount = sortedDisplays.count
        for (displayIndex, display) in sortedDisplays.enumerated() {
            for space in display.spaces {
                labels[space.id] = "\(displayLabel)\(space.index)"
                labels[space.id] = spaceLabel(for: space, displayIndex: displayIndex, displayCount: displayCount)
            }
        }
@@ -328,4 +330,12 @@
        return String(String.UnicodeScalarView(scalars))
    }
    private static func spaceLabel(for space: AlignerSpace, displayIndex: Int, displayCount: Int) -> String {
        if displayCount <= 1 {
            return String(format: "%02d", space.index)
        }
        return "\(displayLabel(for: displayIndex))\(space.index)"
    }
}
C1.source/Tests/AlignerCoreTests/AlignerCoreTests.swift
@@ -1155,6 +1155,23 @@
        XCTAssertEqual(viewModel.initialSelection, QuickSwitchSelection(appGroupIndex: 0, windowIndex: 0, windowID: 1))
    }
    func testQuickSwitchViewModelBuilderUsesNumericSpaceLabelsForSingleDisplay() {
        let app = AlignerApp(bundleIdentifier: "com.example.Alpha", name: "Alpha", category: .generic)
        let snapshot = QuickSwitchSnapshotBuilder.snapshot(
            displays: [makeDisplay(spaceIDs: [10, 11, 12])],
            windows: [
                AlignerWindow(id: 1, app: app, title: "A1", spaceIDs: [10]),
                AlignerWindow(id: 2, app: app, title: "A2", spaceIDs: [11])
            ]
        )
        let viewModel = QuickSwitchViewModelBuilder.viewModel(from: snapshot, currentSpaceIDs: [11])
        XCTAssertEqual(viewModel.displays.flatMap { $0.spaces.map(\.label) }, ["01", "02", "03"])
        XCTAssertEqual(viewModel.displays.flatMap { $0.spaces.filter(\.isCurrent).map(\.label) }, ["02"])
        XCTAssertEqual(viewModel.waterfallColumns.flatMap { $0.windows.map(\.primarySpaceLabel) }, ["01", "02"])
    }
    func testQuickSwitchFocusPolicySelectsFirstStableWindowInitially() {
        let app = makeApp()
        let snapshot = QuickSwitchSnapshotBuilder.snapshot(