diff --git a/Packages/macOS/CmuxSettingsUI/Sources/CmuxSettingsUI/Sections/SidebarSection.swift b/Packages/macOS/CmuxSettingsUI/Sources/CmuxSettingsUI/Sections/SidebarSection.swift index 0822bbec5fca..006b7ce4b94e 100644 --- a/Packages/macOS/CmuxSettingsUI/Sources/CmuxSettingsUI/Sections/SidebarSection.swift +++ b/Packages/macOS/CmuxSettingsUI/Sources/CmuxSettingsUI/Sections/SidebarSection.swift @@ -357,7 +357,7 @@ public struct SidebarSection: View { SettingsCardRow( configurationReview: .json("sidebar.showBranchDirectory"), String(localized: "settings.app.showBranchDirectory", defaultValue: "Show Branch + Directory in Sidebar"), - subtitle: String(localized: "settings.app.showBranchDirectory.subtitle", defaultValue: "Display the built-in git branch and working-directory row.") + subtitle: String(localized: "settings.app.showBranchDirectory.subtitle", defaultValue: "Display git branches, Cloud machine info, and working directories.") ) { Toggle("", isOn: Binding(get: { showBranchDir.current }, set: { showBranchDir.set($0) })) .labelsHidden() diff --git a/Resources/Localizable.xcstrings b/Resources/Localizable.xcstrings index 6fd132f536b1..c89ee735c2d9 100644 --- a/Resources/Localizable.xcstrings +++ b/Resources/Localizable.xcstrings @@ -302888,115 +302888,115 @@ "ar": { "stringUnit": { "state": "translated", - "value": "عرض صف فرع git المدمج ودليل العمل." + "value": "عرض فروع git ومعلومات أجهزة Cloud وأدلة العمل." } }, "bs": { "stringUnit": { "state": "translated", - "value": "Prikaži ugrađeni red za git granu i radni direktorij." + "value": "Prikaži git grane, informacije o Cloud mašinama i radne direktorije." } }, "da": { "stringUnit": { "state": "translated", - "value": "Vis den indbyggede git-gren og arbejdsmapperækken." + "value": "Vis git-grene, oplysninger om Cloud-maskiner og arbejdsmapper." } }, "de": { "stringUnit": { "state": "translated", - "value": "Die integrierte Git-Branch- und Arbeitsverzeichniszeile anzeigen." + "value": "Git-Branches, Informationen zu Cloud-Maschinen und Arbeitsverzeichnisse anzeigen." } }, "en": { "stringUnit": { "state": "translated", - "value": "Display the built-in git branch and working-directory row." + "value": "Display git branches, Cloud machine info, and working directories." } }, "es": { "stringUnit": { "state": "translated", - "value": "Mostrar la fila integrada de rama git y directorio de trabajo." + "value": "Mostrar ramas de git, información de máquinas Cloud y directorios de trabajo." } }, "fr": { "stringUnit": { "state": "translated", - "value": "Afficher la ligne de branche git et de répertoire de travail intégrée." + "value": "Afficher les branches git, les informations des machines Cloud et les répertoires de travail." } }, "it": { "stringUnit": { "state": "translated", - "value": "Visualizza la riga integrata con il branch git e la directory di lavoro." + "value": "Mostra i branch git, le informazioni sulle macchine Cloud e le directory di lavoro." } }, "ja": { "stringUnit": { "state": "translated", - "value": "組み込みのgitブランチと作業ディレクトリの行を表示します。" + "value": "gitブランチ、Cloudマシンの情報、作業ディレクトリを表示します。" } }, "ko": { "stringUnit": { "state": "translated", - "value": "기본 제공 git 브랜치 및 작업 디렉토리 행을 표시합니다." + "value": "git 브랜치, Cloud 머신 정보 및 작업 디렉토리를 표시합니다." } }, "nb": { "stringUnit": { "state": "translated", - "value": "Vis den innebygde git-grenen og arbeidskatalograden." + "value": "Vis git-grener, informasjon om Cloud-maskiner og arbeidskataloger." } }, "pl": { "stringUnit": { "state": "translated", - "value": "Wyświetlaj wbudowany wiersz gałęzi git i katalogu roboczego." + "value": "Wyświetlaj gałęzie git, informacje o maszynach Cloud i katalogi robocze." } }, "pt-BR": { "stringUnit": { "state": "translated", - "value": "Exibir a linha integrada de branch git e diretório de trabalho." + "value": "Exibir branches git, informações das máquinas Cloud e diretórios de trabalho." } }, "ru": { "stringUnit": { "state": "translated", - "value": "Отображать встроенную строку ветки git и рабочего каталога." + "value": "Отображать ветки git, информацию о машинах Cloud и рабочие каталоги." } }, "th": { "stringUnit": { "state": "translated", - "value": "แสดงแถวสาขา git และไดเรกทอรีทำงานในตัว" + "value": "แสดงสาขา git ข้อมูลเครื่อง Cloud และไดเรกทอรีทำงาน" } }, "tr": { "stringUnit": { "state": "translated", - "value": "Yerleşik git dalı ve çalışma dizini satırını göster." + "value": "Git dallarını, Cloud makine bilgilerini ve çalışma dizinlerini göster." } }, "uk": { "stringUnit": { "state": "translated", - "value": "Відображати вбудований рядок з гілкою git та робочою текою." + "value": "Відображати гілки git, інформацію про машини Cloud та робочі каталоги." } }, "zh-Hans": { "stringUnit": { "state": "translated", - "value": "显示内置的 Git 分支和工作目录行。" + "value": "显示 Git 分支、Cloud 机器信息和工作目录。" } }, "zh-Hant": { "stringUnit": { "state": "translated", - "value": "顯示內建的 git 分支及工作目錄列。" + "value": "顯示 Git 分支、Cloud 機器資訊和工作目錄。" } } } diff --git a/Sources/Cloud/CloudWorkspaceSidebarPresentation.swift b/Sources/Cloud/CloudWorkspaceSidebarPresentation.swift index f366d4142d57..07ac765e8051 100644 --- a/Sources/Cloud/CloudWorkspaceSidebarPresentation.swift +++ b/Sources/Cloud/CloudWorkspaceSidebarPresentation.swift @@ -16,9 +16,15 @@ struct CloudWorkspaceSidebarPresentation { var machineIDs = Set(state.projectedResources.values.compactMap { $0.machine.cloudMachineID }) if let id = workspace.cloudVMID { machineIDs.insert(id) } guard !machineIDs.isEmpty else { return nil } - let identities = machineIDs.sorted().map { id -> String in + let names = Dictionary(uniqueKeysWithValues: machineIDs.map { id in let name = state.machineNames[id]?.trimmingCharacters(in: .whitespacesAndNewlines) ?? id - return name.isEmpty || name == id ? id : "\(name) (\(id))" + return (id, name.isEmpty ? id : name) + }) + // Keep stable IDs in badge help/accessibility; width-dependent rows use + // them only when friendly names collide across machines. + let identities = machineIDs.sorted().map { id -> String in + let name = names[id] ?? id + return name == id ? id : "\(name) (\(id))" } machineLabel = String.localizedStringWithFormat( String(localized: "sidebar.cloudWorkspace.label", defaultValue: "Cloud workspace on %@"), @@ -43,12 +49,31 @@ struct CloudWorkspaceSidebarPresentation { ? SidebarPathFormatter.pathCandidates(directory, homeDirectoryPath: "") : [directory] } - let full = zip(entries, paths).map { entry, paths -> String in - let name = state.machineNames[entry.identity]?.trimmingCharacters(in: .whitespacesAndNewlines) ?? entry.identity - let identity = name.isEmpty || name == entry.identity ? entry.identity : "\(name) (\(entry.identity))" - return "\(identity) · \(paths.first ?? Self.unavailableDirectory)" + var grouped: [(identity: String, paths: [[String]])] = [] + var groupIndexes: [String: Int] = [:] + for (entry, pathCandidates) in zip(entries, paths) { + if let index = groupIndexes[entry.identity] { + grouped[index].paths.append(pathCandidates) + } else { + groupIndexes[entry.identity] = grouped.count + grouped.append((entry.identity, [pathCandidates])) + } + } + var nameCounts: [String: Int] = [:] + for group in grouped { + nameCounts[names[group.identity, default: group.identity], default: 0] += 1 + } + let visibleName: (String) -> String = { id in + let name = names[id] ?? id + guard name != id, nameCounts[name, default: 0] > 1 else { return name } + return "\(name) (\(id))" + } + let full = grouped.map { group in + "\(visibleName(group.identity)) · " + group.paths.map { $0.first ?? Self.unavailableDirectory }.joined(separator: ", ") + }.joined(separator: " | ") + let compact = grouped.map { group in + "\(visibleName(group.identity)) · " + group.paths.map { $0.last ?? Self.unavailableDirectory }.joined(separator: ", ") }.joined(separator: " | ") - let compact = zip(entries, paths).map { "\($0.identity) · \($1.last ?? Self.unavailableDirectory)" }.joined(separator: " | ") directoryCandidates = full == compact ? [full] : [full, compact] } } diff --git a/Sources/ContentView.swift b/Sources/ContentView.swift index e49cdea6f2c0..8d08954fd288 100644 --- a/Sources/ContentView.swift +++ b/Sources/ContentView.swift @@ -16029,7 +16029,7 @@ struct TabItemView: View, Equatable { .transition(.opacity) } - SidebarCloudWorkspaceBadgeView(label: workspaceSnapshot.cloudWorkspaceLabel, pointSize: scaledFontSize(10), tint: activeSecondaryColor(0.7)) + SidebarCloudWorkspaceBadgeView(label: detailVisibility.showsBranchDirectory ? workspaceSnapshot.cloudWorkspaceLabel : nil, pointSize: scaledFontSize(10), tint: activeSecondaryColor(0.7)) if isEditing { SidebarInlineRenameField( diff --git a/Sources/Sidebar/AppKitList/Cells/SidebarWorkspaceRowCellView.swift b/Sources/Sidebar/AppKitList/Cells/SidebarWorkspaceRowCellView.swift index 43ba57bfc12e..42812541f542 100644 --- a/Sources/Sidebar/AppKitList/Cells/SidebarWorkspaceRowCellView.swift +++ b/Sources/Sidebar/AppKitList/Cells/SidebarWorkspaceRowCellView.swift @@ -413,7 +413,7 @@ final class SidebarWorkspaceRowTableCellView: NSTableCellView { // Title line cloudImageView.configureSidebarWorkspaceAccessory( - symbol: "cloud", label: snapshot.cloudWorkspaceLabel, + symbol: "cloud", label: model.settings.visibleAuxiliaryDetails.showsBranchDirectory ? snapshot.cloudWorkspaceLabel : nil, pointSize: model.scaled(10), tint: palette.secondary(0.7), weight: .regular ) pinImageView.configureSidebarWorkspaceAccessory( diff --git a/Sources/Sidebar/SidebarPathFormatter.swift b/Sources/Sidebar/SidebarPathFormatter.swift index 54687985a991..4eed4b3f0b9f 100644 --- a/Sources/Sidebar/SidebarPathFormatter.swift +++ b/Sources/Sidebar/SidebarPathFormatter.swift @@ -9,6 +9,7 @@ enum SidebarPathFormatter { ) -> String { let trimmed = path.trimmingCharacters(in: .whitespacesAndNewlines) guard !trimmed.isEmpty else { return path } + guard !homeDirectoryPath.isEmpty else { return trimmed } if trimmed == homeDirectoryPath { return "~" } diff --git a/cmuxTests/CloudDirectoryLifecycleTests.swift b/cmuxTests/CloudDirectoryLifecycleTests.swift index 91ec5f5f9385..233b46d0c91f 100644 --- a/cmuxTests/CloudDirectoryLifecycleTests.swift +++ b/cmuxTests/CloudDirectoryLifecycleTests.swift @@ -158,7 +158,8 @@ struct CloudDirectoryLifecycleTests { let sidebar = try fixture.sidebar() #expect(sidebar.cloudWorkspaceLabel?.contains("Build server") == true) #expect(sidebar.cloudWorkspaceLabel?.contains("cwd-machine") == true) - #expect(try fixture.sidebarText().contains("cwd-machine")) + #expect(try fixture.sidebarText().contains("Build server ·")) + #expect(try !fixture.sidebarText().contains("cwd-machine")) #expect(fixture.workspace.cloudBindingState.revision > revision) #expect(sidebar.accessibilityLabel(index: 0, workspaceCount: 1).contains("Build server")) info.name = "Renamed server" @@ -168,6 +169,73 @@ struct CloudDirectoryLifecycleTests { #expect(fixture.workspace.title == "My explicit task title") } + @Test("Every sidebar width uses the Cloud tree name, with IDs reserved for help", arguments: [ + (nil as String?, "early-plum-alpaca" as String?, "early-plum-alpaca"), + ("Build server", "early-plum-alpaca", "Build server"), + ("", "early-plum-alpaca", "early-plum-alpaca"), + (nil, nil, "cwd-machine"), + ("", "", "cwd-machine") + ]) + func machineNamePresentation(label: String?, slug: String?, expected: String) throws { + let fixture = try CloudDirectoryTestFixture() + defer { fixture.close() } + let binding = fixture.workspace.cloudVMBinding + let projections = fixture.catalog.projections + var summary = fixture.provider.summary + summary.displayName = label + summary.slug = slug + fixture.provider.update(summary: summary) + try fixture.changeDirectory("/home/cmux/a", terminal: 0) + try fixture.changeDirectory("/home/cmux/b", terminal: 1) + + #expect(MachineSnapshotBuilder.snapshot(from: summary).displayName == expected) + #expect(fixture.provider.info.name == expected) + for usesLastSegmentPath in [false, true] { + let presentation = try #require(CloudWorkspaceSidebarPresentation( + workspace: fixture.workspace, orderedPanelIDs: fixture.panels, + usesLastSegmentPath: usesLastSegmentPath + )) + let full = "\(expected) · /home/cmux/a, /home/cmux/b" + if usesLastSegmentPath { + #expect(presentation.directoryCandidates.count == 2) + #expect(presentation.directoryCandidates.first == full) + #expect(presentation.directoryCandidates.last == "\(expected) · …/a, …/b") + } else { + #expect(presentation.directoryCandidates == [full]) + } + #expect(presentation.directoryCandidates.allSatisfy { $0.hasPrefix("\(expected) · ") }) + #expect(presentation.machineLabel.contains(expected)) + #expect(presentation.machineLabel.contains(fixture.machine.rawValue)) + } + let snapshot = try fixture.sidebar() + let candidates = snapshot.compactDirectoryCandidates + snapshot.branchDirectoryLines.flatMap(\.directoryCandidates) + #expect(!candidates.isEmpty) + #expect(candidates.allSatisfy { $0.hasPrefix("\(expected) · ") }) + #expect(snapshot.accessibilityLabel(index: 0, workspaceCount: 1).contains(fixture.machine.rawValue)) + #expect(fixture.workspace.cloudVMBinding == binding) + #expect(fixture.catalog.projections == projections) + } + + @Test("A renamed machine keeps its name while cwd is unavailable and safely falls back when cleared") + func unavailableDirectoryMachineName() throws { + let fixture = try CloudDirectoryTestFixture() + defer { fixture.close() } + try fixture.changeDirectory(nil, terminal: 0) + try fixture.changeDirectory(nil, terminal: 1) + for name in ["Build server", "Renamed server", "", " \n "] { + var info = fixture.provider.info + info.name = name + fixture.catalog.updateMachine(info, from: fixture.provider) + let expected = name.trimmingCharacters(in: .whitespacesAndNewlines).isEmpty + ? fixture.machine.rawValue : name + let presentation = try #require(CloudWorkspaceSidebarPresentation( + workspace: fixture.workspace, orderedPanelIDs: fixture.panels, usesLastSegmentPath: true + )) + #expect(presentation.directoryCandidates == ["\(expected) · \(CloudWorkspaceSidebarPresentation.unavailableDirectory)"]) + #expect(fixture.workspace.cloudVMID == fixture.machine.rawValue) + } + } + @Test("Saved Cloud paths require fresh remote confirmation after restore") func sessionRestore() throws { let fixture = try CloudDirectoryTestFixture() @@ -206,10 +274,61 @@ struct CloudDirectoryLifecycleTests { )) #expect(workspace.reportedPanelDirectory(panelId: fixture.panels[0]) == "/srv/other") #expect(try fixture.sidebar().cloudWorkspaceLabel?.contains("other-machine") == true) - #expect(try fixture.sidebarText().contains("other-machine ·")) + #expect(try fixture.sidebarText().contains("Other machine ·")) #expect(workspace.title == "My explicit task title") } + @Test("A machine name is shown once for multiple directories and collisions stay distinct") + func groupedMachineDirectories() throws { + let fixture = try CloudDirectoryTestFixture() + defer { fixture.close() } + try fixture.changeDirectory("/home/cmux/a", terminal: 0) + try fixture.changeDirectory("/home/cmux/b", terminal: 1) + var info = fixture.provider.info + info.name = "Build server" + fixture.catalog.updateMachine(info, from: fixture.provider) + let singleMachine = try #require(CloudWorkspaceSidebarPresentation( + workspace: fixture.workspace, orderedPanelIDs: fixture.panels, usesLastSegmentPath: false + )) + #expect(singleMachine.directoryCandidates == ["Build server · /home/cmux/a, /home/cmux/b"]) + + let other = SurfaceMachineID.cloud("other-machine") + let otherPanel = fixture.panels[1] + let state = try #require(CmuxTuiSnapshotParser.state(fromSnapshot: [ + "cursor": ["generation": "other", "revision": "1"], + "workspaces": [], "screens": [], "panes": [], "tabs": [], + "terminals": [["id": "other-terminal", "title": "bash", "cwd": "/srv/other", "lifecycle": "running"]], + "browsers": [], "agents": [] + ], machine: other)) + let otherProvider = CmuxTuiSurfaceProvider( + summary: VMSummary(id: other.rawValue, provider: "freestyle", status: "running", image: "cmux-devbox", createdAt: 0, base: nil), + links: CloudMachineLinkManager(clientURL: nil, hostThemeColors: { nil }), catalog: fixture.catalog + ) + fixture.catalog.register(otherProvider) + defer { + fixture.catalog.endProjections(panelID: otherPanel) + fixture.catalog.unregister(machine: other) + } + var otherInfo = otherProvider.info + otherInfo.name = "Build server" + fixture.catalog.replaceCloudState(state, resources: CmuxTuiSnapshotParser.resources(from: state), info: otherInfo) + fixture.catalog.endProjections(panelID: otherPanel, reason: .replaced) + fixture.catalog.record(SurfaceProjection( + resource: SurfaceResourceID(machine: other, kind: .terminal, key: "other-terminal"), + workspaceID: fixture.workspace.id, panelID: otherPanel + )) + let collision = try #require(CloudWorkspaceSidebarPresentation( + workspace: fixture.workspace, orderedPanelIDs: fixture.panels, usesLastSegmentPath: false + )) + #expect(collision.directoryCandidates == [ + "Build server (cwd-machine) · /home/cmux/a | Build server (other-machine) · /srv/other" + ]) + let hiddenOtherMachine = try #require(CloudWorkspaceSidebarPresentation( + workspace: fixture.workspace, orderedPanelIDs: [fixture.panels[0]], usesLastSegmentPath: false + )) + #expect(hiddenOtherMachine.directoryCandidates == ["Build server · /home/cmux/a"]) + } + @Test("Local renderer OSC reports cannot overwrite the accepted Cloud graph") func localRendererCannotClaimRemoteProvenance() throws { let manager = TabManager(autoWelcomeIfNeeded: false) diff --git a/cmuxTests/SidebarCloudWorkspaceBadgeTests.swift b/cmuxTests/SidebarCloudWorkspaceBadgeTests.swift index d637c195e4e2..0a283b4816dd 100644 --- a/cmuxTests/SidebarCloudWorkspaceBadgeTests.swift +++ b/cmuxTests/SidebarCloudWorkspaceBadgeTests.swift @@ -1,5 +1,6 @@ import AppKit import CmuxCore +import CmuxSettings import Observation import Testing @testable import cmux_DEV @@ -36,6 +37,46 @@ struct SidebarCloudWorkspaceBadgeTests { #expect(decision.workspaceSnapshotStorage?.cloudWorkspaceLabel != nil) } + @Test(arguments: [false, true], [false, true]) + func sidebarDetailSettingsHideCloudMachineInfo(hideAll: Bool, verticalLayout: Bool) throws { + let suite = "CloudSidebarVisibility.\(UUID())" + let defaults = try #require(UserDefaults(suiteName: suite)) + defer { defaults.removePersistentDomain(forName: suite) } + let sidebar = SidebarCatalogSection() + defaults.set(verticalLayout, forKey: sidebar.branchVerticalLayout.userDefaultsKey) + let workspace = Workspace(initialSurface: .cloudVMLoading) + defer { for panel in workspace.panels.values { panel.close() } } + workspace.cloudVMBinding = WorkspaceCloudVMBinding(vmID: "vivid-newt", isBase: true) + workspace.updateCloudPanelDirectory(panelId: try #require(workspace.focusedPanelId), directory: "/home/cmux") + let binding = workspace.cloudVMBinding + var cell: SidebarWorkspaceRowTableCellView? + for hidden in [false, true, false] { + defaults.set(hideAll && hidden, forKey: sidebar.hideAllDetails.userDefaultsKey) + defaults.set(hideAll || !hidden, forKey: sidebar.showBranchDirectory.userDefaultsKey) + let settings = SidebarTabItemSettingsSnapshot(defaults: defaults) + let snapshot = SidebarWorkspaceSnapshotFactory( + workspace: workspace, settings: settings, showsAgentActivity: false + ).makeSnapshot() + let directories = snapshot.compactDirectoryCandidates + snapshot.branchDirectoryLines.flatMap(\.directoryCandidates) + #expect(directories.isEmpty == hidden) + #expect(snapshot.compactBranchDirectoryCandidates.isEmpty == (hidden || verticalLayout)) + #expect(snapshot.cloudWorkspaceLabel?.contains("vivid-newt") == true) + let model = Self.makeModel(settings: settings, workspaceSnapshot: snapshot) + if let cell { + cell.applyRebuiltModel(model) + } else { + cell = SidebarAppKitRowCellTests.configuredCell(model: model, tab: workspace) + } + let rendered = try #require(cell) + let badge = try #require(SidebarAppKitRowCellTests.descendants(of: rendered).compactMap { $0 as? NSImageView }.first { + $0.accessibilityIdentifier() == "sidebarCloudBadge" + }) + #expect(badge.isHidden == hidden) + #expect(rendered.accessibilityLabel()?.contains("Cloud workspace on vivid-newt") == true) + #expect(workspace.cloudVMBinding == binding) + } + } + /// Ensures restored Cloud identity survives every connection presentation state. @Test(arguments: [false, true]) func cloudBadgeSurvivesRestoreAndReconnect(legacyTransport: Bool) throws { @@ -86,7 +127,8 @@ struct SidebarCloudWorkspaceBadgeTests { defaults.set(compact, forKey: "sidebarHideAllDetails") let settings = SidebarTabItemSettingsSnapshot(defaults: defaults) let workspace = Workspace(title: "Same project with a long workspace name", - workingDirectory: "/home/cmux", initialSurface: .cloudVMLoading) + workingDirectory: "/home/cmux", initialSurface: .terminal) + defer { for panel in workspace.panels.values { panel.close() } } workspace.isPinned = isPinned let factory = SidebarWorkspaceSnapshotFactory(workspace: workspace, settings: settings, showsAgentActivity: false) let localSnapshot = factory.makeSnapshot() @@ -118,12 +160,16 @@ struct SidebarCloudWorkspaceBadgeTests { Attachment.record(try #require(bitmap.representation(using: .png, properties: [:])), named: "cloud-\(Int(width))-pin\(isPinned)-active\(isActive)-compact\(compact)-dark\(dark)-scale\(magnification).png") #endif - #expect(!badge.isHidden) - #expect(badge.image != nil) - #expect(badge.toolTip == "Cloud workspace on vivid-newt") - #expect(badge.contentTintColor != title.textColor) + #expect(badge.isHidden == compact) + #expect((badge.image != nil) == !compact) + #expect(compact || badge.toolTip == "Cloud workspace on vivid-newt") + if !compact { + #expect(badge.contentTintColor != title.textColor) + } #expect(title.frame.width > 60) - #expect(badge.frame.maxX + 8 == title.frame.minX) + if !compact { + #expect(badge.frame.maxX + 8 == title.frame.minX) + } #expect(title.frame.maxX <= width) #expect(title.lineBreakMode == .byTruncatingTail) #expect(cell.accessibilityLabel()?.contains("Cloud workspace on vivid-newt") == true) @@ -132,8 +178,12 @@ struct SidebarCloudWorkspaceBadgeTests { #expect(pins.count == (isPinned ? 1 : 0)) if isPinned { let pin = try #require(pins.first) - #expect(pin.frame.maxX + 8 == badge.frame.minX) - #expect(pin.frame.midY == badge.frame.midY) + if !compact { + #expect(pin.frame.maxX + 8 == badge.frame.minX) + } + if !compact { + #expect(pin.frame.midY == badge.frame.midY) + } } let cloudTitleFrame = title.frame let directoryFrames = SidebarAppKitRowCellTests.descendants(of: cell) @@ -145,7 +195,7 @@ struct SidebarCloudWorkspaceBadgeTests { colorSchemeIsDark: dark, isActive: isActive, magnification: magnification)) cell.layoutSubtreeIfNeeded() #expect(badge.isHidden) - #expect(title.frame.minX < cloudTitleFrame.minX) + #expect(compact || title.frame.minX < cloudTitleFrame.minX) #expect(title.frame.maxX == cloudTitleFrame.maxX) #expect(cell.accessibilityLabel()?.contains("Cloud workspace") == false) #expect(cell.layoutContent(model: try #require(cell.currentModelForMeasurement), width: width, apply: false) == height)